Merge "Merge "Remove unused dasm tool" am: 2931e87c70 am: f0e5906924 am: 134136a435" into qt-r1-dev-plus-aosp am: d32273e99e
am: 1248b80504

Change-Id: I9424f159511651c681ea7b8ca4b5d1daec994571
diff --git a/.gitignore b/.gitignore
index 33bfd97..ff66172 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,6 @@
 *.iml
 *.class
 *.sw*
+
+# Jars added by Idea's "Konfigure kotlin in project" action
+**/lib/kotlin-*.jar
\ No newline at end of file
diff --git a/CtsCoverage.mk b/CtsCoverage.mk
index 0ac533b..3c065c8 100644
--- a/CtsCoverage.mk
+++ b/CtsCoverage.mk
@@ -31,12 +31,17 @@
 		$(hide) mkdir -p $(dir $@)
 		$(hide) $(ACP)  $< $@
 
+system_api_xml_description := $(TARGET_OUT_COMMON_INTERMEDIATES)/system-api.xml
+
 cts-test-coverage-report := $(coverage_out)/test-coverage.html
+cts-system-api-coverage-report := $(coverage_out)/system-api-coverage.html
+cts-system-api-xml-coverage-report := $(coverage_out)/system-api-coverage.xml
 cts-verifier-coverage-report := $(coverage_out)/verifier-coverage.html
 cts-combined-coverage-report := $(coverage_out)/combined-coverage.html
 cts-combined-xml-coverage-report := $(coverage_out)/combined-coverage.xml
 
 cts_api_coverage_dependencies := $(cts_api_coverage_exe) $(dexdeps_exe) $(api_xml_description) $(napi_xml_description)
+cts_system_api_coverage_dependencies := $(cts_api_coverage_exe) $(dexdeps_exe) $(system_api_xml_description)
 
 android_cts_zip := $(HOST_OUT)/cts/android-cts.zip
 cts_verifier_apk := $(call intermediates-dir-for,APPS,CtsVerifier)/package.apk
@@ -50,6 +55,24 @@
 	$(call generate-coverage-report-cts,"CTS Tests API-NDK Coverage Report",\
 			$(PRIVATE_TEST_CASES),html)
 
+$(cts-system-api-coverage-report): PRIVATE_TEST_CASES := $(COMPATIBILITY_TESTCASES_OUT_cts)
+$(cts-system-api-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)
+$(cts-system-api-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)
+$(cts-system-api-coverage-report): PRIVATE_API_XML_DESC := $(system_api_xml_description)
+$(cts-system-api-coverage-report): PRIVATE_NAPI_XML_DESC := ""
+$(cts-system-api-coverage-report) : $(android_cts_zip) $(cts_system_api_coverage_dependencies) | $(ACP)
+	$(call generate-coverage-report-cts,"CTS System API Coverage Report",\
+			$(PRIVATE_TEST_CASES),html)
+
+$(cts-system-api-xml-coverage-report): PRIVATE_TEST_CASES := $(COMPATIBILITY_TESTCASES_OUT_cts)
+$(cts-system-api-xml-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)
+$(cts-system-api-xml-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)
+$(cts-system-api-xml-coverage-report): PRIVATE_API_XML_DESC := $(system_api_xml_description)
+$(cts-system-api-xml-coverage-report): PRIVATE_NAPI_XML_DESC := ""
+$(cts-system-api-xml-coverage-report) : $(android_cts_zip) $(cts_system_api_coverage_dependencies) | $(ACP)
+	$(call generate-coverage-report-cts,"CTS System API Coverage Report - XML",\
+			$(PRIVATE_TEST_CASES),xml)
+
 $(cts-verifier-coverage-report): PRIVATE_TEST_CASES := $(cts_verifier_apk)
 $(cts-verifier-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)
 $(cts-verifier-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)
@@ -80,6 +103,12 @@
 .PHONY: cts-test-coverage
 cts-test-coverage : $(cts-test-coverage-report)
 
+.PHONY: cts-system-api-coverage
+cts-system-api-coverage : $(cts-system-api-coverage-report)
+
+.PHONY: cts-system-api-xml-coverage
+cts-system-api-xml-coverage : $(cts-system-api-xml-coverage-report)
+
 .PHONY: cts-verifier-coverage
 cts-verifier-coverage : $(cts-verifier-coverage-report)
 
@@ -94,6 +123,8 @@
 
 # Put the test coverage report in the dist dir if "cts-api-coverage" is among the build goals.
 $(call dist-for-goals, cts-api-coverage, $(cts-test-coverage-report):cts-test-coverage-report.html)
+$(call dist-for-goals, cts-api-coverage, $(cts-system-api-coverage-report):cts-system-api-coverage-report.html)
+$(call dist-for-goals, cts-api-coverage, $(cts-system-api-xml-coverage-report):cts-system-api-coverage-report.xml)
 $(call dist-for-goals, cts-api-coverage, $(cts-verifier-coverage-report):cts-verifier-coverage-report.html)
 $(call dist-for-goals, cts-api-coverage, $(cts-combined-coverage-report):cts-combined-coverage-report.html)
 $(call dist-for-goals, cts-api-coverage, $(cts-combined-xml-coverage-report):cts-combined-coverage-report.xml)
@@ -110,12 +141,16 @@
 
 # Reset temp vars
 cts_api_coverage_dependencies :=
+cts_system_api_coverage_dependencies :=
 cts-combined-coverage-report :=
 cts-combined-xml-coverage-report :=
 cts-verifier-coverage-report :=
 cts-test-coverage-report :=
+cts-system-api-coverage-report :=
+cts-system-api-xml-coverage-report :=
 api_xml_description :=
 api_text_description :=
+system_api_xml_description :=
 napi_xml_description :=
 napi_text_description :=
 coverage_out :=
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 39a0f35..926b808 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,5 +1,6 @@
 [Builtin Hooks]
 clang_format = true
+xmllint = true
 
 [Builtin Hooks Options]
 clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
diff --git a/apps/CameraITS/CameraITS.pdf b/apps/CameraITS/CameraITS.pdf
index 5f1e481..f45d652 100644
--- a/apps/CameraITS/CameraITS.pdf
+++ b/apps/CameraITS/CameraITS.pdf
Binary files differ
diff --git a/apps/CameraITS/build/envsetup.sh b/apps/CameraITS/build/envsetup.sh
index ae12e10..6dcdf79 100644
--- a/apps/CameraITS/build/envsetup.sh
+++ b/apps/CameraITS/build/envsetup.sh
@@ -31,7 +31,7 @@
 python -V 2>&1 | grep -q "Python 2.7" || \
     echo ">> Require python 2.7" >&2
 
-for M in numpy PIL matplotlib scipy.stats scipy.spatial
+for M in numpy PIL matplotlib scipy.stats scipy.spatial serial
 do
     python -c "import $M" >/dev/null 2>&1 || \
         echo ">> Require Python $M module" >&2
diff --git a/apps/CameraITS/pymodules/its/device.py b/apps/CameraITS/pymodules/its/device.py
index 3311318..24fae6d 100644
--- a/apps/CameraITS/pymodules/its/device.py
+++ b/apps/CameraITS/pymodules/its/device.py
@@ -320,6 +320,23 @@
         if data['tag'] != 'vibrationStarted':
             raise its.error.Error('Invalid command response')
 
+    def set_audio_restriction(self, mode):
+        """Set the audio restriction mode for this camera device.
+
+        Args:
+            mode: the audio restriction mode. See CameraDevice.java for valid
+                  value.
+        Returns:
+            Nothing.
+        """
+        cmd = {}
+        cmd["cmdName"] = "setAudioRestriction"
+        cmd["mode"] = mode
+        self.sock.send(json.dumps(cmd) + "\n")
+        data,_ = self.__read_response_from_socket()
+        if data["tag"] != "audioRestrictionSet":
+            raise its.error.Error("Invalid command response")
+
     def get_sensors(self):
         """Get all sensors on the device.
 
diff --git a/apps/CameraITS/tests/scene0/test_audio_restriction.py b/apps/CameraITS/tests/scene0/test_audio_restriction.py
new file mode 100644
index 0000000..c771381
--- /dev/null
+++ b/apps/CameraITS/tests/scene0/test_audio_restriction.py
@@ -0,0 +1,100 @@
+# Copyright 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.
+
+import math
+import os.path
+import time
+
+import its.caps
+import its.device
+import matplotlib
+from matplotlib import pylab
+import numpy as np
+
+NAME = os.path.basename(__file__).split(".")[0]
+
+# if the var(x) > var(stable) * this threshold, then device is considered vibrated
+# Test results shows the variance difference is larger for higher sampling frequency
+# This threshold is good enough for 50hz samples.
+THRESHOLD_VIBRATION_VAR = 10.0
+
+# Match CameraDevice.java constant
+AUDIO_RESTRICTION_NONE = 0
+AUDIO_RESTRICTION_VIBRATION = 1
+AUDIO_RESTRICTION_VIBRATION_SOUND = 2
+
+# The sleep time between vibrator on/off to avoid getting some residual vibrations
+SLEEP_BETWEEN_SAMPLES_SEC = 0.5
+# The sleep time to collect sensor samples
+SLEEP_COLLECT_SAMPLES_SEC = 1.0
+
+def calc_magnitude(e):
+    x = e["x"]
+    y = e["y"]
+    z = e["z"]
+    return math.sqrt(x*x + y*y + z*z)
+
+def main():
+    """Test vibrations can be muted by the camera audio restriction API."""
+
+    with its.device.ItsSession() as cam:
+        props = cam.get_camera_properties()
+        props = cam.override_with_hidden_physical_camera_props(props)
+        sensors = cam.get_sensors()
+
+        its.caps.skip_unless(sensors.get("accel") and sensors.get("vibrator"))
+
+        cam.start_sensor_events()
+        pattern_ms = [0, 1000]
+        cam.do_vibrate(pattern_ms)
+        test_length_second = sum(pattern_ms) / 1000
+        time.sleep(test_length_second)
+        events = cam.get_sensor_events()
+        print "Accelerometer events over %ds: %d " % (test_length_second, len(events["accel"]))
+        times_ms = [e["time"]/float(1e6) for e in events["accel"]]
+        t0 = times_ms[0]
+        times_ms = [t - t0 for t in times_ms]
+        magnitudes = [calc_magnitude(e) for e in events["accel"]]
+        var_w_vibration = np.var(magnitudes)
+
+        time.sleep(SLEEP_BETWEEN_SAMPLES_SEC)
+        cam.start_sensor_events()
+        time.sleep(SLEEP_COLLECT_SAMPLES_SEC)
+        events = cam.get_sensor_events()
+        magnitudes = [calc_magnitude(e) for e in events["accel"]]
+        var_wo_vibration = np.var(magnitudes)
+
+        if var_w_vibration < var_wo_vibration * THRESHOLD_VIBRATION_VAR:
+            print "Warning: unable to detect vibration, variance w/wo vibration too close:"\
+                    " %f/%f. Make sure device is on non-dampening surface" % (
+                    var_w_vibration, var_wo_vibration)
+
+        time.sleep(SLEEP_BETWEEN_SAMPLES_SEC)
+        cam.start_sensor_events()
+        cam.set_audio_restriction(AUDIO_RESTRICTION_VIBRATION)
+        cam.do_vibrate(pattern_ms)
+        time.sleep(SLEEP_COLLECT_SAMPLES_SEC)
+        events = cam.get_sensor_events()
+        magnitudes = [calc_magnitude(e) for e in events["accel"]]
+        var_w_vibration_restricted = np.var(magnitudes)
+
+        print "Accel variance with/without/restricted vibration (%f, %f, %f)" % (
+                var_w_vibration, var_wo_vibration, var_w_vibration_restricted)
+
+        e_msg = "Device vibrated while vibration is muted"
+        assert var_w_vibration_restricted < var_wo_vibration * THRESHOLD_VIBRATION_VAR, e_msg
+
+if __name__ == "__main__":
+    main()
+
diff --git a/apps/CameraITS/tests/scene1/scene1.pdf b/apps/CameraITS/tests/scene1_1/scene1_1.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene1/scene1.pdf
rename to apps/CameraITS/tests/scene1_1/scene1_1.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene1/scene1_0.5_scaled.pdf b/apps/CameraITS/tests/scene1_1/scene1_1_0.5x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene1/scene1_0.5_scaled.pdf
rename to apps/CameraITS/tests/scene1_1/scene1_1_0.5x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene1/scene1_0.67_scaled.pdf b/apps/CameraITS/tests/scene1_1/scene1_1_0.67x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene1/scene1_0.67_scaled.pdf
rename to apps/CameraITS/tests/scene1_1/scene1_1_0.67x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene1/test_3a.py b/apps/CameraITS/tests/scene1_1/test_3a.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_3a.py
rename to apps/CameraITS/tests/scene1_1/test_3a.py
diff --git a/apps/CameraITS/tests/scene1/test_ae_af.py b/apps/CameraITS/tests/scene1_1/test_ae_af.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_ae_af.py
rename to apps/CameraITS/tests/scene1_1/test_ae_af.py
diff --git a/apps/CameraITS/tests/scene1/test_ae_precapture_trigger.py b/apps/CameraITS/tests/scene1_1/test_ae_precapture_trigger.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_ae_precapture_trigger.py
rename to apps/CameraITS/tests/scene1_1/test_ae_precapture_trigger.py
diff --git a/apps/CameraITS/tests/scene1/test_auto_vs_manual.py b/apps/CameraITS/tests/scene1_1/test_auto_vs_manual.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_auto_vs_manual.py
rename to apps/CameraITS/tests/scene1_1/test_auto_vs_manual.py
diff --git a/apps/CameraITS/tests/scene1/test_black_white.py b/apps/CameraITS/tests/scene1_1/test_black_white.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_black_white.py
rename to apps/CameraITS/tests/scene1_1/test_black_white.py
diff --git a/apps/CameraITS/tests/scene1/test_burst_sameness_manual.py b/apps/CameraITS/tests/scene1_1/test_burst_sameness_manual.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_burst_sameness_manual.py
rename to apps/CameraITS/tests/scene1_1/test_burst_sameness_manual.py
diff --git a/apps/CameraITS/tests/scene1/test_capture_result.py b/apps/CameraITS/tests/scene1_1/test_capture_result.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_capture_result.py
rename to apps/CameraITS/tests/scene1_1/test_capture_result.py
diff --git a/apps/CameraITS/tests/scene1/test_channel_saturation.py b/apps/CameraITS/tests/scene1_1/test_channel_saturation.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_channel_saturation.py
rename to apps/CameraITS/tests/scene1_1/test_channel_saturation.py
diff --git a/apps/CameraITS/tests/scene1/test_crop_region_raw.py b/apps/CameraITS/tests/scene1_1/test_crop_region_raw.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_crop_region_raw.py
rename to apps/CameraITS/tests/scene1_1/test_crop_region_raw.py
diff --git a/apps/CameraITS/tests/scene1/test_crop_regions.py b/apps/CameraITS/tests/scene1_1/test_crop_regions.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_crop_regions.py
rename to apps/CameraITS/tests/scene1_1/test_crop_regions.py
diff --git a/apps/CameraITS/tests/scene1/test_dng_noise_model.py b/apps/CameraITS/tests/scene1_1/test_dng_noise_model.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_dng_noise_model.py
rename to apps/CameraITS/tests/scene1_1/test_dng_noise_model.py
diff --git a/apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py b/apps/CameraITS/tests/scene1_1/test_ev_compensation_advanced.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py
rename to apps/CameraITS/tests/scene1_1/test_ev_compensation_advanced.py
diff --git a/apps/CameraITS/tests/scene1/test_ev_compensation_basic.py b/apps/CameraITS/tests/scene1_1/test_ev_compensation_basic.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_ev_compensation_basic.py
rename to apps/CameraITS/tests/scene1_1/test_ev_compensation_basic.py
diff --git a/apps/CameraITS/tests/scene1/test_exposure.py b/apps/CameraITS/tests/scene1_1/test_exposure.py
similarity index 75%
rename from apps/CameraITS/tests/scene1/test_exposure.py
rename to apps/CameraITS/tests/scene1_1/test_exposure.py
index a13f020..bf52d6f 100644
--- a/apps/CameraITS/tests/scene1/test_exposure.py
+++ b/apps/CameraITS/tests/scene1_1/test_exposure.py
@@ -25,6 +25,7 @@
 
 IMG_STATS_GRID = 9  # find used to find the center 11.11%
 NAME = os.path.basename(__file__).split('.')[0]
+NUM_PTS_2X_GAIN = 3  # 3 points every 2x increase in gain
 THRESHOLD_MAX_OUTLIER_DIFF = 0.1
 THRESHOLD_MIN_LEVEL = 0.1
 THRESHOLD_MAX_LEVEL = 0.9
@@ -36,6 +37,41 @@
 THRESH_EXP_KNEE = 6E6  # exposures less than knee have relaxed tol
 
 
+def find_fit_and_check(chan, mults, values, thresh_max_level_diff):
+    """Find line fit and check values.
+
+    Check for linearity. Verify sample pixel mean values are close to each
+    other. Also ensure that the images aren't clamped to 0 or 1
+    (which would make them look like flat lines).
+
+    Args:
+        chan:                   [0:2] RGB channel
+        mults:                  list of multiplication values for gain*m, exp/m
+        values:                 mean values for chan
+        thresh_max_level_diff:  threshold for max difference
+    """
+
+    m, b = numpy.polyfit(mults, values, 1).tolist()
+    max_val = max(values)
+    min_val = min(values)
+    max_diff = max_val - min_val
+    print 'Channel %d line fit (y = mx+b): m = %f, b = %f' % (chan, m, b)
+    print 'Channel max %f min %f diff %f' % (max_val, min_val, max_diff)
+    e_msg = 'max_diff: %.4f, THRESH: %.3f' % (
+            max_diff, thresh_max_level_diff)
+    assert max_diff < thresh_max_level_diff, e_msg
+    e_msg = 'b: %.2f, THRESH_MIN: %.1f, THRESH_MAX: %.1f' % (
+            b, THRESHOLD_MIN_LEVEL, THRESHOLD_MAX_LEVEL)
+    assert THRESHOLD_MAX_LEVEL > b > THRESHOLD_MIN_LEVEL, e_msg
+    for v in values:
+        e_msg = 'v: %.2f, THRESH_MIN: %.1f, THRESH_MAX: %.1f' % (
+                v, THRESHOLD_MIN_LEVEL, THRESHOLD_MAX_LEVEL)
+        assert THRESHOLD_MAX_LEVEL > v > THRESHOLD_MIN_LEVEL, e_msg
+        e_msg = 'v: %.2f, b: %.2f, THRESH_MAX_OUTLIER_DIFF: %.1f' % (
+                v, b, THRESHOLD_MAX_OUTLIER_DIFF)
+        assert abs(v - b) < THRESHOLD_MAX_OUTLIER_DIFF, e_msg
+
+
 def get_raw_active_array_size(props):
     """Return the active array w, h from props."""
     aaw = (props['android.sensor.info.preCorrectionActiveArraySize']['right'] -
@@ -137,7 +173,7 @@
                 raw_b_means.append(b[IMG_STATS_GRID/2, IMG_STATS_GRID/2]
                                    * request_result_ratio)
             # Test 3 steps per 2x gain
-            m *= pow(2, 1.0 / 3)
+            m *= pow(2, 1.0 / NUM_PTS_2X_GAIN)
 
         # Allow more threshold for devices with wider exposure range
         if m >= 64.0:
@@ -145,60 +181,40 @@
 
     # Draw plots
     pylab.figure('rgb data')
-    pylab.plot(mults, r_means, 'ro-')
-    pylab.plot(mults, g_means, 'go-')
-    pylab.plot(mults, b_means, 'bo-')
+    pylab.semilogx(mults, r_means, 'ro-')
+    pylab.semilogx(mults, g_means, 'go-')
+    pylab.semilogx(mults, b_means, 'bo-')
     pylab.title(NAME + 'RGB Data')
     pylab.xlabel('Gain Multiplier')
     pylab.ylabel('Normalized RGB Plane Avg')
+    pylab.minorticks_off()
+    pylab.xticks(mults[0::NUM_PTS_2X_GAIN], mults[0::NUM_PTS_2X_GAIN])
     pylab.ylim([0, 1])
     matplotlib.pyplot.savefig('%s_plot_means.png' % (NAME))
 
     if process_raw and debug:
         pylab.figure('raw data')
-        pylab.plot(mults, raw_r_means, 'ro-', label='R')
-        pylab.plot(mults, raw_gr_means, 'go-', label='GR')
-        pylab.plot(mults, raw_gb_means, 'ko-', label='GB')
-        pylab.plot(mults, raw_b_means, 'bo-', label='B')
+        pylab.semilogx(mults, raw_r_means, 'ro-', label='R')
+        pylab.semilogx(mults, raw_gr_means, 'go-', label='GR')
+        pylab.semilogx(mults, raw_gb_means, 'ko-', label='GB')
+        pylab.semilogx(mults, raw_b_means, 'bo-', label='B')
         pylab.title(NAME + 'RAW Data')
         pylab.xlabel('Gain Multiplier')
         pylab.ylabel('Normalized RAW Plane Avg')
+        pylab.minorticks_off()
+        pylab.xticks(mults[0::NUM_PTS_2X_GAIN], mults[0::NUM_PTS_2X_GAIN])
         pylab.ylim([0, 1])
         pylab.legend(numpoints=1)
         matplotlib.pyplot.savefig('%s_plot_raw_means.png' % (NAME))
 
-    # Check for linearity. Verify sample pixel mean values are close to each
-    # other. Also ensure that the images aren't clamped to 0 or 1
-    # (which would make them look like flat lines).
     for chan in xrange(3):
         values = [r_means, g_means, b_means][chan]
-        m, b = numpy.polyfit(mults, values, 1).tolist()
-        max_val = max(values)
-        min_val = min(values)
-        max_diff = max_val - min_val
-        print 'Channel %d line fit (y = mx+b): m = %f, b = %f' % (chan, m, b)
-        print 'Channel max %f min %f diff %f' % (max_val, min_val, max_diff)
-        assert max_diff < threshold_max_level_diff
-        assert b > THRESHOLD_MIN_LEVEL and b < THRESHOLD_MAX_LEVEL
-        for v in values:
-            assert v > THRESHOLD_MIN_LEVEL and v < THRESHOLD_MAX_LEVEL
-            assert abs(v - b) < THRESHOLD_MAX_OUTLIER_DIFF
+        find_fit_and_check(chan, mults, values, threshold_max_level_diff)
     if process_raw and debug:
         for chan in xrange(4):
             values = [raw_r_means, raw_gr_means, raw_gb_means,
                       raw_b_means][chan]
-            m, b = numpy.polyfit(mults, values, 1).tolist()
-            max_val = max(values)
-            min_val = min(values)
-            max_diff = max_val - min_val
-            print 'Channel %d line fit (y = mx+b): m = %f, b = %f' % (chan,
-                                                                      m, b)
-            print 'Channel max %f min %f diff %f' % (max_val, min_val, max_diff)
-            assert max_diff < threshold_max_level_diff
-            assert b > THRESHOLD_MIN_LEVEL and b < THRESHOLD_MAX_LEVEL
-            for v in values:
-                assert v > THRESHOLD_MIN_LEVEL and v < THRESHOLD_MAX_LEVEL
-                assert abs(v - b) < THRESHOLD_MAX_OUTLIER_DIFF
+            find_fit_and_check(chan, mults, values, threshold_max_level_diff)
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CameraITS/tests/scene1/test_jpeg.py b/apps/CameraITS/tests/scene1_1/test_jpeg.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_jpeg.py
rename to apps/CameraITS/tests/scene1_1/test_jpeg.py
diff --git a/apps/CameraITS/tests/scene1/test_latching.py b/apps/CameraITS/tests/scene1_1/test_latching.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_latching.py
rename to apps/CameraITS/tests/scene1_1/test_latching.py
diff --git a/apps/CameraITS/tests/scene1/test_linearity.py b/apps/CameraITS/tests/scene1_1/test_linearity.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_linearity.py
rename to apps/CameraITS/tests/scene1_1/test_linearity.py
diff --git a/apps/CameraITS/tests/scene1/test_locked_burst.py b/apps/CameraITS/tests/scene1_1/test_locked_burst.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_locked_burst.py
rename to apps/CameraITS/tests/scene1_1/test_locked_burst.py
diff --git a/apps/CameraITS/tests/scene1/test_multi_camera_match.py b/apps/CameraITS/tests/scene1_1/test_multi_camera_match.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_multi_camera_match.py
rename to apps/CameraITS/tests/scene1_1/test_multi_camera_match.py
diff --git a/apps/CameraITS/tests/scene1/test_param_color_correction.py b/apps/CameraITS/tests/scene1_1/test_param_color_correction.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_param_color_correction.py
rename to apps/CameraITS/tests/scene1_1/test_param_color_correction.py
diff --git a/apps/CameraITS/tests/scene1/test_param_exposure_time.py b/apps/CameraITS/tests/scene1_1/test_param_exposure_time.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_param_exposure_time.py
rename to apps/CameraITS/tests/scene1_1/test_param_exposure_time.py
diff --git a/apps/CameraITS/tests/scene1/test_param_flash_mode.py b/apps/CameraITS/tests/scene1_1/test_param_flash_mode.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_param_flash_mode.py
rename to apps/CameraITS/tests/scene1_1/test_param_flash_mode.py
diff --git a/apps/CameraITS/tests/scene1/test_param_noise_reduction.py b/apps/CameraITS/tests/scene1_1/test_param_noise_reduction.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_param_noise_reduction.py
rename to apps/CameraITS/tests/scene1_1/test_param_noise_reduction.py
diff --git a/apps/CameraITS/tests/scene1/scene1.pdf b/apps/CameraITS/tests/scene1_2/scene1_2.pdf
similarity index 100%
copy from apps/CameraITS/tests/scene1/scene1.pdf
copy to apps/CameraITS/tests/scene1_2/scene1_2.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene1/scene1_0.5_scaled.pdf b/apps/CameraITS/tests/scene1_2/scene1_2_0.5x_scaled.pdf
similarity index 100%
copy from apps/CameraITS/tests/scene1/scene1_0.5_scaled.pdf
copy to apps/CameraITS/tests/scene1_2/scene1_2_0.5x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene1/scene1_0.67_scaled.pdf b/apps/CameraITS/tests/scene1_2/scene1_2_0.67x_scaled.pdf
similarity index 100%
copy from apps/CameraITS/tests/scene1/scene1_0.67_scaled.pdf
copy to apps/CameraITS/tests/scene1_2/scene1_2_0.67x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene1/test_param_sensitivity.py b/apps/CameraITS/tests/scene1_2/test_param_sensitivity.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_param_sensitivity.py
rename to apps/CameraITS/tests/scene1_2/test_param_sensitivity.py
diff --git a/apps/CameraITS/tests/scene1/test_param_shading_mode.py b/apps/CameraITS/tests/scene1_2/test_param_shading_mode.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_param_shading_mode.py
rename to apps/CameraITS/tests/scene1_2/test_param_shading_mode.py
diff --git a/apps/CameraITS/tests/scene1/test_param_tonemap_mode.py b/apps/CameraITS/tests/scene1_2/test_param_tonemap_mode.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_param_tonemap_mode.py
rename to apps/CameraITS/tests/scene1_2/test_param_tonemap_mode.py
diff --git a/apps/CameraITS/tests/scene1/test_post_raw_sensitivity_boost.py b/apps/CameraITS/tests/scene1_2/test_post_raw_sensitivity_boost.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_post_raw_sensitivity_boost.py
rename to apps/CameraITS/tests/scene1_2/test_post_raw_sensitivity_boost.py
diff --git a/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py b/apps/CameraITS/tests/scene1_2/test_raw_burst_sensitivity.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py
rename to apps/CameraITS/tests/scene1_2/test_raw_burst_sensitivity.py
diff --git a/apps/CameraITS/tests/scene1/test_raw_exposure.py b/apps/CameraITS/tests/scene1_2/test_raw_exposure.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_raw_exposure.py
rename to apps/CameraITS/tests/scene1_2/test_raw_exposure.py
diff --git a/apps/CameraITS/tests/scene1/test_raw_sensitivity.py b/apps/CameraITS/tests/scene1_2/test_raw_sensitivity.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_raw_sensitivity.py
rename to apps/CameraITS/tests/scene1_2/test_raw_sensitivity.py
diff --git a/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py b/apps/CameraITS/tests/scene1_2/test_reprocess_noise_reduction.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py
rename to apps/CameraITS/tests/scene1_2/test_reprocess_noise_reduction.py
diff --git a/apps/CameraITS/tests/scene1/test_tonemap_sequence.py b/apps/CameraITS/tests/scene1_2/test_tonemap_sequence.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_tonemap_sequence.py
rename to apps/CameraITS/tests/scene1_2/test_tonemap_sequence.py
diff --git a/apps/CameraITS/tests/scene1/test_yuv_jpeg_all.py b/apps/CameraITS/tests/scene1_2/test_yuv_jpeg_all.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_yuv_jpeg_all.py
rename to apps/CameraITS/tests/scene1_2/test_yuv_jpeg_all.py
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py b/apps/CameraITS/tests/scene1_2/test_yuv_plus_dng.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_yuv_plus_dng.py
rename to apps/CameraITS/tests/scene1_2/test_yuv_plus_dng.py
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py b/apps/CameraITS/tests/scene1_2/test_yuv_plus_jpeg.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py
rename to apps/CameraITS/tests/scene1_2/test_yuv_plus_jpeg.py
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py b/apps/CameraITS/tests/scene1_2/test_yuv_plus_raw.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
rename to apps/CameraITS/tests/scene1_2/test_yuv_plus_raw.py
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py b/apps/CameraITS/tests/scene1_2/test_yuv_plus_raw10.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
rename to apps/CameraITS/tests/scene1_2/test_yuv_plus_raw10.py
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py b/apps/CameraITS/tests/scene1_2/test_yuv_plus_raw12.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py
rename to apps/CameraITS/tests/scene1_2/test_yuv_plus_raw12.py
diff --git a/apps/CameraITS/tests/scene2/SampleTarget.jpg b/apps/CameraITS/tests/scene2_a/SampleTarget.jpg
similarity index 100%
rename from apps/CameraITS/tests/scene2/SampleTarget.jpg
rename to apps/CameraITS/tests/scene2_a/SampleTarget.jpg
Binary files differ
diff --git a/apps/CameraITS/tests/scene2/scene2.pdf b/apps/CameraITS/tests/scene2_a/scene2_a.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene2/scene2.pdf
rename to apps/CameraITS/tests/scene2_a/scene2_a.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene2/scene2_0.5_scaled.pdf b/apps/CameraITS/tests/scene2_a/scene2_a_0.5x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene2/scene2_0.5_scaled.pdf
rename to apps/CameraITS/tests/scene2_a/scene2_a_0.5x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene2/scene2_0.67_scaled.pdf b/apps/CameraITS/tests/scene2_a/scene2_a_0.67x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene2/scene2_0.67_scaled.pdf
rename to apps/CameraITS/tests/scene2_a/scene2_a_0.67x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene2/test_auto_per_frame_control.py b/apps/CameraITS/tests/scene2_a/test_auto_per_frame_control.py
similarity index 100%
rename from apps/CameraITS/tests/scene2/test_auto_per_frame_control.py
rename to apps/CameraITS/tests/scene2_a/test_auto_per_frame_control.py
diff --git a/apps/CameraITS/tests/scene2/test_effects.py b/apps/CameraITS/tests/scene2_a/test_effects.py
similarity index 100%
rename from apps/CameraITS/tests/scene2/test_effects.py
rename to apps/CameraITS/tests/scene2_a/test_effects.py
diff --git a/apps/CameraITS/tests/scene2/test_faces.py b/apps/CameraITS/tests/scene2_a/test_faces.py
similarity index 100%
rename from apps/CameraITS/tests/scene2/test_faces.py
rename to apps/CameraITS/tests/scene2_a/test_faces.py
diff --git a/apps/CameraITS/tests/scene2/test_format_combos.py b/apps/CameraITS/tests/scene2_a/test_format_combos.py
similarity index 100%
rename from apps/CameraITS/tests/scene2/test_format_combos.py
rename to apps/CameraITS/tests/scene2_a/test_format_combos.py
diff --git a/apps/CameraITS/tests/scene2/test_num_faces.py b/apps/CameraITS/tests/scene2_a/test_num_faces.py
similarity index 100%
rename from apps/CameraITS/tests/scene2/test_num_faces.py
rename to apps/CameraITS/tests/scene2_a/test_num_faces.py
diff --git a/apps/CameraITS/tests/scene2b/scene2b.pdf b/apps/CameraITS/tests/scene2_b/scene2_b.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene2b/scene2b.pdf
rename to apps/CameraITS/tests/scene2_b/scene2_b.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene2b/scene2b_0.5_scaled.pdf b/apps/CameraITS/tests/scene2_b/scene2_b_0.5x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene2b/scene2b_0.5_scaled.pdf
rename to apps/CameraITS/tests/scene2_b/scene2_b_0.5x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene2b/scene2b_0.67_scaled.pdf b/apps/CameraITS/tests/scene2_b/scene2_b_0.67x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene2b/scene2b_0.67_scaled.pdf
rename to apps/CameraITS/tests/scene2_b/scene2_b_0.67x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene2c/scene2c.pdf b/apps/CameraITS/tests/scene2_c/scene2_c.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene2c/scene2c.pdf
rename to apps/CameraITS/tests/scene2_c/scene2_c.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene2c/scene2c_0.5_scaled.pdf b/apps/CameraITS/tests/scene2_c/scene2_c_0.5x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene2c/scene2c_0.5_scaled.pdf
rename to apps/CameraITS/tests/scene2_c/scene2_c_0.5x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene2c/scene2c_0.67_scaled.pdf b/apps/CameraITS/tests/scene2_c/scene2_c_0.67x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene2c/scene2c_0.67_scaled.pdf
rename to apps/CameraITS/tests/scene2_c/scene2_c_0.67x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene2b/test_num_faces.py b/apps/CameraITS/tests/scene2b/test_num_faces.py
deleted file mode 100644
index 044c154..0000000
--- a/apps/CameraITS/tests/scene2b/test_num_faces.py
+++ /dev/null
@@ -1,100 +0,0 @@
-# Copyright 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.
-
-import os.path
-import cv2
-import its.caps
-import its.device
-import its.image
-import its.objects
-
-NAME = os.path.basename(__file__).split('.')[0]
-NUM_TEST_FRAMES = 20
-NUM_FACES = 3
-FD_MODE_OFF = 0
-FD_MODE_SIMPLE = 1
-FD_MODE_FULL = 2
-W, H = 640, 480
-
-
-def main():
-    """Test face detection."""
-    with its.device.ItsSession() as cam:
-        props = cam.get_camera_properties()
-        its.caps.skip_unless(its.caps.face_detect(props))
-        mono_camera = its.caps.mono_camera(props)
-        fd_modes = props['android.statistics.info.availableFaceDetectModes']
-        a = props['android.sensor.info.activeArraySize']
-        aw, ah = a['right'] - a['left'], a['bottom'] - a['top']
-
-        if its.caps.read_3a(props):
-            _, _, _, _, _ = cam.do_3a(get_results=True,
-                                      mono_camera=mono_camera)
-
-        for fd_mode in fd_modes:
-            assert FD_MODE_OFF <= fd_mode <= FD_MODE_FULL
-            req = its.objects.auto_capture_request()
-            req['android.statistics.faceDetectMode'] = fd_mode
-            fmt = {'format': 'yuv', 'width': W, 'height': H}
-            caps = cam.do_capture([req]*NUM_TEST_FRAMES, fmt)
-            for i, cap in enumerate(caps):
-                md = cap['metadata']
-                assert md['android.statistics.faceDetectMode'] == fd_mode
-                faces = md['android.statistics.faces']
-
-                # 0 faces should be returned for OFF mode
-                if fd_mode == FD_MODE_OFF:
-                    assert not faces
-                    continue
-                # Face detection could take several frames to warm up,
-                # but should detect the correct number of faces in last frame
-                if i == NUM_TEST_FRAMES - 1:
-                    img = its.image.convert_capture_to_rgb_image(cap,
-                                                                 props=props)
-                    fnd_faces = len(faces)
-                    print 'Found %d face(s), expected %d.' % (fnd_faces,
-                                                              NUM_FACES)
-                    # draw boxes around faces
-                    for rect in [face['bounds'] for face in faces]:
-                        top_left = (int(round(rect['left']*W/aw)),
-                                    int(round(rect['top']*H/ah)))
-                        bot_rght = (int(round(rect['right']*W/aw)),
-                                    int(round(rect['bottom']*H/ah)))
-                        cv2.rectangle(img, top_left, bot_rght, (0, 1, 0), 2)
-                        img_name = '%s_fd_mode_%s.jpg' % (NAME, fd_mode)
-                        its.image.write_image(img, img_name)
-                    assert fnd_faces == NUM_FACES
-                if not faces:
-                    continue
-
-                print 'Frame %d face metadata:' % i
-                print '  Faces:', faces
-                print ''
-
-                # Reasonable scores for faces
-                face_scores = [face['score'] for face in faces]
-                for score in face_scores:
-                    assert score >= 1 and score <= 100
-                # Face bounds should be within active array
-                face_rectangles = [face['bounds'] for face in faces]
-                for rect in face_rectangles:
-                    assert rect['top'] < rect['bottom']
-                    assert rect['left'] < rect['right']
-                    assert 0 <= rect['top'] <= ah
-                    assert 0 <= rect['bottom'] <= ah
-                    assert 0 <= rect['left'] <= aw
-                    assert 0 <= rect['right'] <= aw
-
-if __name__ == '__main__':
-    main()
diff --git a/apps/CameraITS/tests/scene2c/test_num_faces.py b/apps/CameraITS/tests/scene2c/test_num_faces.py
deleted file mode 100644
index 044c154..0000000
--- a/apps/CameraITS/tests/scene2c/test_num_faces.py
+++ /dev/null
@@ -1,100 +0,0 @@
-# Copyright 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.
-
-import os.path
-import cv2
-import its.caps
-import its.device
-import its.image
-import its.objects
-
-NAME = os.path.basename(__file__).split('.')[0]
-NUM_TEST_FRAMES = 20
-NUM_FACES = 3
-FD_MODE_OFF = 0
-FD_MODE_SIMPLE = 1
-FD_MODE_FULL = 2
-W, H = 640, 480
-
-
-def main():
-    """Test face detection."""
-    with its.device.ItsSession() as cam:
-        props = cam.get_camera_properties()
-        its.caps.skip_unless(its.caps.face_detect(props))
-        mono_camera = its.caps.mono_camera(props)
-        fd_modes = props['android.statistics.info.availableFaceDetectModes']
-        a = props['android.sensor.info.activeArraySize']
-        aw, ah = a['right'] - a['left'], a['bottom'] - a['top']
-
-        if its.caps.read_3a(props):
-            _, _, _, _, _ = cam.do_3a(get_results=True,
-                                      mono_camera=mono_camera)
-
-        for fd_mode in fd_modes:
-            assert FD_MODE_OFF <= fd_mode <= FD_MODE_FULL
-            req = its.objects.auto_capture_request()
-            req['android.statistics.faceDetectMode'] = fd_mode
-            fmt = {'format': 'yuv', 'width': W, 'height': H}
-            caps = cam.do_capture([req]*NUM_TEST_FRAMES, fmt)
-            for i, cap in enumerate(caps):
-                md = cap['metadata']
-                assert md['android.statistics.faceDetectMode'] == fd_mode
-                faces = md['android.statistics.faces']
-
-                # 0 faces should be returned for OFF mode
-                if fd_mode == FD_MODE_OFF:
-                    assert not faces
-                    continue
-                # Face detection could take several frames to warm up,
-                # but should detect the correct number of faces in last frame
-                if i == NUM_TEST_FRAMES - 1:
-                    img = its.image.convert_capture_to_rgb_image(cap,
-                                                                 props=props)
-                    fnd_faces = len(faces)
-                    print 'Found %d face(s), expected %d.' % (fnd_faces,
-                                                              NUM_FACES)
-                    # draw boxes around faces
-                    for rect in [face['bounds'] for face in faces]:
-                        top_left = (int(round(rect['left']*W/aw)),
-                                    int(round(rect['top']*H/ah)))
-                        bot_rght = (int(round(rect['right']*W/aw)),
-                                    int(round(rect['bottom']*H/ah)))
-                        cv2.rectangle(img, top_left, bot_rght, (0, 1, 0), 2)
-                        img_name = '%s_fd_mode_%s.jpg' % (NAME, fd_mode)
-                        its.image.write_image(img, img_name)
-                    assert fnd_faces == NUM_FACES
-                if not faces:
-                    continue
-
-                print 'Frame %d face metadata:' % i
-                print '  Faces:', faces
-                print ''
-
-                # Reasonable scores for faces
-                face_scores = [face['score'] for face in faces]
-                for score in face_scores:
-                    assert score >= 1 and score <= 100
-                # Face bounds should be within active array
-                face_rectangles = [face['bounds'] for face in faces]
-                for rect in face_rectangles:
-                    assert rect['top'] < rect['bottom']
-                    assert rect['left'] < rect['right']
-                    assert 0 <= rect['top'] <= ah
-                    assert 0 <= rect['bottom'] <= ah
-                    assert 0 <= rect['left'] <= aw
-                    assert 0 <= rect['right'] <= aw
-
-if __name__ == '__main__':
-    main()
diff --git a/apps/CameraITS/tests/scene3/scene3_0.5_scaled.pdf b/apps/CameraITS/tests/scene3/scene3_0.5x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene3/scene3_0.5_scaled.pdf
rename to apps/CameraITS/tests/scene3/scene3_0.5x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene3/scene3_0.67_scaled.pdf b/apps/CameraITS/tests/scene3/scene3_0.67x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene3/scene3_0.67_scaled.pdf
rename to apps/CameraITS/tests/scene3/scene3_0.67x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene4/scene4_0.5_scaled.pdf b/apps/CameraITS/tests/scene4/scene4_0.5x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene4/scene4_0.5_scaled.pdf
rename to apps/CameraITS/tests/scene4/scene4_0.5x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene4/scene4_0.67_scaled.pdf b/apps/CameraITS/tests/scene4/scene4_0.67x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene4/scene4_0.67_scaled.pdf
rename to apps/CameraITS/tests/scene4/scene4_0.67x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tools/load_scene.py b/apps/CameraITS/tools/load_scene.py
index e25a3f5..97b6ead 100644
--- a/apps/CameraITS/tools/load_scene.py
+++ b/apps/CameraITS/tools/load_scene.py
@@ -21,6 +21,8 @@
 import its.cv2image
 import numpy as np
 
+LOAD_SCENE_DELAY = 2  # seconds
+
 
 def main():
     """Load charts on device and display."""
@@ -51,10 +53,10 @@
     dst_scene_file = '/sdcard/Download/%s.pdf' % scene
     chart_scaling = its.cv2image.calc_chart_scaling(chart_distance, camera_fov)
     if np.isclose(chart_scaling, its.cv2image.SCALE_TELE_IN_WFOV_BOX, atol=0.01):
-        file_name = '%s_%s_scaled.pdf' % (
+        file_name = '%s_%sx_scaled.pdf' % (
                 scene, str(its.cv2image.SCALE_TELE_IN_WFOV_BOX))
     elif np.isclose(chart_scaling, its.cv2image.SCALE_RFOV_IN_WFOV_BOX, atol=0.01):
-        file_name = '%s_%s_scaled.pdf' % (
+        file_name = '%s_%sx_scaled.pdf' % (
                 scene, str(its.cv2image.SCALE_RFOV_IN_WFOV_BOX))
     else:
         file_name = '%s.pdf' % scene
@@ -63,7 +65,7 @@
     cmd = 'adb -s %s push %s /mnt%s' % (screen_id, src_scene_file,
                                         dst_scene_file)
     subprocess.Popen(cmd.split())
-    time.sleep(1)  # wait-for-device doesn't always seem to work...
+    time.sleep(LOAD_SCENE_DELAY)  # wait-for-device doesn't always seem to work
     # The intent require PDF viewing app be installed on device.
     # Also the first time such app is opened it might request some permission,
     # so it's  better to grant those permissions before using this script
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index c2f4fef..a9d7601 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -29,8 +29,6 @@
 from its.device import ItsSession
 import its.image
 
-import numpy as np
-
 # For sanity checking the installed APK's target SDK version
 MIN_SUPPORTED_SDK_VERSION = 28  # P
 
@@ -51,21 +49,62 @@
 VGA_HEIGHT = 480
 VGA_WIDTH = 640
 
+# All possible scenes
+# Notes on scene names:
+#   scene*_1/2/... are same scene split to load balance run times for scenes
+#   scene*_a/b/... are similar scenes that share one or more tests
+ALL_SCENES = ['scene0', 'scene1_1', 'scene1_2', 'scene2_a', 'scene2_b',
+              'scene2_c', 'scene3', 'scene4', 'scene5', 'sensor_fusion']
+
+# Scenes that are logically grouped and can be called as group
+GROUPED_SCENES = {
+        'scene1': ['scene1_1', 'scene1_2'],
+        'scene2': ['scene2_a', 'scene2_b', 'scene2_c']
+}
+
+# Scenes that can be automated through tablet display
+AUTO_SCENES = ['scene0', 'scene1_1', 'scene1_2', 'scene2_a', 'scene2_b',
+               'scene2_c', 'scene3', 'scene4']
+
+SCENE_REQ = {
+        'scene0': None,
+        'scene1_1': 'A grey card covering at least the middle 30% of the scene',
+        'scene1_2': 'A grey card covering at least the middle 30% of the scene',
+        'scene2_a': 'The picture in tests/scene2_a.pdf with 3 faces',
+        'scene2_b': 'The picture in tests/scene2_b.pdf with 3 faces',
+        'scene2_c': 'The picture in tests/scene2_c.pdf with 3 faces',
+        'scene3': 'The ISO 12233 chart',
+        'scene4': 'A specific test page of a circle covering at least the '
+                  'middle 50% of the scene. See CameraITS.pdf section 2.3.4 '
+                  'for more details',
+        'scene5': 'Capture images with a diffuser attached to the camera. See '
+                  'CameraITS.pdf section 2.3.4 for more details',
+        'sensor_fusion': 'Rotating checkboard pattern. See '
+                         'sensor_fusion/SensorFusion.pdf for detailed '
+                         'instructions.\nNote that this test will be skipped '
+                         'on devices not supporting REALTIME camera timestamp.'
+}
+
+SCENE_EXTRA_ARGS = {
+        'scene5': ['doAF=False']
+}
+
 # Not yet mandated tests
 NOT_YET_MANDATED = {
         'scene0': [
                 'test_test_patterns',
                 'test_tonemap_curve'
         ],
-        'scene1': [
+        'scene1_1': [
                 'test_ae_precapture_trigger',
                 'test_channel_saturation'
         ],
-        'scene2': [
+        'scene1_2': [],
+        'scene2_a': [
                 'test_auto_per_frame_control'
         ],
-        'scene2b': [],
-        'scene2c': [],
+        'scene2_b': [],
+        'scene2_c': [],
         'scene3': [],
         'scene4': [],
         'scene5': [],
@@ -80,19 +119,21 @@
                 'test_read_write',
                 'test_sensor_events'
         ],
-        'scene1': [
+        'scene1_1': [
                 'test_exposure',
                 'test_dng_noise_model',
                 'test_linearity',
+        ],
+        'scene1_2': [
                 'test_raw_exposure',
                 'test_raw_sensitivity'
         ],
-        'scene2': [
+        'scene2_a': [
                 'test_faces',
                 'test_num_faces'
         ],
-        'scene2b': [],
-        'scene2c': [],
+        'scene2_b': [],
+        'scene2_c': [],
         'scene3': [],
         'scene4': [
                 'test_aspect_ratio_and_crop'
@@ -103,6 +144,41 @@
         ]
 }
 
+# Tests run in more than 1 scene.
+# List is created of type ['scene_source', 'test_to_be_repeated']
+# for the test run in current scene.
+REPEATED_TESTS = {
+        'scene0': [],
+        'scene1_1': [],
+        'scene1_2': [],
+        'scene2_a': [],
+        'scene2_b': [
+                ['scene2_a', 'test_num_faces']
+        ],
+        'scene2_c': [
+                ['scene2_a', 'test_num_faces']
+        ],
+        'scene3': [],
+        'scene4': [],
+        'scene5': [],
+        'sensor_fusion': []
+}
+
+
+def expand_scene(scene, scenes):
+    """Expand a grouped scene and append its sub_scenes to scenes.
+    Args:
+        scene:      scene in GROUPED_SCENES dict
+        scenes:     list of scenes to append to
+
+    Returns:
+        updated scenes
+    """
+    print 'Expanding %s to %s.' % (scene, str(GROUPED_SCENES[scene]))
+    for sub_scene in GROUPED_SCENES[scene]:
+        scenes.append(sub_scene)
+
+
 def run_subprocess_with_timeout(cmd, fout, ferr, outdir):
     """Run subprocess with a timeout.
 
@@ -191,50 +267,24 @@
                  camera Ids. Ex: "camera=0,1" or "camera=1"
         device:  device id for adb
         scenes:  the test scene(s) to be executed. Use comma to separate
-                 multiple scenes. Ex: "scenes=scene0,scene1" or
-                 "scenes=0,1,sensor_fusion" (sceneX can be abbreviated by X
-                 where X is a integer)
-        chart:   [Experimental] another android device served as test chart
-                 display. When this argument presents, change of test scene
+                 multiple scenes. Ex: "scenes=scene0,scene1_1" or
+                 "scenes=0,1_1,sensor_fusion" (sceneX can be abbreviated by X
+                 where X is scene name minus 'scene')
+        chart:   another android device served as test chart display.
+                 When this argument presents, change of test scene
                  will be handled automatically. Note that this argument
                  requires special physical/hardware setup to work and may not
                  work on all android devices.
         result:  Device ID to forward results to (in addition to the device
                  that the tests are running on).
-        rot_rig: [Experimental] ID of the rotation rig being used (formatted as
+        rot_rig: ID of the rotation rig being used (formatted as
                  "<vendor ID>:<product ID>:<channel #>" or "default")
         tmp_dir: location of temp directory for output files
         skip_scene_validation: force skip scene validation. Used when test scene
                  is setup up front and don't require tester validation.
-        dist:    [Experimental] chart distance in cm.
+        dist:    chart distance in cm.
     """
 
-    all_scenes = ["scene0", "scene1", "scene2", "scene2b", "scene2c", "scene3", "scene4", "scene5",
-                  "sensor_fusion"]
-
-    auto_scenes = ["scene0", "scene1", "scene2", "scene2b", "scene2c", "scene3", "scene4"]
-
-    scene_req = {
-        "scene0": None,
-        "scene1": "A grey card covering at least the middle 30% of the scene",
-        "scene2": "A picture containing human faces",
-        "scene2b": "A picture containing human faces",
-        "scene2c": "A picture containing human faces",
-        "scene3": "The ISO 12233 chart",
-        "scene4": "A specific test page of a circle covering at least the "
-                  "middle 50% of the scene. See CameraITS.pdf section 2.3.4 "
-                  "for more details",
-        "scene5": "Capture images with a diffuser attached to the camera. See "
-                  "CameraITS.pdf section 2.3.4 for more details",
-        "sensor_fusion": "Rotating checkboard pattern. See "
-                         "sensor_fusion/SensorFusion.pdf for detailed "
-                         "instructions.\nNote that this test will be skipped "
-                         "on devices not supporting REALTIME camera timestamp."
-    }
-    scene_extra_args = {
-        "scene5": ["doAF=False"]
-    }
-
     camera_id_combos = []
     scenes = []
     chart_host_id = None
@@ -275,7 +325,7 @@
     merge_result_switch = result_device_id is not None
 
     # Run through all scenes if user does not supply one
-    possible_scenes = auto_scenes if auto_scene_switch else all_scenes
+    possible_scenes = AUTO_SCENES if auto_scene_switch else ALL_SCENES
     if not scenes:
         scenes = possible_scenes
     else:
@@ -285,14 +335,19 @@
         for s in scenes:
             if s in possible_scenes:
                 temp_scenes.append(s)
+            elif GROUPED_SCENES.has_key(s):
+                expand_scene(s, temp_scenes)
             else:
                 try:
                     # Try replace "X" to "sceneX"
                     scene_str = "scene" + s
-                    if scene_str not in possible_scenes:
+                    if scene_str in possible_scenes:
+                        temp_scenes.append(scene_str)
+                    elif GROUPED_SCENES.has_key(scene_str):
+                        expand_scene(scene_str, temp_scenes)
+                    else:
                         valid_scenes = False
                         break
-                    temp_scenes.append(scene_str)
                 except ValueError:
                     valid_scenes = False
                     break
@@ -300,7 +355,8 @@
         if not valid_scenes:
             print 'Unknown scene specified:', s
             assert False
-        scenes = temp_scenes
+        # assign temp_scenes back to scenes and remove duplicates
+        scenes = sorted(set(temp_scenes), key=temp_scenes.index)
 
     # Make output directories to hold the generated files.
     topdir = tempfile.mkdtemp(dir=tmp_dir)
@@ -395,11 +451,11 @@
         # Initialize test results
         results = {}
         result_key = ItsSession.RESULT_KEY
-        for s in all_scenes:
+        for s in ALL_SCENES:
             results[s] = {result_key: ItsSession.RESULT_NOT_EXECUTED}
 
         camera_fov = calc_camera_fov(id_combo.id, id_combo.sub_id)
-        id_combo_string = id_combo.id;
+        id_combo_string = id_combo.id
         has_hidden_sub_camera = id_combo.sub_id is not None
         if has_hidden_sub_camera:
             id_combo_string += ":" + id_combo.sub_id
@@ -416,9 +472,12 @@
         tot_pass = 0
         for scene in scenes:
             skip_code = None
-            tests = [(s[:-3], os.path.join("tests", scene, s))
-                     for s in os.listdir(os.path.join("tests", scene))
-                     if s[-3:] == ".py" and s[:4] == "test"]
+            tests = [(s[:-3], os.path.join('tests', scene, s))
+                     for s in os.listdir(os.path.join('tests', scene))
+                     if s[-3:] == '.py' and s[:4] == 'test']
+            if REPEATED_TESTS[scene]:
+                for t in REPEATED_TESTS[scene]:
+                    tests.append((t[1], os.path.join('tests', t[0], t[1]+'.py')))
             tests.sort()
             tot_tests.extend(tests)
 
@@ -428,7 +487,7 @@
             num_not_mandated_fail = 0
             numfail = 0
             validate_switch = True
-            if scene_req[scene] is not None:
+            if SCENE_REQ[scene] is not None:
                 out_path = os.path.join(topdir, id_combo_string, scene+".jpg")
                 out_arg = "out=" + out_path
                 if scene == 'sensor_fusion':
@@ -451,8 +510,8 @@
                 else:
                     # Skip scene validation under certain conditions
                     if validate_switch and not merge_result_switch:
-                        scene_arg = 'scene=' + scene_req[scene]
-                        extra_args = scene_extra_args.get(scene, [])
+                        scene_arg = 'scene=' + SCENE_REQ[scene]
+                        extra_args = SCENE_EXTRA_ARGS.get(scene, [])
                         cmd = ['python',
                                os.path.join(os.getcwd(),
                                             'tools/validate_scene.py'),
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index af6dc5b..15ddafa 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -56,6 +56,7 @@
 LOCAL_JAVA_LIBRARIES += android.test.runner.stubs
 LOCAL_JAVA_LIBRARIES += android.test.base.stubs
 LOCAL_JAVA_LIBRARIES += android.test.mock.stubs
+LOCAL_JAVA_LIBRARIES += android.car
 LOCAL_JAVA_LIBRARIES += voip-common
 
 LOCAL_PACKAGE_NAME := CtsVerifier
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index dec48ea..74c7c80 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -22,6 +22,7 @@
 
     <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="29"/>
 
+    <uses-permission android:name="android.car.permission.CAR_POWERTRAIN" />
     <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
@@ -159,6 +160,17 @@
             android:theme="@style/OverlayTheme"
             android:label="Overlaying Activity"/>
 
+        <activity
+            android:name=".battery.BatterySaverTestActivity"
+            android:label="@string/battery_saver_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_other" />
+        </activity>
+
         <activity android:name=".forcestop.RecentTaskRemovalTestActivity"
                   android:label="@string/remove_from_recents_test"
                   android:configChanges="keyboardHidden|orientation|screenSize">
@@ -1308,6 +1320,16 @@
                 android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
         </activity>
 
+        <activity android:name=".security.ProtectedConfirmationTest"
+            android:label="@string/sec_protected_confirmation_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_security" />
+        </activity>
+
         <activity android:name=".security.ScreenLockBoundKeysTest"
                 android:label="@string/sec_lock_bound_key_test"
                 android:configChanges="keyboardHidden|orientation|screenSize" >
@@ -2074,12 +2096,26 @@
             </intent-filter>
         </receiver>
 
+        <receiver android:name=".notifications.AutomaticZenRuleStatusReceiver">
+            <intent-filter>
+                <action android:name="android.app.action.AUTOMATIC_ZEN_RULE_STATUS_CHANGED"/>
+            </intent-filter>
+        </receiver>
+
         <activity android:name=".notifications.ConditionProviderVerifierActivity"
                   android:label="@string/cp_test">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.cts.intent.category.MANUAL_TEST" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.app.action.AUTOMATIC_ZEN_RULE" />
+            </intent-filter>
+            <meta-data android:name="android.service.zen.automatic.ruleType"
+                       android:value="@string/cp_rule_type" />
+            <meta-data android:name="android.service.zen.automatic.ruleInstanceLimit"
+                       android:value="2" />
+
             <meta-data android:name="test_category" android:value="@string/test_category_notifications" />
             <meta-data android:name="test_excluded_features"
                        android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
@@ -3500,6 +3536,30 @@
             </intent-filter>
         </activity-alias>
 
+        <activity android:name=".car.GearSelectionTestActivity"
+                android:label="@string/gear_selection_test">
+            <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_car" />
+            <meta-data
+                android:name="test_required_features"
+                android:value="android.hardware.type.automotive"/>
+        </activity>
+
+        <activity android:name=".car.ParkingBrakeOnTestActivity"
+                android:label="@string/parking_brake_on_test">
+            <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_car" />
+            <meta-data
+                android:name="test_required_features"
+                android:value="android.hardware.type.automotive"/>
+        </activity>
+
         <!-- 6DoF sensor test -->
         <activity
                 android:name="com.android.cts.verifier.sensors.sixdof.Activities.StartActivity"
diff --git a/apps/CtsVerifier/res/layout/gear_selection_test.xml b/apps/CtsVerifier/res/layout/gear_selection_test.xml
new file mode 100644
index 0000000..20508c1
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/gear_selection_test.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:orientation="horizontal">
+
+    <TextView
+          android:id="@+id/expected_gear_selection_title"
+          android:layout_width="0dp"
+          android:layout_weight="1"
+          android:layout_height="wrap_content"
+          android:gravity="center"
+          android:text="@string/expected_gear_selection_title"
+          android:textSize="50dip" />
+
+    <TextView
+          android:id="@+id/current_gear_selection_title"
+          android:layout_width="0dp"
+          android:layout_weight="1"
+          android:layout_height="wrap_content"
+          android:gravity="center"
+          android:text="@string/current_gear_selection_title"
+          android:textSize="50dip" />
+
+
+    </LinearLayout>
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="2"
+        android:orientation="horizontal">
+
+    <TextView
+          android:id="@+id/expected_gear_selection"
+          android:layout_width="0dp"
+          android:layout_weight="1"
+          android:layout_height="wrap_content"
+          android:gravity="center"
+          android:textSize="75dip" />
+
+    <TextView
+          android:id="@+id/current_gear_selection"
+          android:layout_width="0dp"
+          android:layout_weight="1"
+          android:layout_height="wrap_content"
+          android:gravity="center"
+          android:textSize="75dip" />
+
+    </LinearLayout>
+
+    <include layout="@layout/pass_fail_buttons" />
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/iva_pass_fail_item.xml b/apps/CtsVerifier/res/layout/iva_pass_fail_item.xml
new file mode 100644
index 0000000..4bf5b63
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/iva_pass_fail_item.xml
@@ -0,0 +1,78 @@
+<?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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content" >
+
+    <ImageView
+        android:id="@+id/nls_status"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentLeft="true"
+        android:layout_alignParentTop="true"
+        android:layout_marginTop="10dip"
+        android:contentDescription="@string/pass_button_text"
+        android:padding="10dip"
+        android:src="@drawable/fs_indeterminate" />
+
+    <TextView
+        android:id="@+id/nls_instructions"
+        style="@style/InstructionsSmallFont"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:layout_alignParentTop="true"
+        android:layout_toRightOf="@id/nls_status"
+        android:text="@string/nls_enable_service" />
+
+    <Button
+        android:id="@+id/iva_action_button_pass"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:layout_below="@id/nls_instructions"
+        android:layout_marginLeft="20dip"
+        android:layout_marginRight="20dip"
+        android:layout_toRightOf="@id/nls_status"
+        android:text="@string/iva_pass"
+        android:onClick="actionPassed" />
+    <Button
+        android:id="@+id/iva_action_button_fail"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:layout_toRightOf="@id/nls_status"
+        android:layout_below="@id/iva_action_button_pass"
+        android:layout_marginLeft="20dip"
+        android:layout_marginRight="20dip"
+        android:onClick="actionFailed"
+        android:text="@string/iva_fail" />
+
+    <Button
+        android:id="@+id/nls_action_button"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:layout_below="@id/nls_instructions"
+        android:layout_marginLeft="20dip"
+        android:layout_marginRight="20dip"
+        android:layout_toRightOf="@id/nls_status"
+        android:onClick="actionPressed"
+        android:visibility="gone"
+        android:text="@string/nls_start_settings" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/ordered_test.xml b/apps/CtsVerifier/res/layout/ordered_test.xml
new file mode 100644
index 0000000..1c0b029
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/ordered_test.xml
@@ -0,0 +1,39 @@
+<?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
+  -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+    <TextView
+        android:id="@+id/txt_instruction"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        style="@style/InstructionsSmallFont"
+        android:layout_alignParentTop="true" />
+
+    <Button
+        android:id="@+id/btn_next"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/next_button_text"
+        android:layout_below="@id/txt_instruction" />
+
+    <include android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            layout="@layout/pass_fail_buttons" />
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/parking_brake_on_test.xml b/apps/CtsVerifier/res/layout/parking_brake_on_test.xml
new file mode 100644
index 0000000..23faaab
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/parking_brake_on_test.xml
@@ -0,0 +1,56 @@
+<?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/instruction"
+          android:layout_width="match_parent"
+          android:layout_height="0dp"
+          android:layout_weight="1"
+          android:gravity="center"
+          android:textSize="50dip" />
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="2"
+        android:orientation="horizontal">
+
+    <TextView
+          android:id="@+id/current_parking_brake_on_value_title"
+          android:layout_width="0dp"
+          android:layout_weight="2"
+          android:layout_height="match_parent"
+          android:gravity="center"
+          android:text="@string/current_parking_brake_on_value_title"
+          android:textSize="50dip" />
+
+    <TextView
+          android:id="@+id/current_parking_brake_on_value"
+          android:layout_width="0dp"
+          android:layout_weight="1"
+          android:layout_height="match_parent"
+          android:gravity="center"
+          android:textSize="50dip" />
+
+    </LinearLayout>
+
+    <include layout="@layout/pass_fail_buttons" />
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/screen_pinning.xml b/apps/CtsVerifier/res/layout/screen_pinning.xml
deleted file mode 100644
index f1d7b65..0000000
--- a/apps/CtsVerifier/res/layout/screen_pinning.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-<?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.
--->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent" >
-
-    <TextView
-        android:id="@+id/error_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content" />
-
-    <ScrollView
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_alignParentTop="true" >
-
-        <RelativeLayout
-            android:id="@+id/instructions_group"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="vertical" >
-
-            <LinearLayout
-                android:id="@+id/instructions_list"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:orientation="vertical" >
-
-            </LinearLayout>
-
-            <Button
-                android:id="@+id/next_button"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_alignParentEnd="true"
-                android:layout_below="@id/instructions_list"
-                android:text="@string/next_button_text" />
-
-        </RelativeLayout>
-    </ScrollView>
-
-    <include
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_alignParentBottom="true"
-        layout="@layout/pass_fail_buttons" />
-
-</RelativeLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/sec_protected_confirmation_main.xml b/apps/CtsVerifier/res/layout/sec_protected_confirmation_main.xml
new file mode 100644
index 0000000..ffa8d46
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/sec_protected_confirmation_main.xml
@@ -0,0 +1,78 @@
+<?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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:padding="10dip"
+        >
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                  android:id="@+id/sec_protected_confirmation_tee_layout"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:padding="10dp"
+                  android:orientation="horizontal"
+                  android:layout_centerInParent="true"
+                  android:gravity="center"
+                  >
+
+        <Button android:id="@+id/sec_start_test_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/sec_protected_confirmation_tee_test"
+        />
+
+        <ImageView android:id="@+id/sec_protected_confirmation_tee_test_success"
+                   android:layout_width="wrap_content"
+                   android:layout_height="wrap_content"
+                   android:src="@drawable/fs_good"
+        />
+
+    </LinearLayout>
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                  android:id="@+id/sec_protected_confirmation_strongbox_layout"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:padding="10dp"
+                  android:orientation="horizontal"
+                  android:layout_centerHorizontal="true"
+                  android:layout_below="@id/sec_protected_confirmation_tee_layout"
+                  android:gravity="center"
+                  >
+
+        <Button android:id="@+id/sec_start_test_strongbox_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/sec_protected_confirmation_strongbox_test"
+        />
+
+        <ImageView android:id="@+id/sec_protected_confirmation_strongbox_test_success"
+                   android:layout_width="wrap_content"
+                   android:layout_height="wrap_content"
+                   android:src="@drawable/fs_good"
+        />
+
+    </LinearLayout>
+
+    <include android:layout_width="match_parent"
+             android:layout_height="wrap_content"
+             android:layout_alignParentBottom="true"
+             layout="@layout/pass_fail_buttons"
+    />
+
+</RelativeLayout>
+
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 22109e7..a2e7ce9 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -23,6 +23,7 @@
     <string name="fail_button_text">Fail</string>
     <string name="next_button_text">Next</string>
     <string name="go_button_text">Go</string>
+    <string name="tests_completed_successfully">All tests completed successfully.</string>
 
     <string name="retry_button_text">Retry</string>
     <string name="finish_button_text">Finish</string>
@@ -76,6 +77,35 @@
     <string name="bu_generate_error">Error occurred while generating test data...</string>
     <string name="bu_settings">Settings</string>
 
+    <!-- Strings for BatterySaverTestActivity -->
+    <string name="battery_saver_test">Battery Saver Test</string>
+    <string name="battery_saver_test_info">
+        This test verifies that the device provides a user affordance to enable and disable
+        Battery Saver feature.
+    </string>
+    <string name="battery_saver_test_no_battery_detected">
+        No battery detected. This test will only work on devices that contain a battery.
+    </string>
+    <string name="battery_saver_test_unplug">
+        Unplug the device or run \"adb shell dumpsys battery unplug\".
+    </string>
+    <string name="battery_saver_test_device_plugged_in">
+        The device is still plugged in.
+    </string>
+    <string name="battery_saver_test_enable_bs">
+        Look for a button that allows you to turn Battery Saver on and off. The button may be in the
+        Quick Settings section or on the Battery page of device settings. Use the button to turn
+        Battery Saver on.
+    </string>
+    <string name="battery_saver_test_disable_bs">
+        Now use the button to turn Battery Saver off.
+    </string>
+    <string name="battery_saver_test_start_turn_bs_off">
+        Turn Battery Saver off to begin the test.
+    </string>
+    <string name="battery_saver_test_bs_on">Battery Saver is on.</string>
+    <string name="battery_saver_test_bs_off">Battery Saver is off.</string>
+
     <!-- Strings for Device Administration tests -->
     <string name="da_policy_serialization_test">Policy Serialization Test</string>
     <string name="da_policy_serialization_info">This test checks that a device policy is properly
@@ -104,6 +134,19 @@
         framework correctly tries to open the CAR_DOCK app again.</string>
     <string name="car_mode_enable">Enable Car Mode</string>
     <string name="car_dock_activity_text">Press the Home button</string>
+    <string name="gear_selection_test">Gear Selection Test</string>
+    <string name="gear_selection_test_desc">This test ensures that the
+      GEAR_SELECTION property is implemented correctly.\n\nShift the car\'s
+      gear to the expected gear value. If it is able to shift to all the
+      implemented gears then the pass button will be enabled.</string>
+    <string name="expected_gear_selection_title">Expected Gear Selection</string>
+    <string name="current_gear_selection_title">Current Gear Selection</string>
+    <string name="parking_brake_on_test">PARKING_BRAKE_ON Test</string>
+    <string name="parking_brake_on_test_desc">This test ensures that the
+      PARKING_BRAKE_ON property is implemented correctly.\n\nFollow the
+      instructions on the screen to engage and disengage the parking brake. When
+      the instructions are completed, the pass button will be enabled.</string>
+    <string name="current_parking_brake_on_value_title">Current PARKING_BRAKE_ON Value:</string>
     <string name="da_no_policy">1. Press the \"Generate Policy\" to create a random device
         policy\n\n2. Press \"Apply Policy\" to put the policy into effect.\n\n3. Reboot your
         device and return to this test in the CTS Verifier.
@@ -217,6 +260,23 @@
         are unusable without an authentication.
     </string>
 
+    <!-- Strings for protected confirmation test -->
+    <string name="sec_protected_confirmation_test">Android Protected Confirmation Test</string>
+    <string name="sec_protected_confirmation_test_info">
+        This test ensures that - if Android Protected Confirmation is supported by the device under
+        test - the user is able to see the confirmation message and confirm it. It also assures that
+        Keymaster signs a message with a confirmation key only if the message was previously
+        confirmed by the user.
+    </string>
+    <string name="sec_protected_confirmation_not_supported_title">Android Protected Confirmation not supported</string>
+    <string name="sec_protected_confirmation_not_supported_info">
+        Android Protected Confirmation is not implemented by this device. This is okay because this
+        is an optional feature. Set this test to passed and continue.
+    </string>
+    <string name="sec_protected_confirmation_message">This is a CtsVerifier test message!</string>
+    <string name="sec_protected_confirmation_tee_test">Start Tee Test</string>
+    <string name="sec_protected_confirmation_strongbox_test">Start Strongbox Test</string>
+
     <!-- Strings for BluetoothActivity -->
     <string name="bluetooth_test">Bluetooth Test</string>
     <string name="bluetooth_test_info">
@@ -2010,9 +2070,17 @@
     <string name="cp_service_started">Service should start once enabled.</string>
     <string name="cp_service_stopped">Service should stop once disabled.</string>
     <string name="cp_unsubscribe_rule">Unsubscribing to Automatic Zen Rule</string>
-    <string name="cp_delete_rule">Deleting Automatic Zen Rule</string>
+    <string name="cp_delete_rule">Deleting Automatic Zen Rule via api</string>
     <string name="cp_get_rules">Retrieving Automatic Zen Rules</string>
     <string name="cp_get_rule">Retrieving Automatic Zen Rule</string>
+    <string name="cp_show_rules">Click this button to launch the Automatic Zen Rule listing page in settings, and then return to this screen</string>
+    <string name="cp_show_rules_verification">Was the automatic rule screen shown?</string>
+    <string name="cp_disable_rule">Please disable rule "123" and return here.</string>
+    <string name="cp_enable_rule">Please enable rule "123" and return here.</string>
+    <string name="cp_delete_rule_broadcast">Please delete rule "123" and return here.</string>
+    <string name="cp_rule_type">CTS rule</string>
+    <string name="iva_pass">Pass</string>
+    <string name="iva_fail">Fail</string>
 
     <string name="cacert_test">CA Cert Notification Test</string>
     <string name="cacert_info">This test checks that when a CA Certificate is installed, the user is notified.</string>
@@ -4342,7 +4410,6 @@
     <string name="screen_pin_check_pinned">Press Next to verify the app is pinned.</string>
     <string name="screen_pin_no_exit">Try to leave the app without unpinning the screen. Press next once you have verified you cannot leave.</string>
     <string name="screen_pin_exit">Use interactions defined by your device to unpin such as long pressing the back and overview button, then press next.</string>
-    <string name="screen_pinning_done">All tests completed successfully.</string>
 
     <string name="error_screen_no_longer_pinned">The screen was no longer pinned.</string>
     <string name="error_screen_already_pinned">Cannot start the test with the screen already pinned.</string>
@@ -5103,10 +5170,15 @@
     <string name="bubbles_notification_test_verify_9">Click the button below and verify that a bubble
         appears on screen, auto-expanded.</string>
     <string name="bubbles_notification_test_button_9">Send auto-expanded bubble notification</string>
-    <string name="bubbles_notification_no_bubbles_low_mem">No bubbles on low memory device; no tests to run</string>
+    <string name="bubbles_notification_test_title_10">No bubbles on low memory device</string>
+    <string name="bubbles_notification_test_verify_10">Click the button below and verify that a
+        bubble does NOT appear on screen. Verify that there is a notification in the notification
+        shade.</string>
+    <string name="bubbles_notification_test_button_10">Add bubble</string>
     <string name="bubbles_test_summary_title">Test Complete</string>
     <string name="bubbles_test_summary">%1$d out of %2$d tests passed</string>
     <string name="bubble_activity_title">Bubble Activity</string>
+
     <!-- Strings for Instant Apps -->
     <string name="ia_instruction_heading_label">Instructions:</string>
     <string name="ia_instruction_text_photo_label">READ BEFORE STARTING TEST</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/OrderedTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/OrderedTestActivity.java
new file mode 100644
index 0000000..d24fdb7
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/OrderedTestActivity.java
@@ -0,0 +1,122 @@
+/*
+ * 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.verifier;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * {@link PassFailButtons.Activity} that supports showing a series of tests in order.
+ */
+public abstract class OrderedTestActivity extends PassFailButtons.Activity {
+    private static final String KEY_CURRENT_TEST = "current_test";
+
+    private Test[] mTests;
+    private int mTestIndex;
+
+    private Button mNextButton;
+    private TextView mInstructions;
+
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ordered_test);
+        setPassFailButtonClickListeners();
+
+        mTests = getTests();
+
+        mInstructions = findViewById(R.id.txt_instruction);
+
+        mNextButton = findViewById(R.id.btn_next);
+        mNextButton.setOnClickListener(v -> {
+            if ((mTestIndex >= 0) && (mTestIndex < mTests.length)) {
+                mTests[mTestIndex].onNextClick();
+            }
+        });
+
+        // Don't allow pass until all tests complete.
+        findViewById(R.id.pass_button).setVisibility(View.GONE);
+
+        // Figure out if we are in a test or starting from the beginning.
+        if (savedInstanceState != null && savedInstanceState.containsKey(KEY_CURRENT_TEST)) {
+            mTestIndex = savedInstanceState.getInt(KEY_CURRENT_TEST);
+        } else {
+            mTestIndex = 0;
+        }
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+
+        mTests[mTestIndex].run();
+    }
+
+    /** Returns a list of tests to run in order. */
+    protected abstract Test[] getTests();
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        outState.putInt(KEY_CURRENT_TEST, mTestIndex);
+    }
+
+    protected void succeed() {
+        runOnUiThread(() -> {
+            mTestIndex++;
+            if (mTestIndex < mTests.length) {
+                mTests[mTestIndex].run();
+            } else {
+                // On test completion, hide "next" and "fail" buttons, and show "pass" button
+                // instead.
+                mInstructions.setText(R.string.tests_completed_successfully);
+                mNextButton.setVisibility(View.GONE);
+                findViewById(R.id.pass_button).setVisibility(View.VISIBLE);
+                findViewById(R.id.fail_button).setVisibility(View.GONE);
+            }
+        });
+    }
+
+    protected void error(int stringResId) {
+        Toast.makeText(this, stringResId, Toast.LENGTH_SHORT).show();
+    }
+
+    protected abstract class Test {
+        private final int mStringId;
+
+        public Test(int stringResId) {
+            mStringId = stringResId;
+        }
+
+        protected void run() {
+            showText();
+        }
+
+        protected void showText() {
+            if (mStringId == 0) {
+                return;
+            }
+            mInstructions.setText(mStringId);
+        }
+
+        protected void onNextClick() {
+        }
+    }
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/battery/BatterySaverTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/battery/BatterySaverTestActivity.java
new file mode 100644
index 0000000..f0dc540
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/battery/BatterySaverTestActivity.java
@@ -0,0 +1,145 @@
+/*
+ * 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.verifier.battery;
+
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.view.View;
+
+import com.android.cts.verifier.OrderedTestActivity;
+import com.android.cts.verifier.R;
+
+public class BatterySaverTestActivity extends OrderedTestActivity {
+    private PowerManager mPowerManager;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setInfoResources(R.string.battery_saver_test, R.string.battery_saver_test_info, -1);
+
+        mPowerManager = getSystemService(PowerManager.class);
+    }
+
+    @Override
+    protected OrderedTestActivity.Test[] getTests() {
+        return new Test[]{
+                // Confirm a battery exists before proceeding with the rest of the tests,
+                mConfirmBatteryExists,
+                // Verify device is unplugged.
+                mConfirmUnplugged,
+                // Verify battery saver is off.
+                mConfirmBsOff,
+                mEnableBs,
+                mDisableBs
+        };
+    }
+
+    private final Test mConfirmBatteryExists = new Test(
+            R.string.battery_saver_test_no_battery_detected) {
+        @Override
+        protected void run() {
+            super.run();
+
+            final Intent batteryInfo = registerReceiver(null, new
+                    IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+
+            if (batteryInfo.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true)) {
+                succeed();
+            } else {
+                findViewById(R.id.btn_next).setVisibility(View.GONE);
+                // Set both pass and fail to visible in case the device is meant to have a battery.
+                findViewById(R.id.pass_button).setVisibility(View.VISIBLE);
+                findViewById(R.id.fail_button).setVisibility(View.VISIBLE);
+            }
+        }
+    };
+
+    private final Test mConfirmUnplugged = new Test(R.string.battery_saver_test_unplug) {
+        @Override
+        protected void run() {
+            super.run();
+
+            if (!isPluggedIn()) {
+                succeed();
+            }
+        }
+
+        @Override
+        protected void onNextClick() {
+            if (isPluggedIn()) {
+                error(R.string.battery_saver_test_device_plugged_in);
+            } else {
+                succeed();
+            }
+        }
+
+        private boolean isPluggedIn() {
+            Intent intent = registerReceiver(null,
+                    new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+            int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
+            return plugged == BatteryManager.BATTERY_PLUGGED_AC
+                    || plugged == BatteryManager.BATTERY_PLUGGED_USB
+                    || plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS;
+        }
+    };
+
+    private final Test mConfirmBsOff = new Test(R.string.battery_saver_test_start_turn_bs_off) {
+        @Override
+        protected void run() {
+            super.run();
+
+            if (!mPowerManager.isPowerSaveMode()) {
+                succeed();
+            }
+        }
+
+        @Override
+        protected void onNextClick() {
+            if (mPowerManager.isPowerSaveMode()) {
+                error(R.string.battery_saver_test_bs_on);
+            } else {
+                succeed();
+            }
+        }
+    };
+
+    private final Test mEnableBs = new Test(R.string.battery_saver_test_enable_bs) {
+        @Override
+        protected void onNextClick() {
+            if (mPowerManager.isPowerSaveMode()) {
+                succeed();
+            } else {
+                error(R.string.battery_saver_test_bs_off);
+            }
+        }
+    };
+
+    private final Test mDisableBs = new Test(R.string.battery_saver_test_disable_bs) {
+        @Override
+        protected void onNextClick() {
+            if (!mPowerManager.isPowerSaveMode()) {
+                succeed();
+            } else {
+                error(R.string.battery_saver_test_bs_on);
+            }
+        }
+    };
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
index 7745898..f39b590 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
@@ -42,6 +42,7 @@
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
+import android.media.AudioAttributes;
 import android.media.Image;
 import android.media.ImageReader;
 import android.media.ImageWriter;
@@ -147,6 +148,7 @@
     public static final String TRIGGER_AF_KEY = "af";
     public static final String VIB_PATTERN_KEY = "pattern";
     public static final String EVCOMP_KEY = "evComp";
+    public static final String AUDIO_RESTRICTION_MODE_KEY = "mode";
 
     private CameraManager mCameraManager = null;
     private HandlerThread mCameraThread = null;
@@ -264,7 +266,7 @@
             mSensorThread.start();
             mSensorHandler = new Handler(mSensorThread.getLooper());
             mSensorManager.registerListener(this, mAccelSensor,
-                    SensorManager.SENSOR_DELAY_NORMAL, mSensorHandler);
+                    /*100hz*/ 10000, mSensorHandler);
             mSensorManager.registerListener(this, mMagSensor,
                     SensorManager.SENSOR_DELAY_NORMAL, mSensorHandler);
             mSensorManager.registerListener(this, mGyroSensor,
@@ -687,6 +689,8 @@
                     doCapture(cmdObj);
                 } else if ("doVibrate".equals(cmdObj.getString("cmdName"))) {
                     doVibrate(cmdObj);
+                } else if ("setAudioRestriction".equals(cmdObj.getString("cmdName"))) {
+                    doSetAudioRestriction(cmdObj);
                 } else if ("getCameraIds".equals(cmdObj.getString("cmdName"))) {
                     doGetCameraIds();
                 } else if ("doReprocessCapture".equals(cmdObj.getString("cmdName"))) {
@@ -907,6 +911,7 @@
             obj.put("accel", mAccelSensor != null);
             obj.put("mag", mMagSensor != null);
             obj.put("gyro", mGyroSensor != null);
+            obj.put("vibrator", mVibrator.hasVibrator());
             mSocketRunnableObj.sendResponse("sensorExistence", null, obj, null);
         } catch (org.json.JSONException e) {
             throw new ItsException("JSON error: ", e);
@@ -1305,13 +1310,35 @@
                 pattern[i] = patternArray.getLong(i);
             }
             Logt.i(TAG, String.format("Starting vibrator, pattern length %d",len));
-            mVibrator.vibrate(pattern, -1);
+
+            // Mark the vibrator as alarm to test the audio restriction API
+            // TODO: consider making this configurable
+            AudioAttributes audioAttributes = new AudioAttributes.Builder()
+                    .setUsage(AudioAttributes.USAGE_ALARM).build();
+            mVibrator.vibrate(pattern, -1, audioAttributes);
             mSocketRunnableObj.sendResponse("vibrationStarted", "");
         } catch (org.json.JSONException e) {
             throw new ItsException("JSON error: ", e);
         }
     }
 
+    private void doSetAudioRestriction(JSONObject params) throws ItsException {
+        try {
+            if (mCamera == null) {
+                throw new ItsException("Camera is closed");
+            }
+            int mode = params.getInt(AUDIO_RESTRICTION_MODE_KEY);
+            mCamera.setCameraAudioRestriction(mode);
+            Logt.i(TAG, String.format("Set audio restriction mode to %d", mode));
+
+            mSocketRunnableObj.sendResponse("audioRestrictionSet", "");
+        } catch (org.json.JSONException e) {
+            throw new ItsException("JSON error: ", e);
+        } catch (android.hardware.camera2.CameraAccessException e) {
+            throw new ItsException("Access error: ", e);
+        }
+    }
+
     /**
      * Parse jsonOutputSpecs to get output surface sizes and formats. Create input and output
      * image readers for the parsed output surface sizes, output formats, and the given input
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
index 7d2f25d..1a59cb1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
@@ -84,8 +84,11 @@
     // Scenes
     private static final ArrayList<String> mSceneIds = new ArrayList<String> () { {
             add("scene0");
-            add("scene1");
-            add("scene2");
+            add("scene1_1");
+            add("scene1_2");
+            add("scene2_a");
+            add("scene2_b");
+            add("scene2_c");
             add("scene3");
             add("scene4");
             add("scene5");
@@ -95,8 +98,9 @@
     private static final ArrayList<String> mHiddenPhysicalCameraSceneIds =
             new ArrayList<String> () { {
                     add("scene0");
-                    add("scene1");
-                    add("scene2");
+                    add("scene1_1");
+                    add("scene1_2");
+                    add("scene2_a");
                     add("scene4");
                     add("sensor_fusion");
              }};
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java
index 3904f09..bfc41e4 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java
@@ -138,16 +138,12 @@
      * @see #MEDIA_TYPE_IMAGE
      * @see #MEDIA_TYPE_VIDEO
      */
-    private static File getOutputMediaFile(int type) {
-        // Question: why do I need to comment this to get it working?
-        // Logcat says "external storage not ready"
-        // if (Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED) {
-        //     Log.e(TAG, "external storage not ready");
-        //     return null;
-        // }
-
-        File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
-                Environment.DIRECTORY_MOVIES), TAG);
+    private File getOutputMediaFile(int type) {
+        File mediaStorageDir = new File(getExternalFilesDir(null), TAG);
+        if (mediaStorageDir == null) {
+            Log.e(TAG, "failed to retrieve external files directory");
+            return null;
+        }
 
         if (!mediaStorageDir.exists()) {
             if (!mediaStorageDir.mkdirs()) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/car/GearSelectionTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/car/GearSelectionTestActivity.java
new file mode 100644
index 0000000..89197d7
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/car/GearSelectionTestActivity.java
@@ -0,0 +1,121 @@
+/*
+ * 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.verifier.car;
+
+import android.car.Car;
+import android.car.VehicleGear;
+import android.car.hardware.CarPropertyConfig;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.CarPropertyManager;
+import android.car.VehicleAreaType;
+import android.car.VehiclePropertyIds;
+import android.os.Bundle;
+import android.widget.TextView;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.util.Arrays;
+import java.util.List;
+
+/** A CTS Verifier test case to verify GEAR_SELECTION is implemented correctly.*/
+public class GearSelectionTestActivity extends PassFailButtons.Activity {
+    private static final String TAG = GearSelectionTestActivity.class.getSimpleName();
+    private List<Integer> mSupportedGears;
+    private int mGearsAchievedCount = 0;
+    private TextView mExpectedGearSelectionTextView;
+    private TextView mCurrentGearSelectionTextView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Setup the UI.
+        setContentView(R.layout.gear_selection_test);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.gear_selection_test, R.string.gear_selection_test_desc, -1);
+        getPassButton().setEnabled(false);
+
+        mExpectedGearSelectionTextView = (TextView) findViewById(R.id.expected_gear_selection);
+        mCurrentGearSelectionTextView = (TextView) findViewById(R.id.current_gear_selection);
+
+        CarPropertyManager carPropertyManager =
+            (CarPropertyManager) Car.createCar(this).getCarManager(Car.PROPERTY_SERVICE);
+
+        // TODO(b/138961351): Verify test works on manual transmission.
+        mSupportedGears = carPropertyManager.getPropertyList(new ArraySet<>(Arrays.asList(new
+                Integer[]{VehiclePropertyIds.GEAR_SELECTION}))).get(0).getConfigArray();
+
+        if(mSupportedGears.size() != 0){
+          Log.i(TAG, "New Expected Gear: " + VehicleGear.toString(mSupportedGears.get(0)));
+          mExpectedGearSelectionTextView.setText(VehicleGear.toString(mSupportedGears.get(0)));
+        } else {
+          Log.e(TAG, "No gears specified in the config array of GEAR_SELECTION property");
+          mExpectedGearSelectionTextView.setText("ERROR");
+        }
+
+        if(!carPropertyManager.registerCallback(mCarPropertyEventCallback,
+            VehiclePropertyIds.GEAR_SELECTION, CarPropertyManager.SENSOR_RATE_ONCHANGE)) {
+          Log.e(TAG, "Failed to register callback for GEAR_SELECTION with CarPropertyManager");
+        }
+    }
+
+    private final CarPropertyManager.CarPropertyEventCallback mCarPropertyEventCallback =
+      new CarPropertyManager.CarPropertyEventCallback() {
+        @Override
+        public void onChangeEvent(CarPropertyValue value) {
+            if(value.getStatus() != CarPropertyValue.STATUS_AVAILABLE) {
+                Log.e(TAG, "New CarPropertyValue's status is not available - propId: " +
+                    value.getPropertyId() + " status: " + value.getStatus());
+                return;
+            }
+            Integer newGearSelection = (Integer) value.getValue();
+            mCurrentGearSelectionTextView.setText(VehicleGear.toString(newGearSelection));
+            Log.i(TAG, "New Gear Selection: " + VehicleGear.toString(newGearSelection));
+
+            if (mSupportedGears.size() == 0) {
+                Log.e(TAG, "No gears specified in the config array of GEAR_SELECTION property");
+                return;
+            }
+
+            // Check to see if new gear matches the expected gear.
+            if(newGearSelection == mSupportedGears.get(mGearsAchievedCount)) {
+                mGearsAchievedCount++;
+                Log.i(TAG, "Matched gear: " + VehicleGear.toString(newGearSelection));
+                // Check to see if the test is finished.
+                if (mGearsAchievedCount >= mSupportedGears.size()) {
+                    mExpectedGearSelectionTextView.setText("Finished");
+                    getPassButton().setEnabled(true);
+                    Log.i(TAG, "Finished Test");
+                } else {
+                    // Test is not finished so update the expected gear.
+                    mExpectedGearSelectionTextView.setText(
+                        VehicleGear.toString(mSupportedGears.get(mGearsAchievedCount)));
+                    Log.i(TAG, "New Expected Gear: " + 
+                        VehicleGear.toString(mSupportedGears.get(mGearsAchievedCount)));
+                }
+            }
+        }
+
+        @Override
+        public void onErrorEvent(int propId, int zone) {
+            Log.e(TAG, "propId: " + propId + " zone: " + zone);
+        }
+      };
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/car/ParkingBrakeOnTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/car/ParkingBrakeOnTestActivity.java
new file mode 100644
index 0000000..c89e895
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/car/ParkingBrakeOnTestActivity.java
@@ -0,0 +1,111 @@
+/*
+ * 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.verifier.car;
+
+import android.car.Car;
+import android.car.hardware.CarPropertyConfig;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.CarPropertyManager;
+import android.car.VehicleAreaType;
+import android.car.VehiclePropertyIds;
+import android.os.Bundle;
+import android.widget.TextView;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.util.Arrays;
+import java.util.List;
+
+/** A CTS Verifier test case to verify PARKING_BRAKE_ON is implemented correctly.*/
+public class ParkingBrakeOnTestActivity extends PassFailButtons.Activity {
+    private static final String TAG = ParkingBrakeOnTestActivity.class.getSimpleName();
+    private static final int TOTAL_MATCHES_NEEDED_TO_FINISH = 2;
+    private Boolean mCurrentParkingBrakeOnValue;
+    private TextView mInstructionTextView;
+    private TextView mCurrentParkingBrakeOnValueTextView;
+    private int mTotalTimesNewValueMatchInstruction = 0;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Setup the UI.
+        setContentView(R.layout.parking_brake_on_test);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.parking_brake_on_test, R.string.parking_brake_on_test_desc, -1);
+        getPassButton().setEnabled(false);
+
+        mInstructionTextView = (TextView) findViewById(R.id.instruction);
+        mInstructionTextView.setText("Waiting to get first PARKING_BRAKE_ON callback");
+        mCurrentParkingBrakeOnValueTextView =
+            (TextView) findViewById(R.id.current_parking_brake_on_value);
+
+
+        CarPropertyManager carPropertyManager =
+            (CarPropertyManager) Car.createCar(this).getCarManager(Car.PROPERTY_SERVICE);
+
+        if(!carPropertyManager.registerCallback(mCarPropertyEventCallback,
+            VehiclePropertyIds.PARKING_BRAKE_ON, CarPropertyManager.SENSOR_RATE_ONCHANGE)) {
+            mInstructionTextView.setText("ERROR: Unable to register for PARKING_BRAKE_ON callback");
+            Log.e(TAG, "Failed to register callback for PARKING_BRAKE_ON with CarPropertyManager");
+        }
+    }
+
+    private final CarPropertyManager.CarPropertyEventCallback mCarPropertyEventCallback =
+      new CarPropertyManager.CarPropertyEventCallback() {
+        @Override
+        public void onChangeEvent(CarPropertyValue value) {
+            if(value.getStatus() != CarPropertyValue.STATUS_AVAILABLE) {
+                Log.e(TAG, "New CarPropertyValue's status is not available - propId: " +
+                    value.getPropertyId() + " status: " + value.getStatus());
+                return;
+            }
+
+            Boolean newValue = (Boolean) value.getValue();
+            Log.i(TAG, "New PARKING_BRAKE_ON value: " + newValue);
+
+            // On the first callback, mCurrentParkingBrakeOnValue will be null, so just save the
+            // current value. All other callbacks, check if the PARKING_BRAKE_ON value has switched.
+            // If switched, update the count.
+            if (mCurrentParkingBrakeOnValue != null &&
+                !mCurrentParkingBrakeOnValue.equals(newValue)) {
+                mTotalTimesNewValueMatchInstruction++;
+            }
+
+            mCurrentParkingBrakeOnValue = newValue;
+            mCurrentParkingBrakeOnValueTextView.setText(mCurrentParkingBrakeOnValue.toString());
+
+            // Check if the test is finished. If not finished, update the instructions.
+            if(mTotalTimesNewValueMatchInstruction >= TOTAL_MATCHES_NEEDED_TO_FINISH) {
+                mInstructionTextView.setText("Test Finished!");
+                getPassButton().setEnabled(true);
+            } else if(mCurrentParkingBrakeOnValue) {
+                mInstructionTextView.setText("Disengage the Parking Brake");
+            } else {
+                mInstructionTextView.setText("Engage the Parking Brake");
+            }
+        }
+
+        @Override
+        public void onErrorEvent(int propId, int zone) {
+            Log.e(TAG, "propId: " + propId + " zone: " + zone);
+        }
+      };
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AutomaticZenRuleStatusReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AutomaticZenRuleStatusReceiver.java
new file mode 100644
index 0000000..9194cf1
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AutomaticZenRuleStatusReceiver.java
@@ -0,0 +1,47 @@
+/*
+ * 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.verifier.notifications;
+
+import static android.app.NotificationManager.ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_UNKNOWN;
+import static android.app.NotificationManager.EXTRA_AUTOMATIC_ZEN_RULE_STATUS;
+
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.util.Log;
+
+public class AutomaticZenRuleStatusReceiver extends BroadcastReceiver {
+    private static final String TAG = "AZRReceiver";
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        SharedPreferences prefs = context.getSharedPreferences(
+                ConditionProviderVerifierActivity.PREFS, Context.MODE_PRIVATE);
+        SharedPreferences.Editor editor = prefs.edit();
+        if (ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED.equals(intent.getAction())) {
+            String id = intent.getStringExtra(
+                    NotificationManager.EXTRA_AUTOMATIC_ZEN_RULE_ID);
+            int status = intent.getIntExtra(EXTRA_AUTOMATIC_ZEN_RULE_STATUS,
+                    AUTOMATIC_RULE_STATUS_UNKNOWN);
+            Log.d(TAG, "Got broadcast for rule status change: " + id + " " + status);
+            editor.putInt(id, status);
+            editor.commit();
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BlockChangeReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BlockChangeReceiver.java
index 084e907..454b5d2 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BlockChangeReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BlockChangeReceiver.java
@@ -1,3 +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 com.android.cts.verifier.notifications;
 
 import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED;
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 3dc41e6..8760ef8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java
@@ -34,7 +34,6 @@
 import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import com.android.cts.verifier.PassFailButtons;
 import com.android.cts.verifier.R;
@@ -103,9 +102,14 @@
             runNextTestOrShowSummary();
         });
 
+        // Make sure they're enabled
+        mTests.add(new EnableBubbleTest());
+
         ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
-        if (!am.isLowRamDevice()) {
-            mTests.add(new EnableBubbleTest());
+        if (am.isLowRamDevice()) {
+            // Bubbles don't occur on low ram, instead they just show as notifs so test that
+            mTests.add(new LowRamBubbleTest());
+        } else {
             mTests.add(new SendBubbleTest());
             mTests.add(new SuppressNotifTest());
             mTests.add(new AddNotifTest());
@@ -115,10 +119,6 @@
             mTests.add(new DismissBubbleTest());
             mTests.add(new DismissNotificationTest());
             mTests.add(new AutoExpandBubbleTest());
-        } else {
-            Toast.makeText(getApplicationContext(),
-                    getResources().getString(R.string.bubbles_notification_no_bubbles_low_mem),
-                    Toast.LENGTH_LONG).show();
         }
 
         setPassFailButtonClickListeners();
@@ -462,6 +462,32 @@
         }
     }
 
+    private class LowRamBubbleTest extends BubblesTestStep {
+        @Override
+        public int getButtonText() {
+            return R.string.bubbles_notification_test_button_10;
+        }
+
+        @Override
+        public int getTestTitle() {
+            return R.string.bubbles_notification_test_title_10;
+        }
+
+        @Override
+        public int getTestDescription() {
+            return R.string.bubbles_notification_test_verify_10;
+        }
+
+        @Override
+        public void performTestAction() {
+            Notification.Builder builder =
+                    getBasicNotifBuilder("Bubble notification", "Low ram test");
+            builder.setBubbleMetadata(getBasicBubbleBuilder().build());
+
+            mNotificationManager.notify(NOTIFICATION_ID, builder.build());
+        }
+    }
+
     /** Creates a minimally filled out {@link android.app.Notification.BubbleMetadata.Builder} */
     private Notification.BubbleMetadata.Builder getBasicBubbleBuilder() {
         Context context = getApplicationContext();
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 1b14d2b..bad4639 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java
@@ -16,7 +16,12 @@
 
 package com.android.cts.verifier.notifications;
 
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DISABLED;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ENABLED;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_UNKNOWN;
 import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+import static android.provider.Settings.EXTRA_APP_PACKAGE;
 
 import android.app.ActivityManager;
 import android.app.AutomaticZenRule;
@@ -24,6 +29,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.net.Uri;
 import android.provider.Settings;
 import android.service.notification.ZenPolicy;
@@ -31,6 +37,7 @@
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.Button;
 
 import com.android.cts.verifier.R;
 
@@ -41,10 +48,14 @@
 
 public class ConditionProviderVerifierActivity extends InteractiveVerifierActivity
         implements Runnable {
+    private static final String TAG = "CPVerifier";
     protected static final String CP_PACKAGE = "com.android.cts.verifier";
     protected static final String CP_PATH = CP_PACKAGE +
             "/com.android.cts.verifier.notifications.MockConditionProvider";
 
+    protected static final String PREFS = "zen_prefs";
+    private static final String BROADCAST_RULE_NAME = "123";
+
     @Override
     protected int getTitleResource() {
         return R.string.cp_test;
@@ -73,6 +84,11 @@
             tests.add(new UpdateAutomaticZenRuleWithZenPolicyTest());
             tests.add(new GetAutomaticZenRuleTest());
             tests.add(new GetAutomaticZenRulesTest());
+            tests.add(new VerifyRulesIntent());
+            tests.add(new VerifyRulesAvailableToUsers());
+            tests.add(new ReceiveRuleDisableNoticeTest());
+            tests.add(new ReceiveRuleEnabledNoticeTest());
+            tests.add(new ReceiveRuleDeletedNoticeTest());
             tests.add(new SubscribeAutomaticZenRuleTest());
             tests.add(new DeleteAutomaticZenRuleTest());
             tests.add(new UnsubscribeAutomaticZenRuleTest());
@@ -626,6 +642,292 @@
         }
     }
 
+    protected class VerifyRulesIntent extends InteractiveTestCase {
+        @Override
+        protected View inflate(ViewGroup parent) {
+            return createSettingsItem(parent, R.string.cp_show_rules);
+        }
+
+        @Override
+        boolean autoStart() {
+            return true;
+        }
+
+        @Override
+        protected void test() {
+            Intent settings = new Intent(Settings.ACTION_CONDITION_PROVIDER_SETTINGS);
+            if (settings.resolveActivity(mPackageManager) == null) {
+                logFail("no settings activity");
+                status = FAIL;
+            } else {
+                if (buttonPressed) {
+                    status = PASS;
+                } else {
+                    status = RETEST_AFTER_LONG_DELAY;
+                }
+                next();
+            }
+        }
+
+        protected void tearDown() {
+            // wait for the service to start
+            delay();
+        }
+
+        @Override
+        protected Intent getIntent() {
+            return new Intent(Settings.ACTION_CONDITION_PROVIDER_SETTINGS);
+        }
+    }
+
+    protected class VerifyRulesAvailableToUsers extends InteractiveTestCase {
+        @Override
+        protected View inflate(ViewGroup parent) {
+            return createPassFailItem(parent, R.string.cp_show_rules_verification);
+        }
+
+        @Override
+        boolean autoStart() {
+            return true;
+        }
+
+        @Override
+        protected void test() {
+            status = WAIT_FOR_USER;
+            next();
+        }
+    }
+
+    /**
+     * Sends the user to settings to disable the rule. Waits to receive the broadcast that the rule
+     * was disabled, and confirms that the broadcast contains the correct extras.
+     */
+    protected class ReceiveRuleDisableNoticeTest extends InteractiveTestCase {
+        private final int EXPECTED_STATUS = AUTOMATIC_RULE_STATUS_DISABLED;
+        private int mRetries = 2;
+        private View mView;
+        private String mId;
+        @Override
+        protected View inflate(ViewGroup parent) {
+            mView = createNlsSettingsItem(parent, R.string.cp_disable_rule);
+            Button button = mView.findViewById(R.id.nls_action_button);
+            button.setEnabled(false);
+            return mView;
+        }
+
+        @Override
+        protected void setUp() {
+            status = READY;
+            // create enabled so it's ready to be disabled in app
+            AutomaticZenRule rule = new AutomaticZenRule(BROADCAST_RULE_NAME, null,
+                    new ComponentName(CP_PACKAGE,
+                            ConditionProviderVerifierActivity.this.getClass().getName()),
+                    Uri.EMPTY, null, INTERRUPTION_FILTER_PRIORITY, true);
+            mId = mNm.addAutomaticZenRule(rule);
+            Button button = mView.findViewById(R.id.nls_action_button);
+            button.setEnabled(true);
+        }
+
+        @Override
+        boolean autoStart() {
+            return true;
+        }
+
+        @Override
+        protected void test() {
+            SharedPreferences prefs = mContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
+
+            AutomaticZenRule rule = mNm.getAutomaticZenRule(mId);
+
+            if (!rule.isEnabled()) {
+                Log.d(TAG, "Check pref for broadcast " + prefs.contains(mId)
+                        + " " + prefs.getInt(mId, AUTOMATIC_RULE_STATUS_UNKNOWN));
+                if (prefs.contains(mId)
+                        && EXPECTED_STATUS == prefs.getInt(mId, AUTOMATIC_RULE_STATUS_UNKNOWN)) {
+                    status = PASS;
+                } else {
+                    if (mRetries > 0) {
+                        mRetries--;
+                        status = RETEST;
+                    } else {
+                        status = FAIL;
+                    }
+                }
+            } else {
+                Log.d(TAG, "Waiting for user");
+                // user hasn't jumped to settings yet
+                status = WAIT_FOR_USER;
+            }
+
+            next();
+        }
+
+        protected void tearDown() {
+            mNm.removeAutomaticZenRule(mId);
+            SharedPreferences prefs = mContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
+            prefs.edit().clear().commit();
+        }
+
+        @Override
+        protected Intent getIntent() {
+            return new Intent(Settings.ACTION_CONDITION_PROVIDER_SETTINGS)
+                    .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        }
+    }
+
+    /**
+     * Sends the user to settings to enable the rule. Waits to receive the broadcast that the rule
+     * was enabled, and confirms that the broadcast contains the correct extras.
+     */
+    protected class ReceiveRuleEnabledNoticeTest extends InteractiveTestCase {
+        private final int EXPECTED_STATUS = AUTOMATIC_RULE_STATUS_ENABLED;
+        private int mRetries = 2;
+        private View mView;
+        private String mId;
+        @Override
+        protected View inflate(ViewGroup parent) {
+            mView = createNlsSettingsItem(parent, R.string.cp_enable_rule);
+            Button button = mView.findViewById(R.id.nls_action_button);
+            button.setEnabled(false);
+            return mView;
+        }
+
+        @Override
+        protected void setUp() {
+            status = READY;
+            // create disabled so it's ready to be enabled in Settings
+            AutomaticZenRule rule = new AutomaticZenRule(BROADCAST_RULE_NAME, null,
+                    new ComponentName(CP_PACKAGE,
+                            ConditionProviderVerifierActivity.this.getClass().getName()),
+                    Uri.EMPTY, null, INTERRUPTION_FILTER_PRIORITY, false);
+            mId = mNm.addAutomaticZenRule(rule);
+            Button button = mView.findViewById(R.id.nls_action_button);
+            button.setEnabled(true);
+        }
+
+        @Override
+        boolean autoStart() {
+            return true;
+        }
+
+        @Override
+        protected void test() {
+            SharedPreferences prefs = mContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
+
+            AutomaticZenRule rule = mNm.getAutomaticZenRule(mId);
+
+            if (rule.isEnabled()) {
+                Log.d(TAG, "Check pref for broadcast " + prefs.contains(mId)
+                        + " " + prefs.getInt(mId, AUTOMATIC_RULE_STATUS_UNKNOWN));
+                if (prefs.contains(mId)
+                        && EXPECTED_STATUS == prefs.getInt(mId, AUTOMATIC_RULE_STATUS_UNKNOWN)) {
+                    status = PASS;
+                } else {
+                    if (mRetries > 0) {
+                        mRetries--;
+                        status = RETEST;
+                    } else {
+                        status = FAIL;
+                    }
+                }
+            } else {
+                Log.d(TAG, "Waiting for user");
+                // user hasn't jumped to settings yet
+                status = WAIT_FOR_USER;
+            }
+
+            next();
+        }
+
+        protected void tearDown() {
+            mNm.removeAutomaticZenRule(mId);
+            SharedPreferences prefs = mContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
+            prefs.edit().clear().commit();
+        }
+
+        @Override
+        protected Intent getIntent() {
+            return new Intent(Settings.ACTION_CONDITION_PROVIDER_SETTINGS)
+                    .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        }
+    }
+
+    /**
+     * Sends the user to settings to delete the rule. Waits to receive the broadcast that the rule
+     * was deleted, and confirms that the broadcast contains the correct extras.
+     */
+    protected class ReceiveRuleDeletedNoticeTest extends InteractiveTestCase {
+        private final int EXPECTED_STATUS = AUTOMATIC_RULE_STATUS_REMOVED;
+        private int mRetries = 2;
+        private View mView;
+        private String mId;
+        @Override
+        protected View inflate(ViewGroup parent) {
+            mView = createNlsSettingsItem(parent, R.string.cp_delete_rule_broadcast);
+            Button button = mView.findViewById(R.id.nls_action_button);
+            button.setEnabled(false);
+            return mView;
+        }
+
+        @Override
+        protected void setUp() {
+            status = READY;
+            AutomaticZenRule rule = new AutomaticZenRule(BROADCAST_RULE_NAME, null,
+                    new ComponentName(CP_PACKAGE,
+                            ConditionProviderVerifierActivity.this.getClass().getName()),
+                    Uri.EMPTY, null, INTERRUPTION_FILTER_PRIORITY, true);
+            mId = mNm.addAutomaticZenRule(rule);
+            Button button = mView.findViewById(R.id.nls_action_button);
+            button.setEnabled(true);
+        }
+
+        @Override
+        boolean autoStart() {
+            return true;
+        }
+
+        @Override
+        protected void test() {
+            SharedPreferences prefs = mContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
+
+            AutomaticZenRule rule = mNm.getAutomaticZenRule(mId);
+
+            if (rule == null) {
+                Log.d(TAG, "Check pref for broadcast " + prefs.contains(mId)
+                        + " " + prefs.getInt(mId, AUTOMATIC_RULE_STATUS_UNKNOWN));
+                if (prefs.contains(mId)
+                        && EXPECTED_STATUS == prefs.getInt(mId, AUTOMATIC_RULE_STATUS_UNKNOWN)) {
+                    status = PASS;
+                } else {
+                    if (mRetries > 0) {
+                        mRetries--;
+                        status = RETEST;
+                    } else {
+                        status = FAIL;
+                    }
+                }
+            } else {
+                Log.d(TAG, "Waiting for user");
+                // user hasn't jumped to settings yet
+                status = WAIT_FOR_USER;
+            }
+
+            next();
+        }
+
+        protected void tearDown() {
+            mNm.removeAutomaticZenRule(mId);
+            SharedPreferences prefs = mContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
+            prefs.edit().clear().commit();
+        }
+
+        @Override
+        protected Intent getIntent() {
+            return new Intent(Settings.ACTION_CONDITION_PROVIDER_SETTINGS)
+                    .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        }
+    }
+
     private class DeleteAutomaticZenRuleTest extends InteractiveTestCase {
         private String id1 = null;
         private String id2 = null;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
index f1f08ff..296df5d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
@@ -98,6 +98,7 @@
         protected int status;
         private View view;
         protected long delayTime = 3000;
+        boolean buttonPressed;
 
         protected abstract View inflate(ViewGroup parent);
         View getView(ViewGroup parent) {
@@ -247,7 +248,7 @@
     protected View createUserItem(ViewGroup parent, int actionId, int messageId,
             Object... messageFormatArgs) {
         View item = mInflater.inflate(R.layout.nls_item, parent, false);
-        TextView instructions = (TextView) item.findViewById(R.id.nls_instructions);
+        TextView instructions = item.findViewById(R.id.nls_instructions);
         instructions.setText(getString(messageId, messageFormatArgs));
         Button button = (Button) item.findViewById(R.id.nls_action_button);
         button.setText(actionId);
@@ -255,15 +256,22 @@
         return item;
     }
 
-    protected View  createAutoItem(ViewGroup parent, int stringId) {
+    protected View createAutoItem(ViewGroup parent, int stringId) {
         View item = mInflater.inflate(R.layout.nls_item, parent, false);
-        TextView instructions = (TextView) item.findViewById(R.id.nls_instructions);
+        TextView instructions = item.findViewById(R.id.nls_instructions);
         instructions.setText(stringId);
         View button = item.findViewById(R.id.nls_action_button);
         button.setVisibility(View.GONE);
         return item;
     }
 
+    protected View createPassFailItem(ViewGroup parent, int stringId) {
+        View item = mInflater.inflate(R.layout.iva_pass_fail_item, parent, false);
+        TextView instructions = item.findViewById(R.id.nls_instructions);
+        instructions.setText(stringId);
+        return item;
+    }
+
     // Test management
 
     abstract protected List<InteractiveTestCase> createTestItems();
@@ -381,10 +389,25 @@
             }
             if (mCurrentTest != null) {
                 mCurrentTest.mUserVerified = true;
+                mCurrentTest.buttonPressed = true;
             }
         }
     }
 
+    public void actionPassed(View v) {
+        if (mCurrentTest != null) {
+            mCurrentTest.mUserVerified = true;
+            mCurrentTest.status = PASS;
+            next();
+        }
+    }
+
+    public void actionFailed(View v) {
+        if (mCurrentTest != null) {
+            mCurrentTest.setFailed();
+        }
+    }
+
     // Utilities
 
     protected PendingIntent makeIntent(int code, String tag) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
index a6e5f98..180306e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
@@ -18,78 +18,40 @@
 import android.app.ActivityManager;
 import android.os.Bundle;
 import android.util.Log;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.LinearLayout;
 import android.widget.TextView;
 
-import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.OrderedTestActivity;
 import com.android.cts.verifier.R;
 
-public class ScreenPinningTestActivity extends PassFailButtons.Activity {
+public class ScreenPinningTestActivity extends OrderedTestActivity {
 
     private static final String TAG = "ScreenPinningTestActivity";
-    private static final String KEY_CURRENT_TEST = "keyCurrentTest";
     private static final long TASK_MODE_CHECK_DELAY = 200;
     private static final int MAX_TASK_MODE_CHECK_COUNT = 5;
 
-    private Test[] mTests;
-    private int mTestIndex;
-
     private ActivityManager mActivityManager;
-    private Button mNextButton;
-    private LinearLayout mInstructions;
 
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        setContentView(R.layout.screen_pinning);
-        setPassFailButtonClickListeners();
-
-        mTests = new Test[] {
-            // Verify not already pinned.
-            mCheckStartedUnpinned,
-            // Enter pinning, verify pinned, try leaving and have the user exit.
-            mCheckStartPinning,
-            mCheckIsPinned,
-            mCheckTryLeave,
-            mCheckUnpin,
-            // Enter pinning, verify pinned, and use APIs to exit.
-            mCheckStartPinning,
-            mCheckIsPinned,
-            mCheckUnpinFromCode,
-            // All done.
-            mDone,
-        };
 
         mActivityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
-        mInstructions = (LinearLayout) findViewById(R.id.instructions_list);
-
-        mNextButton = (Button) findViewById(R.id.next_button);
-        mNextButton.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                if ((mTestIndex >= 0) && (mTestIndex < mTests.length)) {
-                    mTests[mTestIndex].onNextClick();
-                }
-            }
-        });
-
-        // Don't allow pass until all tests complete.
-        findViewById(R.id.pass_button).setVisibility(View.GONE);
-
-        // Figure out if we are in a test or starting from the beginning.
-        if (savedInstanceState != null && savedInstanceState.containsKey(KEY_CURRENT_TEST)) {
-            mTestIndex = savedInstanceState.getInt(KEY_CURRENT_TEST);
-        } else {
-            mTestIndex = 0;
-        }
-        mTests[mTestIndex].run();
-    };
+    }
 
     @Override
-    protected void onSaveInstanceState(Bundle outState) {
-        outState.putInt(KEY_CURRENT_TEST, mTestIndex);
+    protected Test[] getTests() {
+        return new Test[]{
+                // Verify not already pinned.
+                mCheckStartedUnpinned,
+                // Enter pinning, verify pinned, try leaving and have the user exit.
+                mCheckStartPinning,
+                mCheckIsPinned,
+                mCheckTryLeave,
+                mCheckUnpin,
+                // Enter pinning, verify pinned, and use APIs to exit.
+                mCheckStartPinning,
+                mCheckIsPinned,
+                mCheckUnpinFromCode
+        };
     }
 
     @Override
@@ -98,27 +60,8 @@
         // Users can still leave by pressing fail (or when done the pass) button.
     }
 
-    private void show(int id) {
-        TextView tv = new TextView(this);
-        tv.setPadding(10, 10, 10, 30);
-        tv.setText(id);
-        mInstructions.removeAllViews();
-        mInstructions.addView(tv);
-    }
-
-    private void succeed() {
-        runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mTestIndex++;
-                if (mTestIndex < mTests.length) {
-                    mTests[mTestIndex].run();
-                }
-            }
-        });
-    }
-
-    private void error(int errorId) {
+    @Override
+    protected void error(int errorId) {
         error(errorId, new Throwable());
     }
 
@@ -128,10 +71,8 @@
             public void run() {
                 String error = getString(errorId);
                 Log.d(TAG, error, cause);
-                // No more instructions needed.
-                findViewById(R.id.instructions_group).setVisibility(View.GONE);
 
-                ((TextView) findViewById(R.id.error_text)).setText(error);
+                ((TextView) findViewById(R.id.txt_instruction)).setText(error);
             }
         });
     }
@@ -211,40 +152,6 @@
                     error(R.string.error_screen_pinning_couldnt_exit);
                 }
             }
-        };
+        }
     };
-
-    private final Test mDone = new Test(R.string.screen_pinning_done) {
-        @Override
-        protected void run() {
-            super.run();
-            // On test completion, hide "next" button, and show "pass" button
-            // instead.
-            mNextButton.setVisibility(View.GONE);
-            findViewById(R.id.pass_button).setVisibility(View.VISIBLE);
-        };
-    };
-
-    private abstract class Test {
-        private final int mResId;
-
-        public Test(int showId) {
-            mResId = showId;
-        }
-
-        protected void run() {
-            showText();
-        }
-
-        public void showText() {
-            if (mResId == 0) {
-                return;
-            }
-            show(mResId);
-        }
-
-        protected void onNextClick() {
-        }
-    }
-
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/security/ProtectedConfirmationTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/security/ProtectedConfirmationTest.java
new file mode 100644
index 0000000..62c7772
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/security/ProtectedConfirmationTest.java
@@ -0,0 +1,254 @@
+/*
+ * 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.verifier.security;
+
+import android.content.pm.PackageManager;
+import android.icu.util.Calendar;
+import android.os.Bundle;
+import android.security.ConfirmationAlreadyPresentingException;
+import android.security.ConfirmationCallback;
+import android.security.ConfirmationNotAvailableException;
+import android.security.ConfirmationPrompt;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.Toast;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.UnrecoverableEntryException;
+import java.security.cert.CertificateException;
+import java.util.Date;
+
+public class ProtectedConfirmationTest extends PassFailButtons.Activity {
+
+    /**
+     * Alias for our key in the Android Key Store.
+     */
+    private static final String KEY_NAME = "my_confirmation_key";
+    private boolean teeTestSuccess = false;
+    private boolean strongboxTestSuccess = false;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.sec_protected_confirmation_main);
+        setPassFailButtonClickListeners();
+
+        boolean protectedConfirmationSupported =
+                ConfirmationPrompt.isSupported(getApplicationContext());
+        if (protectedConfirmationSupported) {
+            setInfoResources(R.string.sec_protected_confirmation_test,
+                    R.string.sec_protected_confirmation_test_info, -1);
+            getPassButton().setEnabled(false);
+        } else {
+            setInfoResources(R.string.sec_protected_confirmation_not_supported_title,
+                    R.string.sec_protected_confirmation_not_supported_info, -1);
+            getPassButton().setEnabled(true);
+            return;
+        }
+        boolean hasStrongbox = this.getPackageManager()
+                .hasSystemFeature(PackageManager.FEATURE_STRONGBOX_KEYSTORE);
+
+        findViewById(R.id.sec_protected_confirmation_tee_test_success)
+                .setVisibility(View.INVISIBLE);
+        findViewById(R.id.sec_protected_confirmation_strongbox_test_success)
+                .setVisibility(View.INVISIBLE);
+        Button startTestButton = (Button) findViewById(R.id.sec_start_test_button);
+        startTestButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                showToast("Test running...");
+                v.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        createKey(false /* useStrongbox */);
+                        if (trySign(getString(R.string.sec_protected_confirmation_message)
+                                .getBytes())) {
+                            showToast("Test failed. Key could sign without confirmation.");
+                        } else {
+                            showConfirmationPrompt(
+                                    getString(R.string.sec_protected_confirmation_message),
+                                    false  /* useStrongbox */);
+                        }
+                    }
+                });
+            }
+
+        });
+
+        Button startStrongboxTestButton =
+                (Button) findViewById(R.id.sec_start_test_strongbox_button);
+        if (hasStrongbox) {
+            startStrongboxTestButton.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    showToast("Test running...");
+                    v.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            createKey(true /* useStrongbox */);
+                            if (trySign(getString(R.string.sec_protected_confirmation_message)
+                                    .getBytes())) {
+                                showToast("Test failed. Key could sign without confirmation.");
+                            } else {
+                                showConfirmationPrompt(
+                                        getString(R.string.sec_protected_confirmation_message),
+                                                  true /* useStrongbox */);
+                            }
+                        }
+                    });
+                }
+
+            });
+        } else {
+            startStrongboxTestButton.setVisibility(View.GONE);
+            // since strongbox is available we mark the strongbox test as passed so that the tee
+            // test alone can make the test pass.
+            strongboxTestSuccess = true;
+        }
+
+    }
+
+    /**
+     * Creates an asymmetric signing key in AndroidKeyStore which can only be used for signing
+     * user confirmed messages.
+     */
+    private void createKey(boolean useStrongbox) {
+        Calendar calendar = Calendar.getInstance();
+        Date validityStart = calendar.getTime();
+        calendar.add(Calendar.YEAR, 1);
+        Date validityEnd = calendar.getTime();
+        try {
+            KeyPairGenerator kpg = KeyPairGenerator.getInstance(
+                    KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
+            KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(
+                    KEY_NAME,
+                    KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY);
+            builder.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512);
+            builder.setAttestationChallenge("CtsVerifierTest".getBytes());
+            builder.setUserConfirmationRequired(true);
+            builder.setIsStrongBoxBacked(useStrongbox);
+            builder.setKeyValidityStart(validityStart);
+            builder.setKeyValidityEnd(validityEnd);
+            kpg.initialize(builder.build());
+            kpg.generateKeyPair();
+        } catch (NoSuchAlgorithmException | NoSuchProviderException |
+                 InvalidAlgorithmParameterException e) {
+            throw new RuntimeException("Failed to create confirmation key", e);
+        }
+    }
+
+    private boolean trySign(byte[] dataThatWasConfirmed) {
+        try {
+            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+            keyStore.load(null);
+            KeyStore.Entry key = keyStore.getEntry(KEY_NAME, null);
+            Signature s = Signature.getInstance("SHA256withECDSA");
+            s.initSign(((KeyStore.PrivateKeyEntry) key).getPrivateKey());
+            s.update(dataThatWasConfirmed);
+            s.sign();
+        } catch (CertificateException | KeyStoreException | IOException | NoSuchAlgorithmException |
+                UnrecoverableEntryException | InvalidKeyException e) {
+            throw new RuntimeException("Failed to load confirmation key", e);
+        } catch (SignatureException e) {
+            return false;
+        }
+        return true;
+    }
+
+    private void showConfirmationPrompt(String confirmationMessage, boolean useStrongbox) {
+        ConfirmationPrompt.Builder builder = new ConfirmationPrompt.Builder(this);
+        builder.setPromptText(confirmationMessage);
+        builder.setExtraData(new byte[]{0x1, 0x02, 0x03});
+        ConfirmationPrompt prompt = builder.build();
+        try {
+            prompt.presentPrompt(getMainExecutor(),
+                    new ConfirmationCallback() {
+                        @Override
+                        public void onConfirmed(byte[] dataThatWasConfirmed) {
+                            super.onConfirmed(dataThatWasConfirmed);
+                            if (trySign(dataThatWasConfirmed)) {
+                                markTestSuccess(useStrongbox);
+                            } else {
+                                showToast("Failed to sign confirmed message");
+                            }
+                        }
+
+                        @Override
+                        public void onDismissed() {
+                            super.onDismissed();
+                            showToast("User dismissed the dialog.");
+                        }
+
+                        @Override
+                        public void onCanceled() {
+                            super.onCanceled();
+                            showToast("Confirmation dialog was canceled.");
+                        }
+
+                        @Override
+                        public void onError(Throwable e) {
+                            super.onError(e);
+                            throw new RuntimeException("Confirmation Callback encountered an error",
+                                                       e);
+                        }
+                    });
+        } catch (ConfirmationAlreadyPresentingException | ConfirmationNotAvailableException e) {
+            throw new RuntimeException("Error trying to present the confirmation prompt", e);
+        }
+    }
+
+    private void showToast(String message) {
+        Toast.makeText(this, message, Toast.LENGTH_LONG)
+                .show();
+    }
+
+    private void markTestSuccess(boolean strongbox) {
+        if (strongbox) {
+            if (!strongboxTestSuccess) {
+                findViewById(R.id.sec_protected_confirmation_strongbox_test_success)
+                        .setVisibility(View.VISIBLE);
+            }
+            strongboxTestSuccess = true;
+        } else {
+            if (!teeTestSuccess) {
+                findViewById(R.id.sec_protected_confirmation_tee_test_success)
+                        .setVisibility(View.VISIBLE);
+            }
+            teeTestSuccess = true;
+        }
+        if (strongboxTestSuccess && teeTestSuccess) {
+            showToast("Test passed.");
+            getPassButton().setEnabled(true);
+        }
+    }
+}
+
diff --git a/apps/PermissionApp/AndroidManifest.xml b/apps/PermissionApp/AndroidManifest.xml
index be43556..4efd277 100644
--- a/apps/PermissionApp/AndroidManifest.xml
+++ b/apps/PermissionApp/AndroidManifest.xml
@@ -38,6 +38,7 @@
     <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.WRITE_CONTACTS"/>
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
 
     <application android:label="CtsPermissionApp"
diff --git a/common/device-side/test-app/Android.bp b/common/device-side/test-app/Android.bp
index 14e711a..74f549d 100644
--- a/common/device-side/test-app/Android.bp
+++ b/common/device-side/test-app/Android.bp
@@ -30,7 +30,7 @@
         "compatibility-common-util-devicesidelib",
         "compatibility-device-info-tests",
         "compatibility-device-info",
-        "compatibility-device-util-tests",
+        "compatibility-device-util-tests-axt",
         "compatibility-device-util-axt",
     ],
 
diff --git a/common/device-side/util-axt/Android.bp b/common/device-side/util-axt/Android.bp
index fe2b1b7..169184a 100644
--- a/common/device-side/util-axt/Android.bp
+++ b/common/device-side/util-axt/Android.bp
@@ -12,14 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// temporary compatibility-device-util variant that brings in androidx.test transitively, instead
-// of android.support.test target. Will be removed after androidx.test CTS conversion is complete.
 java_library_static {
     name: "compatibility-device-util-axt",
     sdk_version: "test_current",
 
     srcs: [
         "src/**/*.java",
+        "src/**/*.kt",
         "src/**/*.aidl",
     ],
 
diff --git a/common/device-side/util-axt/OWNERS b/common/device-side/util-axt/OWNERS
index b61fd53..55fc077 100644
--- a/common/device-side/util-axt/OWNERS
+++ b/common/device-side/util-axt/OWNERS
@@ -1,2 +1,2 @@
 
-per-file Android.bp=guangzhu@google.com, fdeng@google.com, moonk@google.com, jdesprez@google.com, aaronholden@google.com, yuji@google.com, nickrose@google.com, felipeal@google.com
+per-file Android.bp=guangzhu@google.com, fdeng@google.com, moonk@google.com, jdesprez@google.com, aaronholden@google.com, yuji@google.com, nickrose@google.com, felipeal@google.com, eugenesusla@google.com, svetoslavganov@google.com
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/ExceptionUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/ExceptionUtils.java
new file mode 100644
index 0000000..0899966
--- /dev/null
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/ExceptionUtils.java
@@ -0,0 +1,102 @@
+/*
+ * 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.compatibility.common.util;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.function.Function;
+
+/**
+ * Utilities to deal with exceptions
+ */
+public class ExceptionUtils {
+    private ExceptionUtils() {}
+
+    /**
+     * Rethrow a given exception, optionally wrapping it in a {@link RuntimeException}
+     */
+    public static RuntimeException propagate(@NonNull Throwable t) {
+        if (t == null) {
+            throw new NullPointerException();
+        }
+        propagateIfInstanceOf(t, Error.class);
+        propagateIfInstanceOf(t, RuntimeException.class);
+        throw new RuntimeException(t);
+    }
+
+    /**
+     * Rethrow a given exception, if it's of type {@code E}
+     */
+    public static <E extends Throwable> void propagateIfInstanceOf(
+            @Nullable Throwable t, Class<E> c) throws E {
+        if (t != null && c.isInstance(t)) {
+            throw c.cast(t);
+        }
+    }
+
+    /**
+     * Gets the root {@link Throwable#getCause() cause} of {@code t}
+     */
+    public static @NonNull Throwable getRootCause(@NonNull Throwable t) {
+        while (t.getCause() != null) t = t.getCause();
+        return t;
+    }
+
+    /**
+     * Appends {@code cause} at the end of the causal chain of {@code t}
+     *
+     * @return {@code t} for convenience
+     */
+    public static @NonNull Throwable appendCause(@NonNull Throwable t, @Nullable Throwable cause) {
+        if (cause != null) {
+            getRootCause(t).initCause(cause);
+        }
+        return t;
+    }
+
+    /**
+     * Runs the given {@code action}, and if any exceptions are thrown in the process, applies
+     * given {@code exceptionTransformer}, rethrowing the result.
+     */
+    public static <R> R wrappingExceptions(
+            Function<Throwable, Throwable> exceptionTransformer, ThrowingSupplier<R> action) {
+        try {
+            return action.get();
+        } catch (Throwable t) {
+            Throwable transformed;
+            try {
+                transformed = exceptionTransformer.apply(t);
+            } catch (Throwable t2) {
+                transformed = new RuntimeException("Failed to apply exception transformation",
+                        ExceptionUtils.appendCause(t2, t));
+            }
+            throw ExceptionUtils.propagate(transformed);
+        }
+    }
+
+    /**
+     * @see #wrappingExceptions(Function, ThrowingSupplier)
+     */
+    public static void wrappingExceptions(
+            Function<Throwable, Throwable> exceptionTransformer, ThrowingRunnable action) {
+        wrappingExceptions(exceptionTransformer, () -> {
+            action.run();
+            return null;
+        });
+    }
+}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/FileUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/FileUtils.java
index ceada01..5530ff3 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/FileUtils.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/FileUtils.java
@@ -53,10 +53,6 @@
     public static final int S_IWOTH = 00002;
     public static final int S_IXOTH = 00001;
 
-    static {
-        System.loadLibrary("cts_jni");
-    }
-
     public static class FileStatus {
 
         public int dev;
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/FutureResultActivity.kt b/common/device-side/util-axt/src/com/android/compatibility/common/util/FutureResultActivity.kt
new file mode 100644
index 0000000..49397a5
--- /dev/null
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/FutureResultActivity.kt
@@ -0,0 +1,58 @@
+/*
+ * 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 com.android.compatibility.common.util
+
+import android.app.Activity
+import android.content.Intent
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.atomic.AtomicInteger
+
+/**
+ * An [Activity] that exposes a special [startActivityForResult],
+ * returning future resultCode as a [CompletableFuture]
+ */
+class FutureResultActivity : Activity() {
+
+    companion object {
+
+        /** requestCode -> Future<resultCode> */
+        private val requests = ConcurrentHashMap<Int, CompletableFuture<Int>>()
+        private val nextRequestCode = AtomicInteger(0)
+
+        fun doAndAwaitStart(act: () -> Unit): CompletableFuture<Int> {
+            val requestCode = nextRequestCode.get()
+            act()
+            PollingCheck.waitFor(60_000) {
+                nextRequestCode.get() >= requestCode + 1
+            }
+            return requests[requestCode]!!
+        }
+    }
+
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        requests[requestCode]!!.complete(resultCode)
+    }
+
+    fun startActivityForResult(intent: Intent): CompletableFuture<Int> {
+        val requestCode = nextRequestCode.getAndIncrement()
+        val future = CompletableFuture<Int>()
+        requests[requestCode] = future
+        startActivityForResult(intent, requestCode)
+        return future
+    }
+}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/ProtoUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/ProtoUtils.java
new file mode 100644
index 0000000..ac99446
--- /dev/null
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/ProtoUtils.java
@@ -0,0 +1,39 @@
+/*
+ * 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.compatibility.common.util;
+
+import android.app.UiAutomation;
+
+/**
+ * Utility class for proto dumps.
+ */
+public class ProtoUtils {
+    public static final String DUMPSYS_JOB_SCHEDULER = "dumpsys jobscheduler --proto";
+
+    /**
+     * Call onto the device with an adb shell command and get the results of
+     * that as a proto of the given type.
+     *
+     * @param clazz  A protobuf message class. e.g. MyProto
+     * @param command The adb shell command to run. e.g. "dumpsys jobscheduler --proto"
+     */
+    public static <T> T getProto(UiAutomation automation, Class<T> clazz, String command)
+            throws Exception {
+        return clazz.cast(clazz.getDeclaredMethod("parseFrom", byte[].class)
+                .invoke(null, SystemUtil.runShellCommandByteOutput(automation, command)));
+    }
+}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/SystemUtil.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/SystemUtil.java
index 16fce10..d63b745 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/SystemUtil.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/SystemUtil.java
@@ -31,6 +31,7 @@
 import androidx.annotation.NonNull;
 import androidx.test.InstrumentationRegistry;
 
+import java.io.ByteArrayOutputStream;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.util.concurrent.Callable;
@@ -81,21 +82,30 @@
      */
     public static String runShellCommand(UiAutomation automation, String cmd)
             throws IOException {
+        return new String(runShellCommandByteOutput(automation, cmd));
+    }
+
+    /**
+     * Executes a shell command using shell user identity, and return the standard output as a byte
+     * array
+     * <p>Note: calling this function requires API level 21 or above
+     *
+     * @param automation {@link UiAutomation} instance, obtained from a test running in
+     *                   instrumentation framework
+     * @param cmd        the command to run
+     * @return the standard output of the command as a byte array
+     */
+    static byte[] runShellCommandByteOutput(UiAutomation automation, String cmd)
+            throws IOException {
         Log.v(TAG, "Running command: " + cmd);
         if (cmd.startsWith("pm grant ") || cmd.startsWith("pm revoke ")) {
             throw new UnsupportedOperationException("Use UiAutomation.grantRuntimePermission() "
                     + "or revokeRuntimePermission() directly, which are more robust.");
         }
         ParcelFileDescriptor pfd = automation.executeShellCommand(cmd);
-        byte[] buf = new byte[512];
-        int bytesRead;
-        FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
-        StringBuffer stdout = new StringBuffer();
-        while ((bytesRead = fis.read(buf)) != -1) {
-            stdout.append(new String(buf, 0, bytesRead));
+        try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+            return FileUtils.readInputStreamFully(fis);
         }
-        fis.close();
-        return stdout.toString();
     }
 
     /**
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ThrowingRunnable.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/ThrowingSupplier.java
similarity index 65%
copy from common/device-side/util/src/com/android/compatibility/common/util/ThrowingRunnable.java
copy to common/device-side/util-axt/src/com/android/compatibility/common/util/ThrowingSupplier.java
index 0588cff..f1e0006 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/ThrowingRunnable.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/ThrowingSupplier.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -15,12 +15,16 @@
  */
 package com.android.compatibility.common.util;
 
+import java.util.function.Supplier;
+
 /**
- * Similar to {@link Runnable} but has {@code throws Exception}.
+ * Similar to {@link Supplier} but has {@code throws Exception}.
+ *
+ * @param <T> type of the value produced
  */
-public interface ThrowingRunnable {
+public interface ThrowingSupplier<T> {
     /**
-     * Similar to {@link Runnable#run} but has {@code throws Exception}.
+     * Similar to {@link Supplier#get} but has {@code throws Exception}.
      */
-    void run() throws Exception;
+    T get() throws Exception;
 }
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils.java
new file mode 100644
index 0000000..f516459
--- /dev/null
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils.java
@@ -0,0 +1,65 @@
+package com.android.compatibility.common.util;
+/*
+ * 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.
+ */
+
+
+import static org.junit.Assert.assertNotNull;
+
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.Direction;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+
+import androidx.test.InstrumentationRegistry;
+
+public class UiAutomatorUtils {
+    private UiAutomatorUtils() {}
+
+    public static UiDevice getUiDevice() {
+        return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+    }
+
+    public static UiObject2 waitFindObject(BySelector selector) {
+        return waitFindObject(selector, 100_000);
+    }
+
+    public static UiObject2 waitFindObject(BySelector selector, long timeoutMs) {
+        final UiObject2 view = waitFindObjectOrNull(selector, timeoutMs);
+        ExceptionUtils.wrappingExceptions(UiDumpUtils::wrapWithUiDump, () -> {
+            assertNotNull("View not found after waiting for " + timeoutMs + "ms: " + selector,
+                    view);
+        });
+        return view;
+    }
+
+    public static UiObject2 waitFindObjectOrNull(BySelector selector, long timeoutMs) {
+        UiObject2 view = null;
+        long start = System.currentTimeMillis();
+        while (view == null && start + timeoutMs > System.currentTimeMillis()) {
+            view = getUiDevice().wait(Until.findObject(selector), timeoutMs / 10);
+
+            if (view == null) {
+                UiObject2 scrollable = getUiDevice().findObject(By.scrollable(true));
+                if (scrollable != null) {
+                    scrollable.scroll(Direction.DOWN, 1);
+                }
+            }
+        }
+        return view;
+    }
+}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/UiDumpUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/UiDumpUtils.java
new file mode 100644
index 0000000..a36c1eb
--- /dev/null
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/UiDumpUtils.java
@@ -0,0 +1,566 @@
+/*
+ * 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.compatibility.common.util;
+
+import static android.text.TextUtils.isEmpty;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.view.accessibility.AccessibilityWindowInfo;
+
+import androidx.test.InstrumentationRegistry;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.function.BiPredicate;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.ToIntFunction;
+import java.util.stream.Stream;
+
+/**
+ * Utilities to dump the view hierrarchy as an indented tree
+ *
+ * @see #dumpNodes(AccessibilityNodeInfo, StringBuilder)
+ * @see #wrapWithUiDump(Throwable)
+ */
+@SuppressWarnings({"PointlessBitwiseExpression"})
+public class UiDumpUtils {
+    private UiDumpUtils() {}
+
+    private static final boolean CONCISE = false;
+    private static final boolean SHOW_ACTIONS = false;
+    private static final boolean IGNORE_INVISIBLE = false;
+
+    private static final int IGNORED_ACTIONS = 0
+            | AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS
+            | AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS
+            | AccessibilityNodeInfo.ACTION_FOCUS
+            | AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+            | AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+            | AccessibilityNodeInfo.ACTION_SELECT
+            | AccessibilityNodeInfo.ACTION_SET_SELECTION
+            | AccessibilityNodeInfo.ACTION_CLEAR_SELECTION
+            | AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT
+            | AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT
+            ;
+
+    private static final int SPECIALLY_HANDLED_ACTIONS = 0
+            | AccessibilityNodeInfo.ACTION_CLICK
+            | AccessibilityNodeInfo.ACTION_LONG_CLICK
+            | AccessibilityNodeInfo.ACTION_EXPAND
+            | AccessibilityNodeInfo.ACTION_COLLAPSE
+            | AccessibilityNodeInfo.ACTION_FOCUS
+            | AccessibilityNodeInfo.ACTION_CLEAR_FOCUS
+            | AccessibilityNodeInfo.ACTION_SCROLL_FORWARD
+            | AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD
+            | AccessibilityNodeInfo.ACTION_SET_TEXT
+            ;
+
+    /** name -> typical_value */
+    private static Map<String, Boolean> sNodeFlags = new LinkedHashMap<>();
+    static {
+        sNodeFlags.put("focused", false);
+        sNodeFlags.put("selected", false);
+        sNodeFlags.put("contextClickable", false);
+        sNodeFlags.put("dismissable", false);
+        sNodeFlags.put("enabled", true);
+        sNodeFlags.put("password", false);
+        sNodeFlags.put("visibleToUser", true);
+        sNodeFlags.put("contentInvalid", false);
+        sNodeFlags.put("heading", false);
+        sNodeFlags.put("showingHintText", false);
+
+        // Less important flags below
+        // Too spammy to report all, but can uncomment what's necessary
+
+//        sNodeFlags.put("focusable", true);
+//        sNodeFlags.put("accessibilityFocused", false);
+//        sNodeFlags.put("screenReaderFocusable", true);
+//        sNodeFlags.put("clickable", false);
+//        sNodeFlags.put("longClickable", false);
+//        sNodeFlags.put("checkable", false);
+//        sNodeFlags.put("checked", false);
+//        sNodeFlags.put("editable", false);
+//        sNodeFlags.put("scrollable", false);
+//        sNodeFlags.put("importantForAccessibility", true);
+//        sNodeFlags.put("multiLine", false);
+    }
+
+    /** action -> pictogram */
+    private static Map<AccessibilityAction, String> sNodeActions = new LinkedHashMap<>();
+    static {
+        sNodeActions.put(AccessibilityAction.ACTION_PASTE, "\uD83D\uDCCB");
+        sNodeActions.put(AccessibilityAction.ACTION_CUT, "✂");
+        sNodeActions.put(AccessibilityAction.ACTION_COPY, "⎘");
+        sNodeActions.put(AccessibilityAction.ACTION_SCROLL_BACKWARD, "←");
+        sNodeActions.put(AccessibilityAction.ACTION_SCROLL_LEFT, "←");
+        sNodeActions.put(AccessibilityAction.ACTION_SCROLL_FORWARD, "→");
+        sNodeActions.put(AccessibilityAction.ACTION_SCROLL_RIGHT, "→");
+        sNodeActions.put(AccessibilityAction.ACTION_SCROLL_DOWN, "↓");
+        sNodeActions.put(AccessibilityAction.ACTION_SCROLL_UP, "↑");
+    }
+
+    private static Instrumentation sInstrumentation = InstrumentationRegistry.getInstrumentation();
+    private static UiAutomation sUiAutomation = sInstrumentation.getUiAutomation();
+
+    private static int sScreenArea;
+    static {
+        Point displaySize = new Point();
+        sInstrumentation.getContext()
+                .getSystemService(WindowManager.class)
+                .getDefaultDisplay()
+                .getRealSize(displaySize);
+        sScreenArea = displaySize.x * displaySize.y;
+    }
+
+
+    /**
+     * Wraps the given exception, with one containing UI hierrarchy {@link #dumpNodes dump}
+     * in its message.
+     *
+     * <p>
+     * Can be used together with {@link ExceptionUtils#wrappingExceptions}, e.g:
+     * {@code
+     *     ExceptionUtils.wrappingExceptions(UiDumpUtils::wrapWithUiDump, () -> {
+     *         // UI-testing code
+     *     });
+     * }
+     */
+    public static UiDumpWrapperException wrapWithUiDump(Throwable cause) {
+        return (cause instanceof UiDumpWrapperException)
+                ? (UiDumpWrapperException) cause
+                : new UiDumpWrapperException(cause);
+    }
+
+    /**
+     * Dumps UI hierarchy with a given {@code root} as indented text tree into {@code out}.
+     */
+    public static void dumpNodes(AccessibilityNodeInfo root, StringBuilder out) {
+        if (root == null) {
+            appendNode(out, root);
+            return;
+        }
+
+        out.append("--- ").append(root.getPackageName()).append(" ---\n|");
+
+        recursively(root, AccessibilityNodeInfo::getChildCount, AccessibilityNodeInfo::getChild,
+                node -> {
+                    if (appendNode(out, node)) {
+                        out.append("\n|");
+                    }
+                },
+                action -> node -> {
+                    out.append("  ");
+                    action.accept(node);
+                });
+    }
+
+    private static <T> void recursively(T node,
+            ToIntFunction<T> getChildCount, BiFunction<T, Integer, T> getChildAt,
+            Consumer<T> action, Function<Consumer<T>, Consumer<T>> actionChange) {
+        if (node == null) return;
+
+        action.accept(node);
+        Consumer<T> childAction = actionChange.apply(action);
+
+        int size = getChildCount.applyAsInt(node);
+        for (int i = 0; i < size; i++) {
+            recursively(getChildAt.apply(node, i),
+                    getChildCount, getChildAt, childAction, actionChange);
+        }
+    }
+
+    private static StringBuilder appendWindow(AccessibilityWindowInfo window, StringBuilder out) {
+        if (window == null) {
+            out.append("<null window>");
+        } else {
+            if (!isEmpty(window.getTitle())) {
+                out.append(window.getTitle());
+                if (CONCISE) return out;
+                out.append(" ");
+            }
+            out.append(valueToString(
+                    AccessibilityWindowInfo.class, "TYPE_", window.getType())).append(" ");
+            if (CONCISE) return out;
+            appendArea(out, window::getBoundsInScreen);
+
+            Rect bounds = new Rect();
+            window.getBoundsInScreen(bounds);
+            out.append(bounds.width()).append("x").append(bounds.height()).append(" ");
+            if (window.isInPictureInPictureMode()) out.append("#PIP ");
+        }
+        return out;
+    }
+
+    private static void appendArea(StringBuilder out, Consumer<Rect> getBoundsInScreen) {
+        Rect rect = new Rect();
+        getBoundsInScreen.accept(rect);
+        out.append("size:");
+        out.append(toStringRounding((float) area(rect) * 100 / sScreenArea)).append("% ");
+    }
+
+    private static boolean appendNode(StringBuilder out, AccessibilityNodeInfo node) {
+        if (node == null) {
+            out.append("<null node>");
+            return true;
+        }
+
+        if (IGNORE_INVISIBLE && !node.isVisibleToUser()) return false;
+
+        boolean markedClickable = false;
+        boolean markedNonFocusable = false;
+
+        try {
+            if (node.isFocused() || node.isAccessibilityFocused()) {
+                out.append(">");
+            }
+
+            if ((node.getActions() & AccessibilityNodeInfo.ACTION_EXPAND) != 0) {
+                out.append("[+] ");
+            }
+            if ((node.getActions() & AccessibilityNodeInfo.ACTION_COLLAPSE) != 0) {
+                out.append("[-] ");
+            }
+
+            CharSequence txt = node.getText();
+            if (node.isCheckable()) {
+                out.append("[").append(node.isChecked() ? "X" : "_").append("] ");
+            } else if (node.isEditable()) {
+                if (txt == null) txt = "";
+                out.append("[");
+                appendTextWithCursor(out, node, txt);
+                out.append("] ");
+            } else if (node.isClickable()) {
+                markedClickable = true;
+                out.append("[");
+            } else if (!node.isImportantForAccessibility()) {
+                markedNonFocusable = true;
+                out.append("(");
+            }
+
+            if (appendNodeText(out, node)) return true;
+        } finally {
+            backspaceIf(' ', out);
+            if (markedClickable) {
+                out.append("]");
+                if (node.isLongClickable()) out.append("+");
+                out.append(" ");
+            }
+            if (markedNonFocusable) out.append(") ");
+
+            if (CONCISE) out.append(" ");
+
+            for (Map.Entry<String, Boolean> prop : sNodeFlags.entrySet()) {
+                boolean value = call(node, boolGetter(prop.getKey()));
+                if (value != prop.getValue()) {
+                    out.append("#");
+                    if (!value) out.append("not_");
+                    out.append(prop.getKey()).append(" ");
+                }
+            }
+
+            if (SHOW_ACTIONS) {
+                LinkedHashSet<String> symbols = new LinkedHashSet<>();
+                for (AccessibilityAction accessibilityAction : node.getActionList()) {
+                    String symbol = sNodeActions.get(accessibilityAction);
+                    if (symbol != null) symbols.add(symbol);
+                }
+                merge(symbols, "←", "→", "↔");
+                merge(symbols, "↑", "↓", "↕");
+                symbols.forEach(out::append);
+                if (!symbols.isEmpty()) out.append(" ");
+
+                getActions(node)
+                        .map(a -> "[" + actionToString(a) + "] ")
+                        .forEach(out::append);
+            }
+
+            Bundle extras = node.getExtras();
+            for (String extra : extras.keySet()) {
+                if (extra.equals("AccessibilityNodeInfo.chromeRole")) continue;
+                if (extra.equals("AccessibilityNodeInfo.roleDescription")) continue;
+                String value = "" + extras.get(extra);
+                if (value.isEmpty()) continue;
+                out.append(extra).append(":").append(value).append(" ");
+            }
+        }
+        return true;
+    }
+
+    private static StringBuilder appendTextWithCursor(StringBuilder out, AccessibilityNodeInfo node,
+            CharSequence txt) {
+        out.append(txt);
+        insertAtEnd(out, txt.length() - 1 - node.getTextSelectionStart(), "ꕯ");
+        if (node.getTextSelectionEnd() != node.getTextSelectionStart()) {
+            insertAtEnd(out, txt.length() - 1 - node.getTextSelectionEnd(), "ꕯ");
+        }
+        return out;
+    }
+
+    private static boolean appendNodeText(StringBuilder out, AccessibilityNodeInfo node) {
+        CharSequence txt = node.getText();
+
+        Bundle extras = node.getExtras();
+        if (extras.containsKey("AccessibilityNodeInfo.roleDescription")) {
+            out.append("<").append(extras.getString("AccessibilityNodeInfo.chromeRole"))
+                    .append("> ");
+        } else if (extras.containsKey("AccessibilityNodeInfo.chromeRole")) {
+            out.append("<").append(extras.getString("AccessibilityNodeInfo.chromeRole"))
+                    .append("> ");
+        }
+
+        if (CONCISE) {
+            if (!isEmpty(node.getContentDescription())) {
+                out.append(escape(node.getContentDescription()));
+                return true;
+            }
+            if (!isEmpty(node.getPaneTitle())) {
+                out.append(escape(node.getPaneTitle()));
+                return true;
+            }
+            if (!isEmpty(txt) && !node.isEditable()) {
+                out.append('"');
+                if (node.getTextSelectionStart() > 0 || node.getTextSelectionEnd() > 0) {
+                    appendTextWithCursor(out, node, txt);
+                } else {
+                    out.append(escape(txt));
+                }
+                out.append('"');
+                return true;
+            }
+            if (!isEmpty(node.getViewIdResourceName())) {
+                out.append("@").append(fromLast("/", node.getViewIdResourceName()));
+                return true;
+            }
+        }
+
+        if (node.getParent() == null && node.getWindow() != null) {
+            appendWindow(node.getWindow(), out);
+            if (CONCISE) return true;
+            out.append(" ");
+        }
+
+        if (!extras.containsKey("AccessibilityNodeInfo.chromeRole")) {
+            out.append(fromLast(".", node.getClassName())).append(" ");
+        }
+        ifNotEmpty(node.getViewIdResourceName(),
+                s -> out.append("@").append(fromLast("/", s)).append(" "));
+        ifNotEmpty(node.getPaneTitle(), s -> out.append("## ").append(s).append(" "));
+        ifNotEmpty(txt, s -> out.append("\"").append(s).append("\" "));
+
+        ifNotEmpty(node.getContentDescription(), s -> out.append("//").append(s).append(" "));
+
+        appendArea(out, node::getBoundsInScreen);
+        return false;
+    }
+
+    private static <T> String valueToString(Class<?> clazz, String prefix, T value) {
+        String s = flagsToString(clazz, prefix, value, Objects::equals);
+        if (s.isEmpty()) s = "" + value;
+        return s;
+    }
+
+    private static <T> String flagsToString(Class<?> clazz, String prefix, T flags,
+            BiPredicate<T, T> test) {
+        return mkStr(sb -> {
+            consts(clazz, prefix)
+                    .filter(f -> box(f.getType()).isInstance(flags))
+                    .forEach(c -> {
+                        try {
+                            if (test.test(flags, read(null, c))) {
+                                sb.append(c.getName().substring(prefix.length())).append("|");
+                            }
+                        } catch (Exception e) {
+                            throw new RuntimeException("Error while dealing with " + c, e);
+                        }
+                    });
+            backspace(sb);
+        });
+    }
+
+    private static Class box(Class c) {
+        return c == int.class ? Integer.class : c;
+    }
+
+    private static Stream<Field> consts(Class<?> clazz, String prefix) {
+        return Arrays.stream(clazz.getDeclaredFields())
+                .filter(f -> isConst(f) && f.getName().startsWith(prefix));
+    }
+
+    private static boolean isConst(Field f) {
+        return Modifier.isStatic(f.getModifiers()) && Modifier.isFinal(f.getModifiers());
+    }
+
+    private static Character last(StringBuilder sb) {
+        return sb.length() == 0 ? null : sb.charAt(sb.length() - 1);
+    }
+
+    private static StringBuilder backspaceIf(char c, StringBuilder sb) {
+        if (Objects.equals(last(sb), c)) backspace(sb);
+        return sb;
+    }
+
+    private static StringBuilder backspace(StringBuilder sb) {
+        if (sb.length() != 0) {
+            sb.deleteCharAt(sb.length() - 1);
+        }
+        return sb;
+    }
+
+    private static String toStringRounding(float f) {
+        return f >= 5.0 ? "" + (int) f : String.format("%.1f", f);
+    }
+
+    private static int area(Rect r) {
+        return Math.abs((r.right - r.left) * (r.bottom - r.top));
+    }
+
+    private static String escape(CharSequence s) {
+        return mkStr(out -> {
+            for (int i = 0; i < s.length(); i++) {
+                char c = s.charAt(i);
+                if (c < 127 || c == 0xa0 || c >= 0x2000 && c < 0x2070) {
+                    out.append(c);
+                } else {
+                    out.append("\\u").append(Integer.toHexString(c));
+                }
+            }
+        });
+    }
+
+    private static Stream<AccessibilityAction> getActions(
+            AccessibilityNodeInfo node) {
+        if (node == null) return Stream.empty();
+        return node.getActionList().stream()
+                .filter(a -> !AccessibilityAction.ACTION_SHOW_ON_SCREEN.equals(a)
+                        && (a.getId()
+                                & ~IGNORED_ACTIONS
+                                & ~SPECIALLY_HANDLED_ACTIONS
+                            ) != 0);
+    }
+
+    private static String actionToString(AccessibilityAction a) {
+        if (!isEmpty(a.getLabel())) return a.getLabel().toString();
+        return valueToString(AccessibilityAction.class, "ACTION_", a);
+    }
+
+    private static void merge(Set<String> symbols, String a, String b, String ab) {
+        if (symbols.contains(a) && symbols.contains(b)) {
+            symbols.add(ab);
+            symbols.remove(a);
+            symbols.remove(b);
+        }
+    }
+
+    private static String fromLast(String substr, CharSequence whole) {
+        String wholeStr = whole.toString();
+        int idx = wholeStr.lastIndexOf(substr);
+        if (idx < 0) return wholeStr;
+        return wholeStr.substring(idx + substr.length());
+    }
+
+    private static String boolGetter(String propName) {
+        return "is" + Character.toUpperCase(propName.charAt(0)) + propName.substring(1);
+    }
+
+    private static <T> T read(Object o, Field f) {
+        try {
+            f.setAccessible(true);
+            return (T) f.get(o);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static <T> T call(Object o, String methodName, Object... args) {
+        Class clazz = o instanceof Class ? (Class) o : o.getClass();
+        try {
+            Method method = clazz.getDeclaredMethod(methodName, mapToTypes(args));
+            method.setAccessible(true);
+            //noinspection unchecked
+            return (T) method.invoke(o, args);
+        } catch (NoSuchMethodException e) {
+            throw new RuntimeException(
+                    newlineSeparated(Arrays.asList(clazz.getDeclaredMethods())), e);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static Class[] mapToTypes(Object[] args) {
+        return Arrays.stream(args).map(Object::getClass).toArray(Class[]::new);
+    }
+
+    private static void ifNotEmpty(CharSequence t, Consumer<CharSequence> f) {
+        if (!isEmpty(t)) {
+            f.accept(t);
+        }
+    }
+
+    private static StringBuilder insertAtEnd(StringBuilder sb, int pos, String s) {
+        return sb.insert(sb.length() - 1 - pos, s);
+    }
+
+    private static <T, R> R fold(List<T> l, R init, BiFunction<R, T, R> combine) {
+        R result = init;
+        for (T t : l) {
+            result = combine.apply(result, t);
+        }
+        return result;
+    }
+
+    private static <T> String toString(List<T> l, String sep, Function<T, String> elemToStr) {
+        return fold(l, "", (a, b) -> a + sep + elemToStr.apply(b));
+    }
+
+    private static <T> String toString(List<T> l, String sep) {
+        return toString(l, sep, String::valueOf);
+    }
+
+    private static String newlineSeparated(List<?> l) {
+        return toString(l, "\n");
+    }
+
+    private static String mkStr(Consumer<StringBuilder> build) {
+        StringBuilder t = new StringBuilder();
+        build.accept(t);
+        return t.toString();
+    }
+
+    private static class UiDumpWrapperException extends RuntimeException {
+        private UiDumpWrapperException(Throwable cause) {
+            super(cause.getMessage() + "\n\nWhile displaying the following UI:\n"
+                    + mkStr(sb -> dumpNodes(sUiAutomation.getRootInActiveWindow(), sb)), cause);
+        }
+    }
+}
diff --git a/common/device-side/util/Android.bp b/common/device-side/util/Android.bp
deleted file mode 100644
index 134b4f7..0000000
--- a/common/device-side/util/Android.bp
+++ /dev/null
@@ -1,39 +0,0 @@
-// 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.
-
-java_library_static {
-    name: "compatibility-device-util",
-    sdk_version: "test_current",
-
-    srcs: [
-        "src/**/*.java",
-        "src/**/*.aidl",
-    ],
-
-    static_libs: [
-        "compatibility-common-util-devicesidelib",
-        "android-support-test",
-        "ub-uiautomator",
-        "mockito-target-minus-junit4",
-        "androidx.annotation_annotation",
-        "truth-prebuilt",
-    ],
-
-    libs: [
-        "android.test.runner.stubs",
-        "android.test.base.stubs",
-    ],
-
-    jarjar_rules: "protobuf-jarjar-rules.txt",
-}
\ No newline at end of file
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ActivitiesWatcher.java b/common/device-side/util/src/com/android/compatibility/common/util/ActivitiesWatcher.java
deleted file mode 100644
index a2b0152..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ActivitiesWatcher.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * 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 com.android.compatibility.common.util;
-
-import android.app.Activity;
-import android.app.Application.ActivityLifecycleCallbacks;
-import android.os.Bundle;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import java.util.Map;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Helper object used to watch for activities lifecycle events.
- *
- * <p><b>NOTE:</b> currently it's limited to just one occurrence of each event.
- *
- * <p>These limitations will be fixed as needed (A.K.A. K.I.S.S. :-)
- */
-public final class ActivitiesWatcher implements ActivityLifecycleCallbacks {
-
-    private static final String TAG = ActivitiesWatcher.class.getSimpleName();
-
-    private final Map<String, ActivityWatcher> mWatchers = new ArrayMap<>();
-    private final long mTimeoutMs;
-
-    /**
-     * Default constructor.
-     *
-     * @param timeoutMs how long to wait for given lifecycle event before timing out.
-     */
-    public ActivitiesWatcher(long timeoutMs) {
-        mTimeoutMs = timeoutMs;
-    }
-
-    @Override
-    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
-        Log.v(TAG, "onActivityCreated(): " + activity);
-        notifyWatcher(activity, ActivityLifecycle.CREATED);
-    }
-
-    @Override
-    public void onActivityStarted(Activity activity) {
-        Log.v(TAG, "onActivityStarted(): " + activity);
-        notifyWatcher(activity, ActivityLifecycle.STARTED);
-    }
-
-    @Override
-    public void onActivityResumed(Activity activity) {
-        Log.v(TAG, "onActivityResumed(): " + activity);
-        notifyWatcher(activity, ActivityLifecycle.RESUMED);
-    }
-
-    @Override
-    public void onActivityPaused(Activity activity) {
-        Log.v(TAG, "onActivityPaused(): " + activity);
-        notifyWatcher(activity, ActivityLifecycle.PAUSED);
-    }
-
-    @Override
-    public void onActivityStopped(Activity activity) {
-        Log.v(TAG, "onActivityStopped(): " + activity);
-        notifyWatcher(activity, ActivityLifecycle.STOPPED);
-    }
-
-    @Override
-    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
-        Log.v(TAG, "onActivitySaveInstanceState(): " + activity);
-        notifyWatcher(activity, ActivityLifecycle.SAVE_INSTANCE);
-    }
-
-    @Override
-    public void onActivityDestroyed(Activity activity) {
-        Log.v(TAG, "onActivityDestroyed(): " + activity);
-        notifyWatcher(activity, ActivityLifecycle.DESTROYED);
-    }
-
-    /**
-     * Gets a watcher for the given activity.
-     *
-     * @throws IllegalStateException if already registered.
-     */
-    public ActivityWatcher watch(@NonNull Class<? extends Activity> clazz) {
-        return watch(clazz.getName());
-    }
-
-    @Override
-    public String toString() {
-        return "[ActivitiesWatcher: activities=" + mWatchers.keySet() + "]";
-    }
-
-    /**
-     * Gets a watcher for the given activity.
-     *
-     * @throws IllegalStateException if already registered.
-     */
-    public ActivityWatcher watch(@NonNull String className) {
-        if (mWatchers.containsKey(className)) {
-            throw new IllegalStateException("Already watching " + className);
-        }
-        Log.d(TAG, "Registering watcher for " + className);
-        final ActivityWatcher watcher = new ActivityWatcher(mTimeoutMs);
-        mWatchers.put(className,  watcher);
-        return watcher;
-    }
-
-    private void notifyWatcher(@NonNull Activity activity, @NonNull ActivityLifecycle lifecycle) {
-        final String className = activity.getComponentName().getClassName();
-        final ActivityWatcher watcher = mWatchers.get(className);
-        if (watcher != null) {
-            Log.d(TAG, "notifying watcher of " + className + " of " + lifecycle);
-            watcher.notify(lifecycle);
-        } else {
-            Log.v(TAG, lifecycle + ": no watcher for " + className);
-        }
-    }
-
-    /**
-     * Object used to watch for acitivity lifecycle events.
-     *
-     * <p><b>NOTE: </b>currently it only supports one occurrence for each event.
-     */
-    public static final class ActivityWatcher {
-        private final CountDownLatch mCreatedLatch = new CountDownLatch(1);
-        private final CountDownLatch mStartedLatch = new CountDownLatch(1);
-        private final CountDownLatch mResumedLatch = new CountDownLatch(1);
-        private final CountDownLatch mPausedLatch = new CountDownLatch(1);
-        private final CountDownLatch mStoppedLatch = new CountDownLatch(1);
-        private final CountDownLatch mSaveInstanceLatch = new CountDownLatch(1);
-        private final CountDownLatch mDestroyedLatch = new CountDownLatch(1);
-        private final long mTimeoutMs;
-
-        private ActivityWatcher(long timeoutMs) {
-            mTimeoutMs = timeoutMs;
-        }
-
-        /**
-         * Blocks until the given lifecycle event happens.
-         *
-         * @throws IllegalStateException if it times out while waiting.
-         * @throws InterruptedException if interrupted while waiting.
-         */
-        public void waitFor(@NonNull ActivityLifecycle lifecycle) throws InterruptedException {
-            final CountDownLatch latch = getLatch(lifecycle);
-            final boolean called = latch.await(mTimeoutMs, TimeUnit.MILLISECONDS);
-            if (!called) {
-                throw new IllegalStateException(lifecycle + " not called in " + mTimeoutMs + " ms");
-            }
-        }
-
-        private CountDownLatch getLatch(@NonNull ActivityLifecycle lifecycle) {
-            switch (lifecycle) {
-                case CREATED:
-                    return mCreatedLatch;
-                case STARTED:
-                    return mStartedLatch;
-                case RESUMED:
-                    return mResumedLatch;
-                case PAUSED:
-                    return mPausedLatch;
-                case STOPPED:
-                    return mStoppedLatch;
-                case SAVE_INSTANCE:
-                    return mSaveInstanceLatch;
-                case DESTROYED:
-                    return mDestroyedLatch;
-                default:
-                    throw new IllegalArgumentException("unsupported lifecycle: " + lifecycle);
-            }
-        }
-
-        private void notify(@NonNull ActivityLifecycle lifecycle) {
-            getLatch(lifecycle).countDown();
-        }
-    }
-
-    /**
-     * Supported activity lifecycle.
-     */
-    public enum ActivityLifecycle {
-        CREATED,
-        STARTED,
-        RESUMED,
-        PAUSED,
-        STOPPED,
-        SAVE_INSTANCE,
-        DESTROYED
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ActivityLauncher.java b/common/device-side/util/src/com/android/compatibility/common/util/ActivityLauncher.java
deleted file mode 100644
index 20d9331..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ActivityLauncher.java
+++ /dev/null
@@ -1,60 +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.
- */
-package com.android.compatibility.common.util;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.support.test.rule.ActivityTestRule;
-
-import androidx.annotation.NonNull;
-
-import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher;
-
-/**
- * Helper used to launch an activity and watch for its lifecycle events.
- *
- * @param <A> activity type
- */
-public final class ActivityLauncher<A extends Activity> {
-
-    private final ActivityWatcher mWatcher;
-    private final ActivityTestRule<A> mActivityTestRule;
-    private final Intent mLaunchIntent;
-
-    public ActivityLauncher(@NonNull Context context, @NonNull ActivitiesWatcher watcher,
-            @NonNull Class<A> activityClass) {
-        mWatcher = watcher.watch(activityClass);
-        mActivityTestRule = new ActivityTestRule<>(activityClass);
-        mLaunchIntent = new Intent(context, activityClass);
-    }
-
-    /**
-     * Gets a watcher for the activity lifecycle events.
-     */
-    @NonNull
-    public ActivityWatcher getWatcher() {
-        return mWatcher;
-    }
-
-    /**
-     * Launches the activity.
-     */
-    @NonNull
-    public A launchActivity() {
-        return mActivityTestRule.launchActivity(mLaunchIntent);
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/AdoptShellPermissionsRule.java b/common/device-side/util/src/com/android/compatibility/common/util/AdoptShellPermissionsRule.java
deleted file mode 100644
index f7b50b4..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/AdoptShellPermissionsRule.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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 com.android.compatibility.common.util;
-
-import android.app.UiAutomation;
-import android.support.test.InstrumentationRegistry;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-/**
- * Custom JUnit4 rule that runs a test adopting Shell's permissions, revoking them at the end.
- *
- * <p>NOTE: should only be used in the cases where *every* test in a class requires the permission.
- * For a more fine-grained access, use
- * {@link SystemUtil#runWithShellPermissionIdentity(ThrowingRunnable)}
- * or {@link SystemUtil#callWithShellPermissionIdentity(java.util.concurrent.Callable)} instead.
- */
-public class AdoptShellPermissionsRule implements TestRule {
-
-    private final UiAutomation mUiAutomation;
-
-    private final String[] mPermissions;
-
-    public AdoptShellPermissionsRule() {
-        this(InstrumentationRegistry.getInstrumentation().getUiAutomation());
-    }
-
-    public AdoptShellPermissionsRule(@NonNull UiAutomation uiAutomation) {
-        this(uiAutomation, (String[]) null);
-    }
-
-    public AdoptShellPermissionsRule(@NonNull UiAutomation uiAutomation,
-            @Nullable String... permissions) {
-        mUiAutomation = uiAutomation;
-        mPermissions = permissions;
-    }
-
-    @Override
-    public Statement apply(Statement base, Description description) {
-        return new Statement() {
-
-            @Override
-            public void evaluate() throws Throwable {
-                if (mPermissions != null) {
-                    mUiAutomation.adoptShellPermissionIdentity(mPermissions);
-                } else {
-                    mUiAutomation.adoptShellPermissionIdentity();
-                }
-                try {
-                    base.evaluate();
-                } finally {
-                    mUiAutomation.dropShellPermissionIdentity();
-                }
-            }
-        };
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/AmUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/AmUtils.java
deleted file mode 100644
index f3e178b..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/AmUtils.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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 com.android.compatibility.common.util;
-
-public class AmUtils {
-    private static final String TAG = "CtsAmUtils";
-
-    private static final String DUMPSYS_ACTIVITY_PROCESSES = "dumpsys activity --proto processes";
-
-    private AmUtils() {
-    }
-
-    /** Run "adb shell am make-uid-idle PACKAGE" */
-    public static void runMakeUidIdle(String packageName) {
-        SystemUtil.runShellCommandForNoOutput("am make-uid-idle " + packageName);
-    }
-
-    /** Run "adb shell am kill PACKAGE" */
-    public static void runKill(String packageName) throws Exception {
-        runKill(packageName, false /* wait */);
-    }
-
-    public static void runKill(String packageName, boolean wait) throws Exception {
-        SystemUtil.runShellCommandForNoOutput("am kill --user cur " + packageName);
-
-        if (!wait) {
-            return;
-        }
-
-        TestUtils.waitUntil("package process was not killed:" + packageName,
-                () -> !isProcessRunning(packageName));
-    }
-
-    private static boolean isProcessRunning(String packageName) {
-        final String output = SystemUtil.runShellCommand("ps -A -o NAME");
-        String[] packages = output.split("\\n");
-        for (int i = packages.length -1; i >=0; --i) {
-            if (packages[i].equals(packageName)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /** Run "adb shell am set-standby-bucket" */
-    public static void setStandbyBucket(String packageName, int value) {
-        SystemUtil.runShellCommandForNoOutput("am set-standby-bucket " + packageName
-                + " " + value);
-    }
-
-    /** Wait until all broad queues are idle. */
-    public static void waitForBroadcastIdle() {
-        SystemUtil.runCommandAndPrintOnLogcat(TAG, "am wait-for-broadcast-idle");
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ApiLevelUtil.java b/common/device-side/util/src/com/android/compatibility/common/util/ApiLevelUtil.java
deleted file mode 100644
index 943ebc7..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ApiLevelUtil.java
+++ /dev/null
@@ -1,95 +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.compatibility.common.util;
-
-import android.os.Build;
-
-import java.lang.reflect.Field;
-
-/**
- * Device-side compatibility utility class for reading device API level.
- */
-public class ApiLevelUtil {
-
-    public static boolean isBefore(int version) {
-        return Build.VERSION.SDK_INT < version;
-    }
-
-    public static boolean isBefore(String version) {
-        return Build.VERSION.SDK_INT < resolveVersionString(version);
-    }
-
-    public static boolean isAfter(int version) {
-        return Build.VERSION.SDK_INT > version;
-    }
-
-    public static boolean isAfter(String version) {
-        return Build.VERSION.SDK_INT > resolveVersionString(version);
-    }
-
-    public static boolean isAtLeast(int version) {
-        return Build.VERSION.SDK_INT >= version;
-    }
-
-    public static boolean isAtLeast(String version) {
-        return Build.VERSION.SDK_INT >= resolveVersionString(version);
-    }
-
-    public static boolean isAtMost(int version) {
-        return Build.VERSION.SDK_INT <= version;
-    }
-
-    public static boolean isAtMost(String version) {
-        return Build.VERSION.SDK_INT <= resolveVersionString(version);
-    }
-
-    public static int getApiLevel() {
-        return Build.VERSION.SDK_INT;
-    }
-
-    public static boolean codenameEquals(String name) {
-        return Build.VERSION.CODENAME.equalsIgnoreCase(name.trim());
-    }
-
-    public static boolean codenameStartsWith(String prefix) {
-        return Build.VERSION.CODENAME.startsWith(prefix);
-    }
-
-    public static String getCodename() {
-        return Build.VERSION.CODENAME;
-    }
-
-    protected static int resolveVersionString(String versionString) {
-        // Attempt 1: Parse version string as an integer, e.g. "23" for M
-        try {
-            return Integer.parseInt(versionString);
-        } catch (NumberFormatException e) { /* ignore for alternate approaches below */ }
-        // Attempt 2: Find matching field in VersionCodes utility class, return value
-        try {
-            Field versionField = VersionCodes.class.getField(versionString.toUpperCase());
-            return versionField.getInt(null); // no instance for VERSION_CODES, use null
-        } catch (IllegalAccessException | NoSuchFieldException e) { /* ignore */ }
-        // Attempt 3: Find field within android.os.Build.VERSION_CODES
-        try {
-            Field versionField = Build.VERSION_CODES.class.getField(versionString.toUpperCase());
-            return versionField.getInt(null); // no instance for VERSION_CODES, use null
-        } catch (IllegalAccessException | NoSuchFieldException e) {
-            throw new RuntimeException(
-                    String.format("Failed to parse version string %s", versionString), e);
-        }
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/AppOpsUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/AppOpsUtils.java
deleted file mode 100644
index 939d6da..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/AppOpsUtils.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2018 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 com.android.compatibility.common.util;
-
-import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.MODE_DEFAULT;
-import static android.app.AppOpsManager.MODE_ERRORED;
-import static android.app.AppOpsManager.MODE_IGNORED;
-
-import android.app.AppOpsManager;
-import android.support.test.InstrumentationRegistry;
-
-import java.io.IOException;
-
-/**
- * Utilities for controlling App Ops settings, and testing whether ops are logged.
- */
-public class AppOpsUtils {
-
-    /**
-     * Resets a package's app ops configuration to the device default. See AppOpsManager for the
-     * default op settings.
-     *
-     * <p>
-     * It's recommended to call this in setUp() and tearDown() of your test so the test starts and
-     * ends with a reproducible default state, and so doesn't affect other tests.
-     *
-     * <p>
-     * Some app ops are configured to be non-resettable, which means that the state of these will
-     * not be reset even when calling this method.
-     */
-    public static String reset(String packageName) throws IOException {
-        return runCommand("appops reset " + packageName);
-    }
-
-    /**
-     * Sets the app op mode (e.g. allowed, denied) for a single package and operation.
-     */
-    public static String setOpMode(String packageName, String opStr, int mode)
-            throws IOException {
-        String modeStr;
-        switch (mode) {
-            case MODE_ALLOWED:
-                modeStr = "allow";
-                break;
-            case MODE_ERRORED:
-                modeStr = "deny";
-                break;
-            case MODE_IGNORED:
-                modeStr = "ignore";
-                break;
-            case MODE_DEFAULT:
-                modeStr = "default";
-                break;
-            default:
-                throw new IllegalArgumentException("Unexpected app op type");
-        }
-        String command = "appops set " + packageName + " " + opStr + " " + modeStr;
-        return runCommand(command);
-    }
-
-    /**
-     * Get the app op mode (e.g. MODE_ALLOWED, MODE_DEFAULT) for a single package and operation.
-     */
-    public static int getOpMode(String packageName, String opStr)
-            throws IOException {
-        String opState = getOpState(packageName, opStr);
-        if (opState.contains(" allow")) {
-            return MODE_ALLOWED;
-        } else if (opState.contains(" deny")) {
-            return MODE_ERRORED;
-        } else if (opState.contains(" ignore")) {
-            return MODE_IGNORED;
-        } else if (opState.contains(" default")) {
-            return MODE_DEFAULT;
-        } else {
-            throw new IllegalStateException("Unexpected app op mode returned " + opState);
-        }
-    }
-
-    /**
-     * Returns whether an allowed operation has been logged by the AppOpsManager for a
-     * package. Operations are noted when the app attempts to perform them and calls e.g.
-     * {@link AppOpsManager#noteOperation}.
-     *
-     * @param opStr The public string constant of the operation (e.g. OPSTR_READ_SMS).
-     */
-    public static boolean allowedOperationLogged(String packageName, String opStr)
-            throws IOException {
-        return getOpState(packageName, opStr).contains(" time=");
-    }
-
-    /**
-     * Returns whether a rejected operation has been logged by the AppOpsManager for a
-     * package. Operations are noted when the app attempts to perform them and calls e.g.
-     * {@link AppOpsManager#noteOperation}.
-     *
-     * @param opStr The public string constant of the operation (e.g. OPSTR_READ_SMS).
-     */
-    public static boolean rejectedOperationLogged(String packageName, String opStr)
-            throws IOException {
-        return getOpState(packageName, opStr).contains(" rejectTime=");
-    }
-
-    /**
-     * Returns the app op state for a package. Includes information on when the operation was last
-     * attempted to be performed by the package.
-     *
-     * Format: "SEND_SMS: allow; time=+23h12m54s980ms ago; rejectTime=+1h10m23s180ms"
-     */
-    private static String getOpState(String packageName, String opStr) throws IOException {
-        return runCommand("appops get " + packageName + " " + opStr);
-    }
-
-    private static String runCommand(String command) throws IOException {
-        return SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/AppStandbyUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/AppStandbyUtils.java
deleted file mode 100644
index 6eeaae2..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/AppStandbyUtils.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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 com.android.compatibility.common.util;
-
-import android.util.Log;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class AppStandbyUtils {
-    private static final String TAG = "CtsAppStandbyUtils";
-
-    /**
-     * Returns if app standby is enabled.
-     *
-     * @return true if enabled; or false if disabled.
-     */
-    public static boolean isAppStandbyEnabled() {
-        final String result = SystemUtil.runShellCommand(
-                "dumpsys usagestats is-app-standby-enabled").trim();
-        return Boolean.parseBoolean(result);
-    }
-
-    /**
-     * Sets enabled state for app standby feature for runtime switch.
-     *
-     * App standby feature has 2 switches. This one affects the switch at runtime. If the build
-     * switch is off, enabling the runtime switch will not enable App standby.
-     *
-     * @param enabled if App standby is enabled.
-     */
-    public static void setAppStandbyEnabledAtRuntime(boolean enabled) {
-        final String value = enabled ? "1" : "0";
-        Log.d(TAG, "Setting AppStandby " + (enabled ? "enabled" : "disabled") + " at runtime.");
-        SettingsUtils.putGlobalSetting("app_standby_enabled", value);
-    }
-
-    /**
-     * Returns if app standby is enabled at runtime. Note {@link #isAppStandbyEnabled()} may still
-     * return {@code false} if this method returns {@code true}, because app standby can be disabled
-     * at build time as well.
-     *
-     * @return true if enabled at runtime; or false if disabled at runtime.
-     */
-    public static boolean isAppStandbyEnabledAtRuntime() {
-        final String result =
-                SystemUtil.runShellCommand("settings get global app_standby_enabled").trim();
-        final boolean boolResult = result.equals("1") || result.equals("null");
-        Log.d(TAG, "AppStandby is " + (boolResult ? "enabled" : "disabled") + " at runtime.");
-        return boolResult;
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BatteryUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/BatteryUtils.java
deleted file mode 100644
index 272bc67..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BatteryUtils.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * 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 com.android.compatibility.common.util;
-
-import static com.android.compatibility.common.util.SettingsUtils.putGlobalSetting;
-import static com.android.compatibility.common.util.TestUtils.waitUntil;
-
-import android.content.pm.PackageManager;
-import android.os.BatteryManager;
-import android.os.PowerManager;
-import android.provider.Settings.Global;
-import android.support.test.InstrumentationRegistry;
-import android.util.Log;
-
-import org.junit.Assume;
-
-public class BatteryUtils {
-    private static final String TAG = "CtsBatteryUtils";
-
-    private BatteryUtils() {
-    }
-
-    public static BatteryManager getBatteryManager() {
-        return InstrumentationRegistry.getContext().getSystemService(BatteryManager.class);
-    }
-
-    public static PowerManager getPowerManager() {
-        return InstrumentationRegistry.getContext().getSystemService(PowerManager.class);
-    }
-
-    /** Make the target device think it's off charger. */
-    public static void runDumpsysBatteryUnplug() {
-        SystemUtil.runShellCommandForNoOutput("cmd battery unplug");
-
-        Log.d(TAG, "Battery UNPLUGGED");
-    }
-
-    /**
-     * Set the battery level to {@code level} percent. The valid range is [0, 100].
-     */
-    public static void runDumpsysBatterySetLevel(int level) throws Exception {
-        SystemUtil.runShellCommandForNoOutput(("cmd battery set level " + level));
-
-        Log.d(TAG, "Battery level set to " + level);
-    }
-
-    /**
-     * Set whether the device is plugged in to a charger or not.
-     */
-    public static void runDumpsysBatterySetPluggedIn(boolean pluggedIn) throws Exception {
-        SystemUtil.runShellCommandForNoOutput(("cmd battery set ac " + (pluggedIn ? "1" : "0")));
-
-        Log.d(TAG, "Battery AC set to " + pluggedIn);
-    }
-
-    /** Reset the effect of all the previous {@code runDumpsysBattery*} call  */
-    public static void runDumpsysBatteryReset() {
-        SystemUtil.runShellCommandForNoOutput(("cmd battery reset"));
-
-        Log.d(TAG, "Battery RESET");
-    }
-
-    /**
-     * Enable / disable battery saver. Note {@link #runDumpsysBatteryUnplug} must have been
-     * executed before enabling BS.
-     */
-    public static void enableBatterySaver(boolean enabled) throws Exception {
-        if (enabled) {
-            SystemUtil.runShellCommandForNoOutput("cmd power set-mode 1");
-            putGlobalSetting(Global.LOW_POWER_MODE, "1");
-            waitUntil("Battery saver still off", () -> getPowerManager().isPowerSaveMode());
-            waitUntil("Location mode still " + getPowerManager().getLocationPowerSaveMode(),
-                    () -> (PowerManager.LOCATION_MODE_NO_CHANGE
-                            != getPowerManager().getLocationPowerSaveMode()));
-
-            Thread.sleep(500);
-            waitUntil("Force all apps standby still off",
-                    () -> SystemUtil.runShellCommand("dumpsys alarm")
-                            .contains(" Force all apps standby: true\n"));
-
-        } else {
-            SystemUtil.runShellCommandForNoOutput("cmd power set-mode 0");
-            putGlobalSetting(Global.LOW_POWER_MODE, "0");
-            putGlobalSetting(Global.LOW_POWER_MODE_STICKY, "0");
-            waitUntil("Battery saver still on", () -> !getPowerManager().isPowerSaveMode());
-            waitUntil("Location mode still " + getPowerManager().getLocationPowerSaveMode(),
-                    () -> (PowerManager.LOCATION_MODE_NO_CHANGE
-                            == getPowerManager().getLocationPowerSaveMode()));
-
-            Thread.sleep(500);
-            waitUntil("Force all apps standby still on",
-                    () -> SystemUtil.runShellCommand("dumpsys alarm")
-                            .contains(" Force all apps standby: false\n"));
-        }
-
-        AmUtils.waitForBroadcastIdle();
-        Log.d(TAG, "Battery saver turned " + (enabled ? "ON" : "OFF"));
-    }
-
-    /**
-     * Turn on/off screen.
-     */
-    public static void turnOnScreen(boolean on) throws Exception {
-        if (on) {
-            SystemUtil.runShellCommandForNoOutput("input keyevent KEYCODE_WAKEUP");
-            waitUntil("Device still not interactive", () -> getPowerManager().isInteractive());
-
-        } else {
-            SystemUtil.runShellCommandForNoOutput("input keyevent KEYCODE_SLEEP");
-            waitUntil("Device still interactive", () -> !getPowerManager().isInteractive());
-        }
-        AmUtils.waitForBroadcastIdle();
-        Log.d(TAG, "Screen turned " + (on ? "ON" : "OFF"));
-    }
-
-    /** @return true if the device supports battery saver. */
-    public static boolean isBatterySaverSupported() {
-        final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
-        return !pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
-    }
-
-    /** "Assume" the current device supports battery saver. */
-    public static void assumeBatterySaverFeature() {
-        Assume.assumeTrue("Device doesn't support battery saver", isBatterySaverSupported());
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BeforeAfterRule.java b/common/device-side/util/src/com/android/compatibility/common/util/BeforeAfterRule.java
deleted file mode 100644
index be75671..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BeforeAfterRule.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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 com.android.compatibility.common.util;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-/**
- * Custom JUnit4 rule that provides "before" / "after" callbacks, which is useful to use with
- * {@link org.junit.rules.RuleChain}.
- */
-public class BeforeAfterRule implements TestRule {
-    @Override
-    public Statement apply(Statement base, Description description) {
-        return new Statement() {
-
-            @Override
-            public void evaluate() throws Throwable {
-                onBefore(base, description);
-                try {
-                    base.evaluate();
-                } finally {
-                    onAfter(base, description);
-                }
-            }
-        };
-    }
-
-    protected void onBefore(Statement base, Description description) throws Throwable {
-    }
-
-    protected void onAfter(Statement base, Description description) throws Throwable {
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BitmapUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/BitmapUtils.java
deleted file mode 100644
index 88753b1..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BitmapUtils.java
+++ /dev/null
@@ -1,180 +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.compatibility.common.util;
-
-import android.app.WallpaperManager;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.CompressFormat;
-import android.graphics.Color;
-import android.util.Log;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.lang.reflect.Method;
-import java.util.Random;
-
-public class BitmapUtils {
-    private static final String TAG = "BitmapUtils";
-
-    private BitmapUtils() {}
-
-    private static Boolean compareBasicBitmapsInfo(Bitmap bmp1, Bitmap bmp2) {
-        if (bmp1 == bmp2) {
-            return Boolean.TRUE;
-        }
-
-        if (bmp1 == null) {
-            Log.d(TAG, "compareBitmaps() failed because bmp1 is null");
-            return Boolean.FALSE;
-        }
-
-        if (bmp2 == null) {
-            Log.d(TAG, "compareBitmaps() failed because bmp2 is null");
-            return Boolean.FALSE;
-        }
-
-        if ((bmp1.getWidth() != bmp2.getWidth()) || (bmp1.getHeight() != bmp2.getHeight())) {
-            Log.d(TAG, "compareBitmaps() failed because sizes don't match "
-                    + "bmp1=(" + bmp1.getWidth() + "x" + bmp1.getHeight() + "), "
-                    + "bmp2=(" + bmp2.getWidth() + "x" + bmp2.getHeight() + ")");
-            return Boolean.FALSE;
-        }
-        return null;
-    }
-
-    /**
-     * Compares two bitmaps by pixels.
-     */
-    public static boolean compareBitmaps(Bitmap bmp1, Bitmap bmp2) {
-        final Boolean basicComparison = compareBasicBitmapsInfo(bmp1, bmp2);
-        if (basicComparison != null) return basicComparison.booleanValue();
-
-        for (int i = 0; i < bmp1.getWidth(); i++) {
-            for (int j = 0; j < bmp1.getHeight(); j++) {
-                if (bmp1.getPixel(i, j) != bmp2.getPixel(i, j)) {
-                    Log.d(TAG, "compareBitmaps(): pixels (" + i + ", " + j + ") don't match");
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    /**
-     *  Compares two bitmaps by pixels, with a buffer for mistmatches.
-     *
-     *  <p>For example, if {@code minimumPrecision} is 0.99, at least 99% of the pixels should
-     *  match.
-     */
-    public static boolean compareBitmaps(Bitmap bmp1, Bitmap bmp2, double minimumPrecision) {
-        final Boolean basicComparison = compareBasicBitmapsInfo(bmp1, bmp2);
-        if (basicComparison != null) return basicComparison.booleanValue();
-
-        final int width = bmp1.getWidth();
-        final int height = bmp1.getHeight();
-
-        final long numberPixels = width * height;
-        long numberMismatches = 0;
-
-        for (int i = 0; i < width; i++) {
-            for (int j = 0; j < height; j++) {
-                if (bmp1.getPixel(i, j) != bmp2.getPixel(i, j)) {
-                    numberMismatches++;
-                    if (numberMismatches <= 10) {
-                        // Let's not spam logcat...
-                        Log.w(TAG, "compareBitmaps(): pixels (" + i + ", " + j + ") don't match");
-                    }
-                }
-            }
-        }
-        final double actualPrecision = ((double) numberPixels - numberMismatches) / (numberPixels);
-        Log.v(TAG, "compareBitmaps(): numberPixels=" + numberPixels
-                + ", numberMismatches=" + numberMismatches
-                + ", minimumPrecision=" + minimumPrecision
-                + ", actualPrecision=" + actualPrecision);
-        return actualPrecision >= minimumPrecision;
-    }
-
-    public static Bitmap generateRandomBitmap(int width, int height) {
-        final Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-        final Random generator = new Random();
-        for (int x = 0; x < width; x++) {
-            for (int y = 0; y < height; y++) {
-                bmp.setPixel(x, y, generator.nextInt(Integer.MAX_VALUE));
-            }
-        }
-        return bmp;
-    }
-
-    public static Bitmap generateWhiteBitmap(int width, int height) {
-        final Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-        bmp.eraseColor(Color.WHITE);
-        return bmp;
-    }
-
-    public static Bitmap getWallpaperBitmap(Context context) throws Exception {
-        WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
-        Class<?> noparams[] = {};
-        Class<?> wmClass = wallpaperManager.getClass();
-        Method methodGetBitmap = wmClass.getDeclaredMethod("getBitmap", noparams);
-        return (Bitmap) methodGetBitmap.invoke(wallpaperManager, null);
-    }
-
-    public static ByteArrayInputStream bitmapToInputStream(Bitmap bmp) {
-        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
-        bmp.compress(CompressFormat.PNG, 0 /*ignored for PNG*/, bos);
-        byte[] bitmapData = bos.toByteArray();
-        return new ByteArrayInputStream(bitmapData);
-    }
-
-    private static void logIfBitmapSolidColor(String fileName, Bitmap bitmap) {
-        int firstColor = bitmap.getPixel(0, 0);
-        for (int x = 0; x < bitmap.getWidth(); x++) {
-            for (int y = 0; y < bitmap.getHeight(); y++) {
-                if (bitmap.getPixel(x, y) != firstColor) {
-                    return;
-                }
-            }
-        }
-
-        Log.w(TAG, String.format("%s entire bitmap color is %x", fileName, firstColor));
-    }
-
-    public static void saveBitmap(Bitmap bitmap, String directoryName, String fileName) {
-        new File(directoryName).mkdirs(); // create dirs if needed
-
-        Log.d(TAG, "Saving file: " + fileName + " in directory: " + directoryName);
-
-        if (bitmap == null) {
-            Log.d(TAG, "File not saved, bitmap was null");
-            return;
-        }
-
-        logIfBitmapSolidColor(fileName, bitmap);
-
-        File file = new File(directoryName, fileName);
-        try (FileOutputStream fileStream = new FileOutputStream(file)) {
-            bitmap.compress(Bitmap.CompressFormat.PNG, 0 /* ignored for PNG */, fileStream);
-            fileStream.flush();
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BlockedNumberService.java b/common/device-side/util/src/com/android/compatibility/common/util/BlockedNumberService.java
deleted file mode 100644
index 360c078..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BlockedNumberService.java
+++ /dev/null
@@ -1,96 +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.
- */
-
-package com.android.compatibility.common.util;
-
-import static android.provider.BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER;
-import static android.provider.BlockedNumberContract.BlockedNumbers.CONTENT_URI;
-
-import android.app.IntentService;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.ResultReceiver;
-import android.util.Log;
-
-/**
- * A service to handle interactions with the BlockedNumberProvider. The BlockedNumberProvider
- * can only be accessed by the primary user. This service can be run as a singleton service
- * which will then be able to access the BlockedNumberProvider from a test running in a
- * secondary user.
- */
-public class BlockedNumberService extends IntentService {
-
-    static final String INSERT_ACTION = "android.telecom.cts.InsertBlockedNumber";
-    static final String DELETE_ACTION = "android.telecom.cts.DeleteBlockedNumber";
-    static final String PHONE_NUMBER_EXTRA = "number";
-    static final String URI_EXTRA = "uri";
-    static final String ROWS_EXTRA = "rows";
-    static final String RESULT_RECEIVER_EXTRA = "resultReceiver";
-
-    private static final String TAG = "CtsBlockNumberSvc";
-
-    private ContentResolver mContentResolver;
-
-    public BlockedNumberService() {
-        super(BlockedNumberService.class.getName());
-    }
-
-    @Override
-    public void onHandleIntent(Intent intent) {
-        Log.i(TAG, "Starting BlockedNumberService service: " + intent);
-        if (intent == null) {
-            return;
-        }
-        Bundle bundle;
-        mContentResolver = getContentResolver();
-        switch (intent.getAction()) {
-            case INSERT_ACTION:
-                bundle = insertBlockedNumber(intent.getStringExtra(PHONE_NUMBER_EXTRA));
-                break;
-            case DELETE_ACTION:
-                bundle = deleteBlockedNumber(Uri.parse(intent.getStringExtra(URI_EXTRA)));
-                break;
-            default:
-                bundle = new Bundle();
-                break;
-        }
-        ResultReceiver receiver = intent.getParcelableExtra(RESULT_RECEIVER_EXTRA);
-        receiver.send(0, bundle);
-    }
-
-    private Bundle insertBlockedNumber(String number) {
-        Log.i(TAG, "insertBlockedNumber: " + number);
-
-        ContentValues cv = new ContentValues();
-        cv.put(COLUMN_ORIGINAL_NUMBER, number);
-        Uri uri = mContentResolver.insert(CONTENT_URI, cv);
-        Bundle bundle = new Bundle();
-        bundle.putString(URI_EXTRA, uri.toString());
-        return bundle;
-    }
-
-    private Bundle deleteBlockedNumber(Uri uri) {
-        Log.i(TAG, "deleteBlockedNumber: " + uri);
-
-        int rows = mContentResolver.delete(uri, null, null);
-        Bundle bundle = new Bundle();
-        bundle.putInt(ROWS_EXTRA, rows);
-        return bundle;
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BlockedNumberUtil.java b/common/device-side/util/src/com/android/compatibility/common/util/BlockedNumberUtil.java
deleted file mode 100644
index e5a0ce4..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BlockedNumberUtil.java
+++ /dev/null
@@ -1,92 +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.
- */
-
-package com.android.compatibility.common.util;
-
-import static com.android.compatibility.common.util.BlockedNumberService.DELETE_ACTION;
-import static com.android.compatibility.common.util.BlockedNumberService.INSERT_ACTION;
-import static com.android.compatibility.common.util.BlockedNumberService.PHONE_NUMBER_EXTRA;
-import static com.android.compatibility.common.util.BlockedNumberService.RESULT_RECEIVER_EXTRA;
-import static com.android.compatibility.common.util.BlockedNumberService.ROWS_EXTRA;
-import static com.android.compatibility.common.util.BlockedNumberService.URI_EXTRA;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.ResultReceiver;
-
-import junit.framework.TestCase;
-
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Utility for starting the blocked number service.
- */
-public class BlockedNumberUtil {
-
-    private static final int TIMEOUT = 2;
-
-    private BlockedNumberUtil() {}
-
-    /** Insert a phone number into the blocked number provider and returns the resulting Uri. */
-    public static Uri insertBlockedNumber(Context context, String phoneNumber) {
-        Intent intent = new Intent(INSERT_ACTION);
-        intent.putExtra(PHONE_NUMBER_EXTRA, phoneNumber);
-
-        return Uri.parse(runBlockedNumberService(context, intent).getString(URI_EXTRA));
-    }
-
-    /** Remove a number from the blocked number provider and returns the number of rows deleted. */
-    public static int deleteBlockedNumber(Context context, Uri uri) {
-        Intent intent = new Intent(DELETE_ACTION);
-        intent.putExtra(URI_EXTRA, uri.toString());
-
-        return runBlockedNumberService(context, intent).getInt(ROWS_EXTRA);
-    }
-
-    /** Start the blocked number service. */
-    static Bundle runBlockedNumberService(Context context, Intent intent) {
-        // Temporarily allow background service
-        SystemUtil.runShellCommand("cmd deviceidle tempwhitelist " + context.getPackageName());
-
-        final Semaphore semaphore = new Semaphore(0);
-        final Bundle result = new Bundle();
-
-        ResultReceiver receiver = new ResultReceiver(new Handler(Looper.getMainLooper())) {
-            @Override
-            protected void onReceiveResult(int resultCode, Bundle resultData) {
-                result.putAll(resultData);
-                semaphore.release();
-            }
-        };
-        intent.putExtra(RESULT_RECEIVER_EXTRA, receiver);
-        intent.setComponent(new ComponentName(context, BlockedNumberService.class));
-
-        context.startService(intent);
-
-        try {
-            TestCase.assertTrue(semaphore.tryAcquire(TIMEOUT, TimeUnit.SECONDS));
-        } catch (InterruptedException e) {
-            TestCase.fail("Timed out waiting for result from BlockedNumberService");
-        }
-        return result;
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BlockingBroadcastReceiver.java b/common/device-side/util/src/com/android/compatibility/common/util/BlockingBroadcastReceiver.java
deleted file mode 100755
index c26ddd0..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BlockingBroadcastReceiver.java
+++ /dev/null
@@ -1,102 +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.compatibility.common.util;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import androidx.annotation.Nullable;
-import android.util.Log;
-
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.TimeUnit;
-
-/**
- * A receiver that allows caller to wait for the broadcast synchronously. Notice that you should not
- * reuse the instance. Usage is typically like this:
- * <pre>
- *     BlockingBroadcastReceiver receiver = new BlockingBroadcastReceiver(context, "action");
- *     try {
- *         receiver.register();
- *         Intent intent = receiver.awaitForBroadcast();
- *         // assert the intent
- *     } finally {
- *         receiver.unregisterQuietly();
- *     }
- * </pre>
- */
-public class BlockingBroadcastReceiver extends BroadcastReceiver {
-    private static final String TAG = "BlockingBroadcast";
-
-    private static final int DEFAULT_TIMEOUT_SECONDS = 30;
-
-    private final BlockingQueue<Intent> mBlockingQueue;
-    private final String mExpectedAction;
-    private final Context mContext;
-
-    public BlockingBroadcastReceiver(Context context, String expectedAction) {
-        mContext = context;
-        mExpectedAction = expectedAction;
-        mBlockingQueue = new ArrayBlockingQueue<>(1);
-    }
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        if (mExpectedAction.equals(intent.getAction())) {
-            mBlockingQueue.add(intent);
-        }
-    }
-
-    public void register() {
-        mContext.registerReceiver(this, new IntentFilter(mExpectedAction));
-    }
-
-    /**
-     * Wait until the broadcast and return the received broadcast intent. {@code null} is returned
-     * if no broadcast with expected action is received within 30 seconds.
-     */
-    public @Nullable Intent awaitForBroadcast() {
-        try {
-            return mBlockingQueue.poll(DEFAULT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
-        } catch (InterruptedException e) {
-            Log.e(TAG, "waitForBroadcast get interrupted: ", e);
-        }
-        return null;
-    }
-
-    /**
-     * Wait until the broadcast and return the received broadcast intent. {@code null} is returned
-     * if no broadcast with expected action is received within the given timeout.
-     */
-    public @Nullable Intent awaitForBroadcast(long timeoutMillis) {
-        try {
-            return mBlockingQueue.poll(timeoutMillis, TimeUnit.MILLISECONDS);
-        } catch (InterruptedException e) {
-            Log.e(TAG, "waitForBroadcast get interrupted: ", e);
-        }
-        return null;
-    }
-
-    public void unregisterQuietly() {
-        try {
-            mContext.unregisterReceiver(this);
-        } catch (Exception ex) {
-            Log.e(TAG, "Failed to unregister BlockingBroadcastReceiver: ", ex);
-        }
-    }
-}
\ No newline at end of file
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BroadcastRpcBase.java b/common/device-side/util/src/com/android/compatibility/common/util/BroadcastRpcBase.java
deleted file mode 100644
index 772e7d3..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BroadcastRpcBase.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * 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 com.android.compatibility.common.util;
-
-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.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.support.test.InstrumentationRegistry;
-import android.util.Log;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * Base class to help broadcast-based RPC.
- */
-public abstract class BroadcastRpcBase<TRequest, TResponse> {
-    private static final String TAG = "BroadcastRpc";
-
-    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
-
-    static final String ACTION_REQUEST = "ACTION_REQUEST";
-    static final String EXTRA_PAYLOAD = "EXTRA_PAYLOAD";
-    static final String EXTRA_EXCEPTION = "EXTRA_EXCEPTION";
-
-    static Handler sMainHandler = new Handler(Looper.getMainLooper());
-
-    /** Implement in a subclass */
-    protected abstract byte[] requestToBytes(TRequest request);
-
-    /** Implement in a subclass */
-    protected abstract TResponse bytesToResponse(byte[] bytes);
-
-    public TResponse invoke(ComponentName targetReceiver, TRequest request) throws Exception {
-        // Create a request intent.
-        Log.i(TAG, "Sending to: " + targetReceiver + (VERBOSE ? "\nRequest: " + request : ""));
-
-        final Intent requestIntent = new Intent(ACTION_REQUEST)
-                .setComponent(targetReceiver)
-                .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
-                .putExtra(EXTRA_PAYLOAD, requestToBytes(request));
-
-        // Send it.
-        final CountDownLatch latch = new CountDownLatch(1);
-        final AtomicReference<Bundle> responseBundle = new AtomicReference<>();
-
-        InstrumentationRegistry.getContext().sendOrderedBroadcast(
-                requestIntent, null, new BroadcastReceiver() {
-                    @Override
-                    public void onReceive(Context context, Intent intent) {
-                        responseBundle.set(getResultExtras(false));
-                        latch.countDown();
-                    }
-                }, sMainHandler, 0, null, null);
-
-        // Wait for a reply and check it.
-        final boolean responseArrived = latch.await(60, TimeUnit.SECONDS);
-        assertTrue("Didn't receive broadcast result.", responseArrived);
-
-        // TODO If responseArrived is false, print if the package / component is installed?
-
-        assertNotNull("Didn't receive result extras", responseBundle.get());
-
-        final String exception = responseBundle.get().getString(EXTRA_EXCEPTION);
-        if (exception != null) {
-            fail("Target throw exception: receiver=" + targetReceiver
-                    + "\nException: " + exception);
-        }
-
-        final byte[] resultPayload = responseBundle.get().getByteArray(EXTRA_PAYLOAD);
-        assertNotNull("Didn't receive result payload", resultPayload);
-
-        Log.i(TAG, "Response received: " + (VERBOSE ? resultPayload.toString() : ""));
-
-        return bytesToResponse(resultPayload);
-    }
-
-    /**
-     * Base class for a receiver for a broadcast-based RPC.
-     */
-    public abstract static class ReceiverBase<TRequest, TResponse> extends BroadcastReceiver {
-        @Override
-        public final void onReceive(Context context, Intent intent) {
-            assertEquals(ACTION_REQUEST, intent.getAction());
-
-            // Parse the request.
-            final TRequest request = bytesToRequest(intent.getByteArrayExtra(EXTRA_PAYLOAD));
-
-            Log.i(TAG, "Request received: " + (VERBOSE ? request.toString() : ""));
-
-            Throwable exception = null;
-
-            // Handle it and generate a response.
-            TResponse response = null;
-            try {
-                response = handleRequest(context, request);
-                Log.i(TAG, "Response generated: " + (VERBOSE ? response.toString() : ""));
-            } catch (Throwable e) {
-                exception = e;
-                Log.e(TAG, "Exception thrown: " + e.getMessage(), e);
-            }
-
-            // Send back.
-            final Bundle extras = new Bundle();
-            if (response != null) {
-                extras.putByteArray(EXTRA_PAYLOAD, responseToBytes(response));
-            }
-            if (exception != null) {
-                extras.putString(EXTRA_EXCEPTION,
-                        exception.toString() + "\n" + Log.getStackTraceString(exception));
-            }
-            setResultExtras(extras);
-        }
-
-        /** Implement in a subclass */
-        protected abstract TResponse handleRequest(Context context, TRequest request)
-                throws Exception;
-
-        /** Implement in a subclass */
-        protected abstract byte[] responseToBytes(TResponse response);
-
-        /** Implement in a subclass */
-        protected abstract TRequest bytesToRequest(byte[] bytes);
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BroadcastTestBase.java b/common/device-side/util/src/com/android/compatibility/common/util/BroadcastTestBase.java
deleted file mode 100644
index 7500050..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BroadcastTestBase.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.compatibility.common.util;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.test.ActivityInstrumentationTestCase2;
-import android.util.Log;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-public class BroadcastTestBase extends ActivityInstrumentationTestCase2<
-                                       BroadcastTestStartActivity> {
-    static final String TAG = "BroadcastTestBase";
-    protected static final int TIMEOUT_MS = 20 * 1000;
-
-    protected Context mContext;
-    protected Bundle mResultExtras;
-    private CountDownLatch mLatch;
-    protected ActivityDoneReceiver mActivityDoneReceiver = null;
-    private BroadcastTestStartActivity mActivity;
-    private BroadcastUtils.TestcaseType mTestCaseType;
-    protected boolean mHasFeature;
-
-    public BroadcastTestBase() {
-        super(BroadcastTestStartActivity.class);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mHasFeature = false;
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        Log.v(TAG, getClass().getSimpleName() + ".tearDown(): hasFeature=" + mHasFeature
-                + " receiver=" + mActivityDoneReceiver);
-        if (mHasFeature && mActivityDoneReceiver != null) {
-            try {
-                mContext.unregisterReceiver(mActivityDoneReceiver);
-            } catch (IllegalArgumentException e) {
-                // This exception is thrown if mActivityDoneReceiver in
-                // the above call to unregisterReceiver is never registered.
-                // If so, no harm done by ignoring this exception.
-            }
-            mActivityDoneReceiver = null;
-        }
-        super.tearDown();
-    }
-
-    protected boolean isIntentSupported(String intentStr) {
-        Intent intent = new Intent(intentStr);
-        final PackageManager manager = mContext.getPackageManager();
-        assertNotNull(manager);
-        if (manager.resolveActivity(intent, 0) == null) {
-            Log.i(TAG, "No Activity found for the intent: " + intentStr);
-            return false;
-        }
-        return true;
-    }
-
-    protected void startTestActivity(String intentSuffix) {
-        Intent intent = new Intent();
-        intent.setAction("android.intent.action.TEST_START_ACTIVITY_" + intentSuffix);
-        intent.setComponent(new ComponentName(getInstrumentation().getContext(),
-                BroadcastTestStartActivity.class));
-        setActivityIntent(intent);
-        mActivity = getActivity();
-    }
-
-    protected void registerBroadcastReceiver(BroadcastUtils.TestcaseType testCaseType) throws Exception {
-        mTestCaseType = testCaseType;
-        mLatch = new CountDownLatch(1);
-        mActivityDoneReceiver = new ActivityDoneReceiver();
-        mContext.registerReceiver(mActivityDoneReceiver,
-                new IntentFilter(BroadcastUtils.BROADCAST_INTENT + testCaseType.toString()));
-    }
-
-    protected boolean startTestAndWaitForBroadcast(BroadcastUtils.TestcaseType testCaseType,
-                                                   String pkg, String cls) throws Exception {
-        Log.i(TAG, "Begin Testing: " + testCaseType);
-        registerBroadcastReceiver(testCaseType);
-        mActivity.startTest(testCaseType.toString(), pkg, cls);
-        if (!mLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
-            fail("Failed to receive broadcast in " + TIMEOUT_MS + "msec");
-            return false;
-        }
-        return true;
-    }
-
-    class ActivityDoneReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (intent.getAction().equals(
-                    BroadcastUtils.BROADCAST_INTENT +
-                        BroadcastTestBase.this.mTestCaseType.toString())) {
-                Bundle extras = intent.getExtras();
-                Log.i(TAG, "received_broadcast for " + BroadcastUtils.toBundleString(extras));
-                BroadcastTestBase.this.mResultExtras = extras;
-                mLatch.countDown();
-            }
-        }
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BroadcastTestStartActivity.java b/common/device-side/util/src/com/android/compatibility/common/util/BroadcastTestStartActivity.java
deleted file mode 100644
index 4b3e85d..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BroadcastTestStartActivity.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.compatibility.common.util;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.content.ComponentName;
-import android.os.Bundle;
-import android.util.Log;
-
-public class BroadcastTestStartActivity extends Activity {
-    static final String TAG = "BroadcastTestStartActivity";
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        Log.i(TAG, " in onCreate");
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        Log.i(TAG, " in onResume");
-    }
-
-    void startTest(String testCaseType, String pkg, String cls) {
-        Intent intent = new Intent();
-        Log.i(TAG, "received_testcasetype = " + testCaseType);
-        intent.putExtra(BroadcastUtils.TESTCASE_TYPE, testCaseType);
-        intent.setAction("android.intent.action.VIMAIN_" + testCaseType);
-        intent.setComponent(new ComponentName(pkg, cls));
-        startActivity(intent);
-    }
-
-    @Override
-    protected void onPause() {
-        Log.i(TAG, " in onPause");
-        super.onPause();
-    }
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-        Log.i(TAG, " in onStart");
-    }
-
-    @Override
-    protected void onRestart() {
-        super.onRestart();
-        Log.i(TAG, " in onRestart");
-    }
-
-    @Override
-    protected void onStop() {
-        Log.i(TAG, " in onStop");
-        super.onStop();
-    }
-
-    @Override
-    protected void onDestroy() {
-        Log.i(TAG, " in onDestroy");
-        super.onDestroy();
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BroadcastUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/BroadcastUtils.java
deleted file mode 100644
index a4661fc..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BroadcastUtils.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.
- */
-package com.android.compatibility.common.util;
-
-import android.os.Bundle;
-
-public class BroadcastUtils {
-    public enum TestcaseType {
-        ZEN_MODE_ON,
-        ZEN_MODE_OFF,
-        AIRPLANE_MODE_ON,
-        AIRPLANE_MODE_OFF,
-        BATTERYSAVER_MODE_ON,
-        BATTERYSAVER_MODE_OFF,
-        THEATER_MODE_ON,
-        THEATER_MODE_OFF
-    }
-    public static final String TESTCASE_TYPE = "Testcase_type";
-    public static final String BROADCAST_INTENT =
-            "android.intent.action.FROM_UTIL_CTS_TEST_";
-    public static final int NUM_MINUTES_FOR_ZENMODE = 10;
-
-    public static final String toBundleString(Bundle bundle) {
-        if (bundle == null) {
-            return "*** Bundle is null ****";
-        }
-        StringBuilder buf = new StringBuilder();
-        if (bundle != null) {
-            buf.append("extras: ");
-            for (String s : bundle.keySet()) {
-                buf.append("(" + s + " = " + bundle.get(s) + "), ");
-            }
-        }
-        return buf.toString();
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BundleUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/BundleUtils.java
deleted file mode 100644
index eda641d..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BundleUtils.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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 com.android.compatibility.common.util;
-
-import android.os.Bundle;
-
-public class BundleUtils {
-    private BundleUtils() {
-    }
-
-    public static Bundle makeBundle(Object... keysAndValues) {
-        if ((keysAndValues.length % 2) != 0) {
-            throw new IllegalArgumentException("Argument count not even.");
-        }
-
-        if (keysAndValues.length == 0) {
-            return null;
-        }
-        final Bundle ret = new Bundle();
-
-        for (int i = keysAndValues.length - 2; i >= 0; i -= 2) {
-            final String key = keysAndValues[i].toString();
-            final Object value = keysAndValues[i + 1];
-
-            if (value == null) {
-                ret.putString(key, null);
-
-            } else if (value instanceof Boolean) {
-                ret.putBoolean(key, (Boolean) value);
-
-            } else if (value instanceof Integer) {
-                ret.putInt(key, (Integer) value);
-
-            } else if (value instanceof String) {
-                ret.putString(key, (String) value);
-
-            } else if (value instanceof Bundle) {
-                ret.putBundle(key, (Bundle) value);
-            } else {
-                throw new IllegalArgumentException(
-                        "Type not supported yet: " + value.getClass().getName());
-            }
-        }
-        return ret;
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicDeviceExecutor.java b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicDeviceExecutor.java
deleted file mode 100644
index 7d7aaf0..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicDeviceExecutor.java
+++ /dev/null
@@ -1,117 +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.compatibility.common.util;
-
-import android.content.Context;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Execute business logic methods for device side test cases
- */
-public class BusinessLogicDeviceExecutor extends BusinessLogicExecutor {
-
-    private Context mContext;
-    private Object mTestObj;
-
-    public BusinessLogicDeviceExecutor(Context context, Object testObj) {
-        mContext = context;
-        mTestObj = testObj;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected Object getTestObject() {
-        return mTestObj;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void logInfo(String format, Object... args) {
-        Log.i(LOG_TAG, String.format(format, args));
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void logDebug(String format, Object... args) {
-        Log.d(LOG_TAG, String.format(format, args));
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected String formatExecutionString(String method, String... args) {
-        return String.format("%s(%s)", method, TextUtils.join(", ", args));
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected ResolvedMethod getResolvedMethod(Class cls, String methodName, String... args)
-            throws ClassNotFoundException {
-        List<Method> nameMatches = getMethodsWithName(cls, methodName);
-        for (Method m : nameMatches) {
-            ResolvedMethod rm = new ResolvedMethod(m);
-            int paramTypesMatched = 0;
-            int argsUsed = 0;
-            Class[] paramTypes = m.getParameterTypes();
-            for (Class paramType : paramTypes) {
-                if (argsUsed == args.length && paramType.equals(String.class)) {
-                    // We've used up all supplied string args, so this method will not match.
-                    // If paramType is the Context class, we can match a paramType without needing
-                    // more string args. similarly, paramType "String[]" can be matched with zero
-                    // string args. If we add support for more paramTypes, this logic may require
-                    // adjustment.
-                    break;
-                }
-                if (paramType.equals(String.class)) {
-                    // Type "String" -- supply the next available arg
-                    rm.addArg(args[argsUsed++]);
-                } else if (Context.class.isAssignableFrom(paramType)) {
-                    // Type "Context" -- supply the context from the test case
-                    rm.addArg(mContext);
-                } else if (paramType.equals(Class.forName(STRING_ARRAY_CLASS))) {
-                    // Type "String[]" (or "String...") -- supply all remaining args
-                    rm.addArg(Arrays.copyOfRange(args, argsUsed, args.length));
-                    argsUsed += (args.length - argsUsed);
-                } else {
-                    break; // Param type is unrecognized, this method will not match.
-                }
-                paramTypesMatched++; // A param type has been matched when reaching this point.
-            }
-            if (paramTypesMatched == paramTypes.length && argsUsed == args.length) {
-                return rm; // Args match, methods match, so return the first method-args pairing.
-            }
-            // Not a match, try args for next method that matches by name.
-        }
-        throw new RuntimeException(String.format(
-                "BusinessLogic: Failed to invoke action method %s with args: %s", methodName,
-                Arrays.toString(args)));
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java
deleted file mode 100644
index d567a5f..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java
+++ /dev/null
@@ -1,137 +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.compatibility.common.util;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
-
-import android.app.Instrumentation;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.support.test.InstrumentationRegistry;
-import android.util.Log;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.rules.TestName;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.lang.reflect.Field;
-import java.util.Map;
-
-/**
- * Device-side base class for tests leveraging the Business Logic service.
- */
-public class BusinessLogicTestCase {
-    private static final String TAG = "BusinessLogicTestCase";
-
-    /* String marking the beginning of the parameter in a test name */
-    private static final String PARAM_START = "[";
-
-    public static final String CONTENT_PROVIDER =
-            String.format("%s://android.tradefed.contentprovider", ContentResolver.SCHEME_CONTENT);
-
-    /* Test name rule that tracks the current test method under execution */
-    @Rule public TestName mTestCase = new TestName();
-
-    protected BusinessLogic mBusinessLogic;
-    protected boolean mCanReadBusinessLogic = true;
-
-    @Before
-    public void handleBusinessLogic() {
-        loadBusinessLogic();
-        executeBusinessLogic();
-    }
-
-    protected void executeBusinessLogic() {
-        String methodName = mTestCase.getMethodName();
-        assertTrue(String.format("Test \"%s\" is unable to execute as it depends on the missing "
-                + "remote configuration.", methodName), mCanReadBusinessLogic);
-        if (methodName.contains(PARAM_START)) {
-            // Strip parameter suffix (e.g. "[0]") from method name
-            methodName = methodName.substring(0, methodName.lastIndexOf(PARAM_START));
-        }
-        String testName = String.format("%s#%s", this.getClass().getName(), methodName);
-        if (mBusinessLogic.hasLogicFor(testName)) {
-            Log.i(TAG, "Finding business logic for test case: " + testName);
-            BusinessLogicExecutor executor = new BusinessLogicDeviceExecutor(getContext(), this);
-            mBusinessLogic.applyLogicFor(testName, executor);
-        }
-    }
-
-    protected void loadBusinessLogic() {
-        String uriPath = String.format("%s/%s", CONTENT_PROVIDER, BusinessLogic.DEVICE_FILE);
-        Uri sdcardUri = Uri.parse(uriPath);
-        Context appContext = InstrumentationRegistry.getTargetContext();
-        try {
-            ContentResolver resolver = appContext.getContentResolver();
-            ParcelFileDescriptor descriptor = resolver.openFileDescriptor(sdcardUri, "r");
-            mBusinessLogic = BusinessLogicFactory.createFromFile(
-                    new ParcelFileDescriptor.AutoCloseInputStream(descriptor));
-            return;
-        } catch (FileNotFoundException e) {
-            // Log the error and use the fallback too
-            Log.e(TAG, "Error while using content provider for config", e);
-        }
-        // Fallback to reading the business logic directly.
-        File businessLogicFile = new File(BusinessLogic.DEVICE_FILE);
-        if (businessLogicFile.canRead()) {
-            mBusinessLogic = BusinessLogicFactory.createFromFile(businessLogicFile);
-        } else {
-            mCanReadBusinessLogic = false;
-        }
-    }
-
-    protected static Instrumentation getInstrumentation() {
-        return InstrumentationRegistry.getInstrumentation();
-    }
-
-    protected static Context getContext() {
-        return getInstrumentation().getTargetContext();
-    }
-
-    public static void skipTest(String message) {
-        assumeTrue(message, false);
-    }
-
-    public static void failTest(String message) {
-        fail(message);
-    }
-
-    public void mapPut(String mapName, String key, String value) {
-        boolean put = false;
-        for (Field f : getClass().getDeclaredFields()) {
-            if (f.getName().equalsIgnoreCase(mapName) && Map.class.isAssignableFrom(f.getType())) {
-                try {
-                    ((Map) f.get(this)).put(key, value);
-                    put = true;
-                } catch (IllegalAccessException e) {
-                    Log.w(String.format("failed to invoke mapPut on field \"%s\". Resuming...",
-                            f.getName()), e);
-                    // continue iterating through fields, throw exception if no other fields match
-                }
-            }
-        }
-        if (!put) {
-            throw new RuntimeException(String.format("Failed to find map %s in class %s", mapName,
-                    getClass().getName()));
-        }
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/CTSResult.java b/common/device-side/util/src/com/android/compatibility/common/util/CTSResult.java
deleted file mode 100644
index d60155a..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/CTSResult.java
+++ /dev/null
@@ -1,23 +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.
- */
-
-package com.android.compatibility.common.util;
-
-public interface CTSResult {
-    public static final int RESULT_OK = 1;
-    public static final int RESULT_FAIL = 2;
-    public void setResult(int resultCode);
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/CallbackAsserter.java b/common/device-side/util/src/com/android/compatibility/common/util/CallbackAsserter.java
deleted file mode 100644
index 436161a..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/CallbackAsserter.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * 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 com.android.compatibility.common.util;
-
-import static junit.framework.Assert.fail;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.support.test.InstrumentationRegistry;
-import android.util.Log;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Predicate;
-
-/**
- * CallbackAsserter helps wait until a callback is called.
- */
-public class CallbackAsserter {
-    private static final String TAG = "CallbackAsserter";
-
-    final CountDownLatch mLatch = new CountDownLatch(1);
-
-    CallbackAsserter() {
-    }
-
-    /**
-     * Call this to assert a callback be called within the given timeout.
-     */
-    public final void assertCalled(String message, int timeoutSeconds) throws Exception {
-        try {
-            if (mLatch.await(timeoutSeconds, TimeUnit.SECONDS)) {
-                return;
-            }
-            fail("Didn't receive callback: " + message);
-        } finally {
-            cleanUp();
-        }
-    }
-
-    void cleanUp() {
-    }
-
-    /**
-     * Create an instance for a broadcast.
-     */
-    public static CallbackAsserter forBroadcast(IntentFilter filter) {
-        return forBroadcast(filter, null);
-    }
-
-    /**
-     * Create an instance for a broadcast.
-     */
-    public static CallbackAsserter forBroadcast(IntentFilter filter, Predicate<Intent> checker) {
-        return new BroadcastAsserter(filter, checker);
-    }
-
-    /**
-     * Create an instance for a content changed notification.
-     */
-    public static CallbackAsserter forContentUri(Uri watchUri) {
-        return forContentUri(watchUri, null);
-    }
-
-    /**
-     * Create an instance for a content changed notification.
-     */
-    public static CallbackAsserter forContentUri(Uri watchUri, Predicate<Uri> checker) {
-        return new ContentObserverAsserter(watchUri, checker);
-    }
-
-    private static class BroadcastAsserter extends CallbackAsserter {
-        private final BroadcastReceiver mReceiver;
-
-        BroadcastAsserter(IntentFilter filter, Predicate<Intent> checker) {
-            mReceiver = new BroadcastReceiver() {
-                @Override
-                public void onReceive(Context context, Intent intent) {
-                    if (checker != null && !checker.test(intent)) {
-                        Log.v(TAG, "Ignoring intent: " + intent);
-                        return;
-                    }
-                    mLatch.countDown();
-                }
-            };
-            InstrumentationRegistry.getContext().registerReceiver(mReceiver, filter);
-        }
-
-        @Override
-        void cleanUp() {
-            InstrumentationRegistry.getContext().unregisterReceiver(mReceiver);
-        }
-    }
-
-    private static class ContentObserverAsserter extends CallbackAsserter {
-        private final ContentObserver mObserver;
-
-        ContentObserverAsserter(Uri watchUri, Predicate<Uri> checker) {
-            mObserver = new ContentObserver(null) {
-                @Override
-                public void onChange(boolean selfChange, Uri uri) {
-                    if (checker != null && !checker.test(uri)) {
-                        Log.v(TAG, "Ignoring notification on URI: " + uri);
-                        return;
-                    }
-                    mLatch.countDown();
-                }
-            };
-            InstrumentationRegistry.getContext().getContentResolver().registerContentObserver(
-                    watchUri, true, mObserver);
-        }
-
-        @Override
-        void cleanUp() {
-            InstrumentationRegistry.getContext().getContentResolver().unregisterContentObserver(
-                    mObserver);
-        }
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ColorUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/ColorUtils.java
deleted file mode 100644
index c0da13d..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ColorUtils.java
+++ /dev/null
@@ -1,117 +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.compatibility.common.util;
-
-import static org.junit.Assert.fail;
-
-import android.graphics.Color;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import java.util.function.Function;
-import java.util.function.IntUnaryOperator;
-
-public class ColorUtils {
-    public static void verifyColor(int expected, int observed) {
-        verifyColor(expected, observed, 0);
-    }
-
-    public static void verifyColor(int expected, int observed, int tolerance) {
-        verifyColor("", expected, observed, tolerance);
-    }
-
-    /**
-     * Verify that two colors match within a per-channel tolerance.
-     *
-     * @param s String with extra information about the test with an error.
-     * @param expected Expected color.
-     * @param observed Observed color.
-     * @param tolerance Per-channel tolerance by which the color can mismatch.
-     */
-    public static void verifyColor(@NonNull String s, int expected, int observed, int tolerance) {
-        s += " expected 0x" + Integer.toHexString(expected)
-            + ", observed 0x" + Integer.toHexString(observed)
-            + ", tolerated channel error 0x" + tolerance;
-        String red = verifyChannel("red", expected, observed, tolerance, (i) -> Color.red(i));
-        String green = verifyChannel("green", expected, observed, tolerance, (i) -> Color.green(i));
-        String blue = verifyChannel("blue", expected, observed, tolerance, (i) -> Color.blue(i));
-        String alpha = verifyChannel("alpha", expected, observed, tolerance, (i) -> Color.alpha(i));
-
-        buildErrorString(s, red, green, blue, alpha);
-    }
-
-    private static void buildErrorString(@NonNull String s, @Nullable String red,
-            @Nullable String green, @Nullable String blue, @Nullable String alpha) {
-        String err = null;
-        for (String channel : new String[]{red, green, blue, alpha}) {
-            if (channel == null) continue;
-            if (err == null) err = s;
-            err += "\n\t\t" + channel;
-        }
-        if (err != null) {
-            fail(err);
-        }
-    }
-
-    private static String verifyChannel(String channelName, int expected, int observed,
-            int tolerance, IntUnaryOperator f) {
-        int e = f.applyAsInt(expected);
-        int o = f.applyAsInt(observed);
-        if (Math.abs(e - o) <= tolerance) {
-            return null;
-        }
-        return "Channel " + channelName + " mismatch: expected<0x" + Integer.toHexString(e)
-            + ">, observed: <0x" + Integer.toHexString(o) + ">";
-    }
-
-    /**
-     * Verify that two colors match within a per-channel tolerance.
-     *
-     * @param msg String with extra information about the test with an error.
-     * @param expected Expected color.
-     * @param observed Observed color.
-     * @param tolerance Per-channel tolerance by which the color can mismatch.
-     */
-    public static void verifyColor(@NonNull String msg, Color expected, Color observed,
-            float tolerance) {
-        if (!expected.getColorSpace().equals(observed.getColorSpace())) {
-            fail("Cannot compare Colors with different color spaces! expected: " + expected
-                    + "\tobserved: " + observed);
-        }
-        msg += " expected " + expected + ", observed " + observed + ", tolerated channel error "
-            + tolerance;
-        String red = verifyChannel("red", expected, observed, tolerance, (c) -> c.red());
-        String green = verifyChannel("green", expected, observed, tolerance, (c) -> c.green());
-        String blue = verifyChannel("blue", expected, observed, tolerance, (c) -> c.blue());
-        String alpha = verifyChannel("alpha", expected, observed, tolerance, (c) -> c.alpha());
-
-        buildErrorString(msg, red, green, blue, alpha);
-    }
-
-    private static String verifyChannel(String channelName, Color expected, Color observed,
-            float tolerance, Function<Color, Float> f) {
-        float e = f.apply(expected);
-        float o = f.apply(observed);
-        float diff = Math.abs(e - o);
-        if (diff <= tolerance) {
-            return null;
-        }
-        return "Channel " + channelName + " mismatch: expected<" + e + ">, observed: <" + o
-            + ">, difference: <" + diff + ">";
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ConnectivityUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/ConnectivityUtils.java
deleted file mode 100644
index 09a0a85..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ConnectivityUtils.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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 com.android.compatibility.common.util;
-
-import static org.junit.Assert.assertTrue;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-
-public class ConnectivityUtils {
-    private ConnectivityUtils() {
-    }
-
-    /** @return true when the device has a network connection. */
-    public static boolean isNetworkConnected(Context context) {
-        final NetworkInfo networkInfo = context.getSystemService(ConnectivityManager.class)
-                .getActiveNetworkInfo();
-        return (networkInfo != null) && networkInfo.isConnected();
-    }
-
-    /** Assert that the device has a network connection. */
-    public static void assertNetworkConnected(Context context) {
-        assertTrue("Network must be connected", isNetworkConnected(context));
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/CpuFeatures.java b/common/device-side/util/src/com/android/compatibility/common/util/CpuFeatures.java
deleted file mode 100644
index 9360942..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/CpuFeatures.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.compatibility.common.util;
-
-import android.os.Build;
-
-public class CpuFeatures {
-
-    public static final String ARMEABI_V7 = "armeabi-v7a";
-
-    public static final String ARMEABI = "armeabi";
-
-    public static final String MIPSABI = "mips";
-
-    public static final  String X86ABI = "x86";
-
-    public static final int HWCAP_VFP = (1 << 6);
-
-    public static final int HWCAP_NEON = (1 << 12);
-
-    public static final int HWCAP_VFPv3 = (1 << 13);
-
-    public static final int HWCAP_VFPv4 = (1 << 16);
-
-    public static final int HWCAP_IDIVA = (1 << 17);
-
-    public static final int HWCAP_IDIVT = (1 << 18);
-
-    static {
-        System.loadLibrary("cts_jni");
-    }
-
-    public static native boolean isArmCpu();
-
-    public static native boolean isMipsCpu();
-
-    public static native boolean isX86Cpu();
-
-    public static native boolean isArm64Cpu();
-
-    public static native boolean isMips64Cpu();
-
-    public static native boolean isX86_64Cpu();
-
-    public static native int getHwCaps();
-
-    public static boolean isArm64CpuIn32BitMode() {
-        if (!isArmCpu()) {
-            return false;
-        }
-
-        for (String abi : Build.SUPPORTED_64_BIT_ABIS) {
-            if (abi.equals("arm64-v8a")) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/CtsAndroidTestCase.java b/common/device-side/util/src/com/android/compatibility/common/util/CtsAndroidTestCase.java
deleted file mode 100644
index 1ffad1d..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/CtsAndroidTestCase.java
+++ /dev/null
@@ -1,36 +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.compatibility.common.util;
-
-import android.content.Context;
-import android.test.ActivityInstrumentationTestCase2;
-
-/**
- *  This class emulates AndroidTestCase, but internally it is ActivityInstrumentationTestCase2
- *  to access Instrumentation.
- *  DummyActivity is not supposed to be accessed.
- */
-public class CtsAndroidTestCase extends ActivityInstrumentationTestCase2<DummyActivity> {
-    public CtsAndroidTestCase() {
-        super(DummyActivity.class);
-    }
-
-    public Context getContext() {
-        return getInstrumentation().getContext();
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/CtsKeyEventUtil.java b/common/device-side/util/src/com/android/compatibility/common/util/CtsKeyEventUtil.java
deleted file mode 100644
index 97e4310..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/CtsKeyEventUtil.java
+++ /dev/null
@@ -1,293 +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.compatibility.common.util;
-
-import android.app.Instrumentation;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.InputDevice;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.inputmethod.InputMethodManager;
-
-import java.lang.reflect.Field;
-
-/**
- * Utility class to send KeyEvents bypassing the IME. The code is similar to functions in
- * {@link Instrumentation} and {@link android.test.InstrumentationTestCase} classes. It uses
- * {@link InputMethodManager#dispatchKeyEventFromInputMethod(View, KeyEvent)} to send the events.
- * After sending the events waits for idle.
- */
-public final class CtsKeyEventUtil {
-
-    private CtsKeyEventUtil() {}
-
-    /**
-     * Sends the key events corresponding to the text to the app being instrumented.
-     *
-     * @param instrumentation the instrumentation used to run the test.
-     * @param targetView View to find the ViewRootImpl and dispatch.
-     * @param text The text to be sent. Null value returns immediately.
-     */
-    public static void sendString(final Instrumentation instrumentation, final View targetView,
-            final String text) {
-        if (text == null) {
-            return;
-        }
-
-        KeyEvent[] events = getKeyEvents(text);
-
-        if (events != null) {
-            for (int i = 0; i < events.length; i++) {
-                // We have to change the time of an event before injecting it because
-                // all KeyEvents returned by KeyCharacterMap.getEvents() have the same
-                // time stamp and the system rejects too old events. Hence, it is
-                // possible for an event to become stale before it is injected if it
-                // takes too long to inject the preceding ones.
-                sendKey(instrumentation, targetView, KeyEvent.changeTimeRepeat(
-                        events[i], SystemClock.uptimeMillis(), 0 /* newRepeat */));
-            }
-        }
-    }
-
-    /**
-     * Sends a series of key events through instrumentation. For instance:
-     * sendKeys(view, KEYCODE_DPAD_LEFT, KEYCODE_DPAD_CENTER).
-     *
-     * @param instrumentation the instrumentation used to run the test.
-     * @param targetView View to find the ViewRootImpl and dispatch.
-     * @param keys The series of key codes.
-     */
-    public static void sendKeys(final Instrumentation instrumentation, final View targetView,
-            final int...keys) {
-        final int count = keys.length;
-
-        for (int i = 0; i < count; i++) {
-            try {
-                sendKeyDownUp(instrumentation, targetView, keys[i]);
-            } catch (SecurityException e) {
-                // Ignore security exceptions that are now thrown
-                // when trying to send to another app, to retain
-                // compatibility with existing tests.
-            }
-        }
-    }
-
-    /**
-     * Sends a series of key events through instrumentation. The sequence of keys is a string
-     * containing the key names as specified in KeyEvent, without the KEYCODE_ prefix. For
-     * instance: sendKeys(view, "DPAD_LEFT A B C DPAD_CENTER"). Each key can be repeated by using
-     * the N* prefix. For instance, to send two KEYCODE_DPAD_LEFT, use the following:
-     * sendKeys(view, "2*DPAD_LEFT").
-     *
-     * @param instrumentation the instrumentation used to run the test.
-     * @param targetView View to find the ViewRootImpl and dispatch.
-     * @param keysSequence The sequence of keys.
-     */
-    public static void sendKeys(final Instrumentation instrumentation, final View targetView,
-            final String keysSequence) {
-        final String[] keys = keysSequence.split(" ");
-        final int count = keys.length;
-
-        for (int i = 0; i < count; i++) {
-            String key = keys[i];
-            int repeater = key.indexOf('*');
-
-            int keyCount;
-            try {
-                keyCount = repeater == -1 ? 1 : Integer.parseInt(key.substring(0, repeater));
-            } catch (NumberFormatException e) {
-                Log.w("ActivityTestCase", "Invalid repeat count: " + key);
-                continue;
-            }
-
-            if (repeater != -1) {
-                key = key.substring(repeater + 1);
-            }
-
-            for (int j = 0; j < keyCount; j++) {
-                try {
-                    final Field keyCodeField = KeyEvent.class.getField("KEYCODE_" + key);
-                    final int keyCode = keyCodeField.getInt(null);
-                    try {
-                        sendKeyDownUp(instrumentation, targetView, keyCode);
-                    } catch (SecurityException e) {
-                        // Ignore security exceptions that are now thrown
-                        // when trying to send to another app, to retain
-                        // compatibility with existing tests.
-                    }
-                } catch (NoSuchFieldException e) {
-                    Log.w("ActivityTestCase", "Unknown keycode: KEYCODE_" + key);
-                    break;
-                } catch (IllegalAccessException e) {
-                    Log.w("ActivityTestCase", "Unknown keycode: KEYCODE_" + key);
-                    break;
-                }
-            }
-        }
-    }
-
-    /**
-     * Sends an up and down key events.
-     *
-     * @param instrumentation the instrumentation used to run the test.
-     * @param targetView View to find the ViewRootImpl and dispatch.
-     * @param key The integer keycode for the event to be sent.
-     */
-    public static void sendKeyDownUp(final Instrumentation instrumentation, final View targetView,
-            final int key) {
-        sendKey(instrumentation, targetView, new KeyEvent(KeyEvent.ACTION_DOWN, key));
-        sendKey(instrumentation, targetView, new KeyEvent(KeyEvent.ACTION_UP, key));
-    }
-
-    /**
-     * Sends a key event.
-     *
-     * @param instrumentation the instrumentation used to run the test.
-     * @param targetView View to find the ViewRootImpl and dispatch.
-     * @param event KeyEvent to be send.
-     */
-    public static void sendKey(final Instrumentation instrumentation, final View targetView,
-            final KeyEvent event) {
-        validateNotAppThread();
-
-        long downTime = event.getDownTime();
-        long eventTime = event.getEventTime();
-        int action = event.getAction();
-        int code = event.getKeyCode();
-        int repeatCount = event.getRepeatCount();
-        int metaState = event.getMetaState();
-        int deviceId = event.getDeviceId();
-        int scanCode = event.getScanCode();
-        int source = event.getSource();
-        int flags = event.getFlags();
-        if (source == InputDevice.SOURCE_UNKNOWN) {
-            source = InputDevice.SOURCE_KEYBOARD;
-        }
-        if (eventTime == 0) {
-            eventTime = SystemClock.uptimeMillis();
-        }
-        if (downTime == 0) {
-            downTime = eventTime;
-        }
-
-        final KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount,
-                metaState, deviceId, scanCode, flags, source);
-
-        InputMethodManager imm = targetView.getContext().getSystemService(InputMethodManager.class);
-        imm.dispatchKeyEventFromInputMethod(null, newEvent);
-        instrumentation.waitForIdleSync();
-    }
-
-    /**
-     * Sends a key event while holding another modifier key down, then releases both keys and
-     * waits for idle sync. Useful for sending combinations like shift + tab.
-     *
-     * @param instrumentation the instrumentation used to run the test.
-     * @param targetView View to find the ViewRootImpl and dispatch.
-     * @param keyCodeToSend The integer keycode for the event to be sent.
-     * @param modifierKeyCodeToHold The integer keycode of the modifier to be held.
-     */
-    public static void sendKeyWhileHoldingModifier(final Instrumentation instrumentation,
-            final View targetView, final int keyCodeToSend,
-            final int modifierKeyCodeToHold) {
-        final int metaState = getMetaStateForModifierKeyCode(modifierKeyCodeToHold);
-        final long downTime = SystemClock.uptimeMillis();
-
-        final KeyEvent holdKeyDown = new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
-                modifierKeyCodeToHold, 0 /* repeat */);
-        sendKey(instrumentation ,targetView, holdKeyDown);
-
-        final KeyEvent keyDown = new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
-                keyCodeToSend, 0 /* repeat */, metaState);
-        sendKey(instrumentation, targetView, keyDown);
-
-        final KeyEvent keyUp = new KeyEvent(downTime, downTime, KeyEvent.ACTION_UP,
-                keyCodeToSend, 0 /* repeat */, metaState);
-        sendKey(instrumentation, targetView, keyUp);
-
-        final KeyEvent holdKeyUp = new KeyEvent(downTime, downTime, KeyEvent.ACTION_UP,
-                modifierKeyCodeToHold, 0 /* repeat */);
-        sendKey(instrumentation, targetView, holdKeyUp);
-
-        instrumentation.waitForIdleSync();
-    }
-
-    private static int getMetaStateForModifierKeyCode(int modifierKeyCode) {
-        if (!KeyEvent.isModifierKey(modifierKeyCode)) {
-            throw new IllegalArgumentException("Modifier key expected, but got: "
-                    + KeyEvent.keyCodeToString(modifierKeyCode));
-        }
-
-        int metaState;
-        switch (modifierKeyCode) {
-            case KeyEvent.KEYCODE_SHIFT_LEFT:
-                metaState = KeyEvent.META_SHIFT_LEFT_ON;
-                break;
-            case KeyEvent.KEYCODE_SHIFT_RIGHT:
-                metaState = KeyEvent.META_SHIFT_RIGHT_ON;
-                break;
-            case KeyEvent.KEYCODE_ALT_LEFT:
-                metaState = KeyEvent.META_ALT_LEFT_ON;
-                break;
-            case KeyEvent.KEYCODE_ALT_RIGHT:
-                metaState = KeyEvent.META_ALT_RIGHT_ON;
-                break;
-            case KeyEvent.KEYCODE_CTRL_LEFT:
-                metaState = KeyEvent.META_CTRL_LEFT_ON;
-                break;
-            case KeyEvent.KEYCODE_CTRL_RIGHT:
-                metaState = KeyEvent.META_CTRL_RIGHT_ON;
-                break;
-            case KeyEvent.KEYCODE_META_LEFT:
-                metaState = KeyEvent.META_META_LEFT_ON;
-                break;
-            case KeyEvent.KEYCODE_META_RIGHT:
-                metaState = KeyEvent.META_META_RIGHT_ON;
-                break;
-            case KeyEvent.KEYCODE_SYM:
-                metaState = KeyEvent.META_SYM_ON;
-                break;
-            case KeyEvent.KEYCODE_NUM:
-                metaState = KeyEvent.META_NUM_LOCK_ON;
-                break;
-            case KeyEvent.KEYCODE_FUNCTION:
-                metaState = KeyEvent.META_FUNCTION_ON;
-                break;
-            default:
-                // Safety net: all modifier keys need to have at least one meta state associated.
-                throw new UnsupportedOperationException("No meta state associated with "
-                        + "modifier key: " + KeyEvent.keyCodeToString(modifierKeyCode));
-        }
-
-        return KeyEvent.normalizeMetaState(metaState);
-    }
-
-    private static KeyEvent[] getKeyEvents(final String text) {
-        KeyCharacterMap keyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
-        return keyCharacterMap.getEvents(text.toCharArray());
-    }
-
-    private static void validateNotAppThread() {
-        if (Looper.myLooper() == Looper.getMainLooper()) {
-            throw new RuntimeException(
-                    "This method can not be called from the main application thread");
-        }
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/CtsMockitoUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/CtsMockitoUtils.java
deleted file mode 100644
index 54985dc..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/CtsMockitoUtils.java
+++ /dev/null
@@ -1,26 +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.compatibility.common.util;
-
-import org.mockito.verification.VerificationMode;
-
-public class CtsMockitoUtils {
-    private CtsMockitoUtils() {}
-
-    public static VerificationMode within(long timeout) {
-        return new Within(timeout);
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/CtsMouseUtil.java b/common/device-side/util/src/com/android/compatibility/common/util/CtsMouseUtil.java
deleted file mode 100644
index e53eb08..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/CtsMouseUtil.java
+++ /dev/null
@@ -1,162 +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.compatibility.common.util;
-
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-
-import android.app.Instrumentation;
-import android.app.UiAutomation;
-import android.os.SystemClock;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.View;
-
-import org.mockito.ArgumentMatcher;
-import org.mockito.InOrder;
-
-public final class CtsMouseUtil {
-
-    private CtsMouseUtil() {}
-
-    public static View.OnHoverListener installHoverListener(View view) {
-        return installHoverListener(view, true);
-    }
-
-    public static View.OnHoverListener installHoverListener(View view, boolean result) {
-        final View.OnHoverListener mockListener = mock(View.OnHoverListener.class);
-        view.setOnHoverListener((v, event) -> {
-            // Clone the event to work around event instance reuse in the framework.
-            mockListener.onHover(v, MotionEvent.obtain(event));
-            return result;
-        });
-        return mockListener;
-    }
-
-    public static void clearHoverListener(View view) {
-        view.setOnHoverListener(null);
-    }
-
-    public static MotionEvent obtainMouseEvent(int action, View anchor, int offsetX, int offsetY) {
-        final long eventTime = SystemClock.uptimeMillis();
-        final int[] screenPos = new int[2];
-        anchor.getLocationOnScreen(screenPos);
-        final int x = screenPos[0] + offsetX;
-        final int y = screenPos[1] + offsetY;
-        MotionEvent event = MotionEvent.obtain(eventTime, eventTime, action, x, y, 0);
-        event.setSource(InputDevice.SOURCE_MOUSE);
-        return event;
-    }
-
-    /**
-     * Emulates a hover move on a point relative to the top-left corner of the passed {@link View}.
-     * Offset parameters are used to compute the final screen coordinates of the tap point.
-     *
-     * @param instrumentation the instrumentation used to run the test
-     * @param anchor the anchor view to determine the tap location on the screen
-     * @param offsetX extra X offset for the move
-     * @param offsetY extra Y offset for the move
-     */
-    public static void emulateHoverOnView(Instrumentation instrumentation, View anchor, int offsetX,
-            int offsetY) {
-        final long downTime = SystemClock.uptimeMillis();
-        final UiAutomation uiAutomation = instrumentation.getUiAutomation();
-        final int[] screenPos = new int[2];
-        anchor.getLocationOnScreen(screenPos);
-        final int x = screenPos[0] + offsetX;
-        final int y = screenPos[1] + offsetY;
-
-        injectHoverEvent(uiAutomation, downTime, x, y);
-    }
-
-    private static void injectHoverEvent(UiAutomation uiAutomation, long downTime, int xOnScreen,
-            int yOnScreen) {
-        MotionEvent event = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_HOVER_MOVE,
-                xOnScreen, yOnScreen, 0);
-        event.setSource(InputDevice.SOURCE_MOUSE);
-        uiAutomation.injectInputEvent(event, true);
-        event.recycle();
-    }
-
-    public static class ActionMatcher implements ArgumentMatcher<MotionEvent> {
-        private final int mAction;
-
-        public ActionMatcher(int action) {
-            mAction = action;
-        }
-
-        @Override
-        public boolean matches(MotionEvent actual) {
-            return actual.getAction() == mAction;
-        }
-
-        @Override
-        public String toString() {
-            return "action=" + MotionEvent.actionToString(mAction);
-        }
-    }
-
-    public static class PositionMatcher extends ActionMatcher {
-        private final int mX;
-        private final int mY;
-
-        public PositionMatcher(int action, int x, int y) {
-            super(action);
-            mX = x;
-            mY = y;
-        }
-
-        @Override
-        public boolean matches(MotionEvent actual) {
-            return super.matches(actual)
-                    && ((int) actual.getX()) == mX
-                    && ((int) actual.getY()) == mY;
-        }
-
-        @Override
-        public String toString() {
-            return super.toString() + "@(" + mX + "," + mY + ")";
-        }
-    }
-
-    public static void verifyEnterMove(View.OnHoverListener listener, View view, int moveCount) {
-        final InOrder inOrder = inOrder(listener);
-        verifyEnterMoveInternal(listener, view, moveCount, inOrder);
-        inOrder.verifyNoMoreInteractions();
-    }
-
-    public static void verifyEnterMoveExit(
-            View.OnHoverListener listener, View view, int moveCount) {
-        final InOrder inOrder = inOrder(listener);
-        verifyEnterMoveInternal(listener, view, moveCount, inOrder);
-        inOrder.verify(listener, times(1)).onHover(eq(view),
-                argThat(new ActionMatcher(MotionEvent.ACTION_HOVER_EXIT)));
-        inOrder.verifyNoMoreInteractions();
-    }
-
-    private static void verifyEnterMoveInternal(
-            View.OnHoverListener listener, View view, int moveCount, InOrder inOrder) {
-        inOrder.verify(listener, times(1)).onHover(eq(view),
-                argThat(new ActionMatcher(MotionEvent.ACTION_HOVER_ENTER)));
-        inOrder.verify(listener, times(moveCount)).onHover(eq(view),
-                argThat(new ActionMatcher(MotionEvent.ACTION_HOVER_MOVE)));
-    }
-}
-
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/CtsTouchUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/CtsTouchUtils.java
deleted file mode 100644
index bd53549..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/CtsTouchUtils.java
+++ /dev/null
@@ -1,614 +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.compatibility.common.util;
-
-import android.app.Instrumentation;
-import android.app.UiAutomation;
-import android.graphics.Point;
-import android.os.SystemClock;
-import android.support.test.rule.ActivityTestRule;
-import android.util.SparseArray;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-
-/**
- * Test utilities for touch emulation.
- */
-public final class CtsTouchUtils {
-    /**
-     * Interface definition for a callback to be invoked when an event has been injected.
-     */
-    public interface EventInjectionListener {
-        /**
-         * Callback method to be invoked when a {MotionEvent#ACTION_DOWN} has been injected.
-         * @param xOnScreen X coordinate of the injected event.
-         * @param yOnScreen Y coordinate of the injected event.
-         */
-        public void onDownInjected(int xOnScreen, int yOnScreen);
-
-        /**
-         * Callback method to be invoked when a {MotionEvent#ACTION_MOVE} has been injected.
-         * @param xOnScreen X coordinates of the injected event.
-         * @param yOnScreen Y coordinates of the injected event.
-         */
-        public void onMoveInjected(int[] xOnScreen, int[] yOnScreen);
-
-        /**
-         * Callback method to be invoked when a {MotionEvent#ACTION_UP} has been injected.
-         * @param xOnScreen X coordinate of the injected event.
-         * @param yOnScreen Y coordinate of the injected event.
-         */
-        public void onUpInjected(int xOnScreen, int yOnScreen);
-    }
-
-    private CtsTouchUtils() {}
-
-    /**
-     * Emulates a tap in the center of the passed {@link View}.
-     *
-     * @param instrumentation the instrumentation used to run the test
-     * @param view the view to "tap"
-     */
-    public static void emulateTapOnViewCenter(Instrumentation instrumentation,
-            ActivityTestRule<?> activityTestRule, View view) {
-        emulateTapOnView(instrumentation, activityTestRule, view, view.getWidth() / 2,
-                view.getHeight() / 2);
-    }
-
-    /**
-     * Emulates a tap on a point relative to the top-left corner of the passed {@link View}. Offset
-     * parameters are used to compute the final screen coordinates of the tap point.
-     *
-     * @param instrumentation the instrumentation used to run the test
-     * @param anchorView the anchor view to determine the tap location on the screen
-     * @param offsetX extra X offset for the tap
-     * @param offsetY extra Y offset for the tap
-     */
-    public static void emulateTapOnView(Instrumentation instrumentation,
-            ActivityTestRule<?> activityTestRule, View anchorView,
-            int offsetX, int offsetY) {
-        final int touchSlop = ViewConfiguration.get(anchorView.getContext()).getScaledTouchSlop();
-        // Get anchor coordinates on the screen
-        final int[] viewOnScreenXY = new int[2];
-        anchorView.getLocationOnScreen(viewOnScreenXY);
-        int xOnScreen = viewOnScreenXY[0] + offsetX;
-        int yOnScreen = viewOnScreenXY[1] + offsetY;
-        final UiAutomation uiAutomation = instrumentation.getUiAutomation();
-        final long downTime = SystemClock.uptimeMillis();
-
-        injectDownEvent(uiAutomation, downTime, xOnScreen, yOnScreen, null);
-        injectMoveEventForTap(uiAutomation, downTime, touchSlop, xOnScreen, yOnScreen);
-        injectUpEvent(uiAutomation, downTime, false, xOnScreen, yOnScreen, null);
-
-        // Wait for the system to process all events in the queue
-        if (activityTestRule != null) {
-            WidgetTestUtils.runOnMainAndDrawSync(activityTestRule,
-                    activityTestRule.getActivity().getWindow().getDecorView(), null);
-        } else {
-            instrumentation.waitForIdleSync();
-        }
-    }
-
-    /**
-     * Emulates a double tap in the center of the passed {@link View}.
-     *
-     * @param instrumentation the instrumentation used to run the test
-     * @param view the view to "double tap"
-     */
-    public static void emulateDoubleTapOnViewCenter(Instrumentation instrumentation,
-            ActivityTestRule<?> activityTestRule, View view) {
-        emulateDoubleTapOnView(instrumentation, activityTestRule, view, view.getWidth() / 2,
-                view.getHeight() / 2);
-    }
-
-    /**
-     * Emulates a double tap on a point relative to the top-left corner of the passed {@link View}.
-     * Offset parameters are used to compute the final screen coordinates of the tap points.
-     *
-     * @param instrumentation the instrumentation used to run the test
-     * @param anchorView the anchor view to determine the tap location on the screen
-     * @param offsetX extra X offset for the taps
-     * @param offsetY extra Y offset for the taps
-     */
-    public static void emulateDoubleTapOnView(Instrumentation instrumentation,
-            ActivityTestRule<?> activityTestRule, View anchorView,
-            int offsetX, int offsetY) {
-        final int touchSlop = ViewConfiguration.get(anchorView.getContext()).getScaledTouchSlop();
-        // Get anchor coordinates on the screen
-        final int[] viewOnScreenXY = new int[2];
-        anchorView.getLocationOnScreen(viewOnScreenXY);
-        int xOnScreen = viewOnScreenXY[0] + offsetX;
-        int yOnScreen = viewOnScreenXY[1] + offsetY;
-        final UiAutomation uiAutomation = instrumentation.getUiAutomation();
-        final long downTime = SystemClock.uptimeMillis();
-
-        injectDownEvent(uiAutomation, downTime, xOnScreen, yOnScreen, null);
-        injectMoveEventForTap(uiAutomation, downTime, touchSlop, xOnScreen, yOnScreen);
-        injectUpEvent(uiAutomation, downTime, false, xOnScreen, yOnScreen, null);
-        injectDownEvent(uiAutomation, downTime, xOnScreen, yOnScreen, null);
-        injectMoveEventForTap(uiAutomation, downTime, touchSlop, xOnScreen, yOnScreen);
-        injectUpEvent(uiAutomation, downTime, false, xOnScreen, yOnScreen, null);
-
-        // Wait for the system to process all events in the queue
-        if (activityTestRule != null) {
-            WidgetTestUtils.runOnMainAndDrawSync(activityTestRule,
-                    activityTestRule.getActivity().getWindow().getDecorView(), null);
-        } else {
-            instrumentation.waitForIdleSync();
-        }
-    }
-
-    /**
-     * Emulates a linear drag gesture between 2 points across the screen.
-     *
-     * @param instrumentation the instrumentation used to run the test
-     * @param dragStartX Start X of the emulated drag gesture
-     * @param dragStartY Start Y of the emulated drag gesture
-     * @param dragAmountX X amount of the emulated drag gesture
-     * @param dragAmountY Y amount of the emulated drag gesture
-     */
-    public static void emulateDragGesture(Instrumentation instrumentation,
-            ActivityTestRule<?> activityTestRule,
-            int dragStartX, int dragStartY, int dragAmountX, int dragAmountY) {
-        emulateDragGesture(instrumentation, activityTestRule,
-                dragStartX, dragStartY, dragAmountX, dragAmountY,
-                2000, 20, null);
-    }
-
-    private static void emulateDragGesture(Instrumentation instrumentation,
-            ActivityTestRule<?> activityTestRule,
-            int dragStartX, int dragStartY, int dragAmountX, int dragAmountY,
-            int dragDurationMs, int moveEventCount) {
-        emulateDragGesture(instrumentation, activityTestRule,
-                dragStartX, dragStartY, dragAmountX, dragAmountY,
-                dragDurationMs, moveEventCount, null);
-    }
-
-    private static void emulateDragGesture(Instrumentation instrumentation,
-            ActivityTestRule<?> activityTestRule,
-            int dragStartX, int dragStartY, int dragAmountX, int dragAmountY,
-            int dragDurationMs, int moveEventCount,
-            EventInjectionListener eventInjectionListener) {
-        // We are using the UiAutomation object to inject events so that drag works
-        // across view / window boundaries (such as for the emulated drag and drop
-        // sequences)
-        final UiAutomation uiAutomation = instrumentation.getUiAutomation();
-        final long downTime = SystemClock.uptimeMillis();
-
-        injectDownEvent(uiAutomation, downTime, dragStartX, dragStartY, eventInjectionListener);
-
-        // Inject a sequence of MOVE events that emulate the "move" part of the gesture
-        injectMoveEventsForDrag(uiAutomation, downTime, true, dragStartX, dragStartY,
-                dragStartX + dragAmountX, dragStartY + dragAmountY, moveEventCount, dragDurationMs,
-            eventInjectionListener);
-
-        injectUpEvent(uiAutomation, downTime, true, dragStartX + dragAmountX,
-                dragStartY + dragAmountY, eventInjectionListener);
-
-        // Wait for the system to process all events in the queue
-        if (activityTestRule != null) {
-            WidgetTestUtils.runOnMainAndDrawSync(activityTestRule,
-                    activityTestRule.getActivity().getWindow().getDecorView(), null);
-        } else {
-            instrumentation.waitForIdleSync();
-        }
-    }
-
-    /**
-     * Emulates a series of linear drag gestures across the screen between multiple points without
-     * lifting the finger. Note that this function does not support curve movements between the
-     * points.
-     *
-     * @param instrumentation the instrumentation used to run the test
-     * @param coordinates the ordered list of points for the drag gesture
-     */
-    public static void emulateDragGesture(Instrumentation instrumentation,
-            ActivityTestRule<?> activityTestRule, SparseArray<Point> coordinates) {
-        emulateDragGesture(instrumentation, activityTestRule, coordinates, 2000, 20);
-    }
-
-    private static void emulateDragGesture(Instrumentation instrumentation,
-            ActivityTestRule<?> activityTestRule,
-            SparseArray<Point> coordinates, int dragDurationMs, int moveEventCount) {
-        final int coordinatesSize = coordinates.size();
-        if (coordinatesSize < 2) {
-            throw new IllegalArgumentException("Need at least 2 points for emulating drag");
-        }
-        // We are using the UiAutomation object to inject events so that drag works
-        // across view / window boundaries (such as for the emulated drag and drop
-        // sequences)
-        final UiAutomation uiAutomation = instrumentation.getUiAutomation();
-        final long downTime = SystemClock.uptimeMillis();
-
-        injectDownEvent(uiAutomation, downTime, coordinates.get(0).x, coordinates.get(0).y, null);
-
-        // Move to each coordinate.
-        for (int i = 0; i < coordinatesSize - 1; i++) {
-            // Inject a sequence of MOVE events that emulate the "move" part of the gesture.
-            injectMoveEventsForDrag(uiAutomation,
-                    downTime,
-                    true,
-                    coordinates.get(i).x,
-                    coordinates.get(i).y,
-                    coordinates.get(i + 1).x,
-                    coordinates.get(i + 1).y,
-                    moveEventCount,
-                    dragDurationMs,
-                    null);
-        }
-
-        injectUpEvent(uiAutomation,
-                downTime,
-                true,
-                coordinates.get(coordinatesSize - 1).x,
-                coordinates.get(coordinatesSize - 1).y,
-                null);
-
-        // Wait for the system to process all events in the queue
-        if (activityTestRule != null) {
-            WidgetTestUtils.runOnMainAndDrawSync(activityTestRule,
-                    activityTestRule.getActivity().getWindow().getDecorView(), null);
-        } else {
-            instrumentation.waitForIdleSync();
-        }
-    }
-
-    private static long injectDownEvent(UiAutomation uiAutomation, long downTime, int xOnScreen,
-            int yOnScreen, EventInjectionListener eventInjectionListener) {
-        MotionEvent eventDown = MotionEvent.obtain(
-                downTime, downTime, MotionEvent.ACTION_DOWN, xOnScreen, yOnScreen, 1);
-        eventDown.setSource(InputDevice.SOURCE_TOUCHSCREEN);
-        uiAutomation.injectInputEvent(eventDown, true);
-        if (eventInjectionListener != null) {
-            eventInjectionListener.onDownInjected(xOnScreen, yOnScreen);
-        }
-        eventDown.recycle();
-        return downTime;
-    }
-
-    private static void injectMoveEventForTap(UiAutomation uiAutomation, long downTime,
-            int touchSlop, int xOnScreen, int yOnScreen) {
-        MotionEvent eventMove = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_MOVE,
-                xOnScreen + (touchSlop / 2.0f), yOnScreen + (touchSlop / 2.0f), 1);
-        eventMove.setSource(InputDevice.SOURCE_TOUCHSCREEN);
-        uiAutomation.injectInputEvent(eventMove, true);
-        eventMove.recycle();
-    }
-
-    private static void injectMoveEventsForDrag(UiAutomation uiAutomation, long downTime,
-            boolean useCurrentEventTime, int dragStartX, int dragStartY, int dragEndX, int dragEndY,
-            int moveEventCount, int dragDurationMs, EventInjectionListener eventInjectionListener) {
-        final int dragAmountX = dragEndX - dragStartX;
-        final int dragAmountY = dragEndY - dragStartY;
-        final int sleepTime = dragDurationMs / moveEventCount;
-
-        // sleep for a bit to emulate the overall drag gesture.
-        long prevEventTime = downTime;
-        SystemClock.sleep(sleepTime);
-        for (int i = 0; i < moveEventCount; i++) {
-            // Note that the first MOVE event is generated "away" from the coordinates
-            // of the start / DOWN event, and the last MOVE event is generated
-            // at the same coordinates as the subsequent UP event.
-            final int moveX = dragStartX + dragAmountX * (i  + 1) / moveEventCount;
-            final int moveY = dragStartY + dragAmountY * (i  + 1) / moveEventCount;
-            long eventTime = useCurrentEventTime ? SystemClock.uptimeMillis() : downTime;
-
-            // If necessary, generate history for our next MOVE event. The history is generated
-            // to be spaced at 10 millisecond intervals, interpolating the coordinates from the
-            // last generated MOVE event to our current one.
-            int historyEventCount = (int) ((eventTime - prevEventTime) / 10);
-            int[] xCoordsForListener = (eventInjectionListener == null) ? null :
-                    new int[Math.max(1, historyEventCount)];
-            int[] yCoordsForListener = (eventInjectionListener == null) ? null :
-                    new int[Math.max(1, historyEventCount)];
-            MotionEvent eventMove = null;
-            if (historyEventCount == 0) {
-                eventMove = MotionEvent.obtain(
-                        downTime, eventTime, MotionEvent.ACTION_MOVE, moveX, moveY, 1);
-                if (eventInjectionListener != null) {
-                    xCoordsForListener[0] = moveX;
-                    yCoordsForListener[0] = moveY;
-                }
-            } else {
-                final int prevMoveX = dragStartX + dragAmountX * i / moveEventCount;
-                final int prevMoveY = dragStartY + dragAmountY * i / moveEventCount;
-                final int deltaMoveX = moveX - prevMoveX;
-                final int deltaMoveY = moveY - prevMoveY;
-                final long deltaTime = (eventTime - prevEventTime);
-                for (int historyIndex = 0; historyIndex < historyEventCount; historyIndex++) {
-                    int stepMoveX = prevMoveX + deltaMoveX * (historyIndex + 1) / historyEventCount;
-                    int stepMoveY = prevMoveY + deltaMoveY * (historyIndex + 1) / historyEventCount;
-                    long stepEventTime = useCurrentEventTime
-                            ? prevEventTime + deltaTime * (historyIndex + 1) / historyEventCount
-                            : downTime;
-                    if (historyIndex == 0) {
-                        // Generate the first event in our sequence
-                        eventMove = MotionEvent.obtain(downTime, stepEventTime,
-                                MotionEvent.ACTION_MOVE, stepMoveX, stepMoveY, 1);
-                    } else {
-                        // and then add to it
-                        eventMove.addBatch(stepEventTime, stepMoveX, stepMoveY, 1.0f, 1.0f, 1);
-                    }
-                    if (eventInjectionListener != null) {
-                        xCoordsForListener[historyIndex] = stepMoveX;
-                        yCoordsForListener[historyIndex] = stepMoveY;
-                    }
-                }
-            }
-
-            eventMove.setSource(InputDevice.SOURCE_TOUCHSCREEN);
-            uiAutomation.injectInputEvent(eventMove, true);
-            if (eventInjectionListener != null) {
-                eventInjectionListener.onMoveInjected(xCoordsForListener, yCoordsForListener);
-            }
-            eventMove.recycle();
-            prevEventTime = eventTime;
-
-            // sleep for a bit to emulate the overall drag gesture.
-            SystemClock.sleep(sleepTime);
-        }
-    }
-
-    private static void injectUpEvent(UiAutomation uiAutomation, long downTime,
-            boolean useCurrentEventTime, int xOnScreen, int yOnScreen,
-            EventInjectionListener eventInjectionListener) {
-        long eventTime = useCurrentEventTime ? SystemClock.uptimeMillis() : downTime;
-        MotionEvent eventUp = MotionEvent.obtain(
-                downTime, eventTime, MotionEvent.ACTION_UP, xOnScreen, yOnScreen, 1);
-        eventUp.setSource(InputDevice.SOURCE_TOUCHSCREEN);
-        uiAutomation.injectInputEvent(eventUp, true);
-        if (eventInjectionListener != null) {
-            eventInjectionListener.onUpInjected(xOnScreen, yOnScreen);
-        }
-        eventUp.recycle();
-    }
-
-    /**
-     * Emulates a fling gesture across the horizontal center of the passed view.
-     *
-     * @param instrumentation the instrumentation used to run the test
-     * @param view the view to fling
-     * @param isDownwardsFlingGesture if <code>true</code>, the emulated fling will
-     *      be a downwards gesture
-     * @return The vertical amount of emulated fling in pixels
-     */
-    public static int emulateFlingGesture(Instrumentation instrumentation,
-            ActivityTestRule<?> activityTestRule, View view, boolean isDownwardsFlingGesture) {
-        return emulateFlingGesture(instrumentation, activityTestRule,
-                view, isDownwardsFlingGesture, null);
-    }
-
-    /**
-     * Emulates a fling gesture across the horizontal center of the passed view.
-     *
-     * @param instrumentation the instrumentation used to run the test
-     * @param view the view to fling
-     * @param isDownwardsFlingGesture if <code>true</code>, the emulated fling will
-     *      be a downwards gesture
-     * @param eventInjectionListener optional listener to notify about the injected events
-     * @return The vertical amount of emulated fling in pixels
-     */
-    public static int emulateFlingGesture(Instrumentation instrumentation,
-            ActivityTestRule<?> activityTestRule, View view, boolean isDownwardsFlingGesture,
-            EventInjectionListener eventInjectionListener) {
-        final ViewConfiguration configuration = ViewConfiguration.get(view.getContext());
-        final int flingVelocity = (configuration.getScaledMinimumFlingVelocity() +
-                configuration.getScaledMaximumFlingVelocity()) / 2;
-        // Get view coordinates on the screen
-        final int[] viewOnScreenXY = new int[2];
-        view.getLocationOnScreen(viewOnScreenXY);
-
-        // Our fling gesture will be from 25% height of the view to 75% height of the view
-        // for downwards fling gesture, and the other way around for upwards fling gesture
-        final int viewHeight = view.getHeight();
-        final int x = viewOnScreenXY[0] + view.getWidth() / 2;
-        final int startY = isDownwardsFlingGesture ? viewOnScreenXY[1] + viewHeight / 4
-                : viewOnScreenXY[1] + 3 * viewHeight / 4;
-        final int amountY = isDownwardsFlingGesture ? viewHeight / 2 : -viewHeight / 2;
-
-        // Compute fling gesture duration based on the distance (50% height of the view) and
-        // fling velocity
-        final int durationMs = (1000 * viewHeight) / (2 * flingVelocity);
-
-        // And do the same event injection sequence as our generic drag gesture
-        emulateDragGesture(instrumentation, activityTestRule,
-                x, startY, 0, amountY, durationMs, durationMs / 16,
-            eventInjectionListener);
-
-        return amountY;
-    }
-
-    private static class ViewStateSnapshot {
-        final View mFirst;
-        final View mLast;
-        final int mFirstTop;
-        final int mLastBottom;
-        final int mChildCount;
-        private ViewStateSnapshot(ViewGroup viewGroup) {
-            mChildCount = viewGroup.getChildCount();
-            if (mChildCount == 0) {
-                mFirst = mLast = null;
-                mFirstTop = mLastBottom = Integer.MIN_VALUE;
-            } else {
-                mFirst = viewGroup.getChildAt(0);
-                mLast = viewGroup.getChildAt(mChildCount - 1);
-                mFirstTop = mFirst.getTop();
-                mLastBottom = mLast.getBottom();
-            }
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-
-            final ViewStateSnapshot that = (ViewStateSnapshot) o;
-            return mFirstTop == that.mFirstTop &&
-                    mLastBottom == that.mLastBottom &&
-                    mFirst == that.mFirst &&
-                    mLast == that.mLast &&
-                    mChildCount == that.mChildCount;
-        }
-
-        @Override
-        public int hashCode() {
-            int result = mFirst != null ? mFirst.hashCode() : 0;
-            result = 31 * result + (mLast != null ? mLast.hashCode() : 0);
-            result = 31 * result + mFirstTop;
-            result = 31 * result + mLastBottom;
-            result = 31 * result + mChildCount;
-            return result;
-        }
-    }
-
-    /**
-     * Emulates a scroll to the bottom of the specified {@link ViewGroup}.
-     *
-     * @param instrumentation the instrumentation used to run the test
-     * @param viewGroup View group
-     */
-    public static void emulateScrollToBottom(Instrumentation instrumentation,
-            ActivityTestRule<?> activityTestRule, ViewGroup viewGroup) {
-        final int[] viewGroupOnScreenXY = new int[2];
-        viewGroup.getLocationOnScreen(viewGroupOnScreenXY);
-
-        final int emulatedX = viewGroupOnScreenXY[0] + viewGroup.getWidth() / 2;
-        final int emulatedStartY = viewGroupOnScreenXY[1] + 3 * viewGroup.getHeight() / 4;
-        final int swipeAmount = viewGroup.getHeight() / 2;
-
-        ViewStateSnapshot prev;
-        ViewStateSnapshot next = new ViewStateSnapshot(viewGroup);
-        do {
-            prev = next;
-            emulateDragGesture(instrumentation, activityTestRule,
-                    emulatedX, emulatedStartY, 0, -swipeAmount, 300, 10);
-            next = new ViewStateSnapshot(viewGroup);
-        } while (!prev.equals(next));
-    }
-
-    /**
-     * Emulates a long press in the center of the passed {@link View}.
-     *
-     * @param instrumentation the instrumentation used to run the test
-     * @param view the view to "long press"
-     */
-    public static void emulateLongPressOnViewCenter(Instrumentation instrumentation,
-            ActivityTestRule<?> activityTestRule, View view) {
-        emulateLongPressOnViewCenter(instrumentation, activityTestRule, view, 0);
-    }
-
-    /**
-     * Emulates a long press in the center of the passed {@link View}.
-     *
-     * @param instrumentation the instrumentation used to run the test
-     * @param view the view to "long press"
-     * @param extraWaitMs the duration of emulated "long press" in milliseconds starting
-     *      after system-level long press timeout.
-     */
-    public static void emulateLongPressOnViewCenter(Instrumentation instrumentation,
-            ActivityTestRule<?> activityTestRule, View view, long extraWaitMs) {
-        final int touchSlop = ViewConfiguration.get(view.getContext()).getScaledTouchSlop();
-        // Use instrumentation to emulate a tap on the spinner to bring down its popup
-        final int[] viewOnScreenXY = new int[2];
-        view.getLocationOnScreen(viewOnScreenXY);
-        int xOnScreen = viewOnScreenXY[0] + view.getWidth() / 2;
-        int yOnScreen = viewOnScreenXY[1] + view.getHeight() / 2;
-
-        emulateLongPressOnScreen(instrumentation, activityTestRule,
-                xOnScreen, yOnScreen, touchSlop, extraWaitMs, true);
-    }
-
-    /**
-     * Emulates a long press confirmed on a point relative to the top-left corner of the passed
-     * {@link View}. Offset parameters are used to compute the final screen coordinates of the
-     * press point.
-     *
-     * @param instrumentation the instrumentation used to run the test
-     * @param view the view to "long press"
-     * @param offsetX extra X offset for the tap
-     * @param offsetY extra Y offset for the tap
-     */
-    public static void emulateLongPressOnView(Instrumentation instrumentation,
-            ActivityTestRule<?> activityTestRule, View view, int offsetX, int offsetY) {
-        final int touchSlop = ViewConfiguration.get(view.getContext()).getScaledTouchSlop();
-        final int[] viewOnScreenXY = new int[2];
-        view.getLocationOnScreen(viewOnScreenXY);
-        int xOnScreen = viewOnScreenXY[0] + offsetX;
-        int yOnScreen = viewOnScreenXY[1] + offsetY;
-
-        emulateLongPressOnScreen(instrumentation, activityTestRule,
-                xOnScreen, yOnScreen, touchSlop, 0, true);
-    }
-
-    /**
-     * Emulates a long press then a linear drag gesture between 2 points across the screen.
-     * This is used for drag selection.
-     *
-     * @param instrumentation the instrumentation used to run the test
-     * @param dragStartX Start X of the emulated drag gesture
-     * @param dragStartY Start Y of the emulated drag gesture
-     * @param dragAmountX X amount of the emulated drag gesture
-     * @param dragAmountY Y amount of the emulated drag gesture
-     */
-    public static void emulateLongPressAndDragGesture(Instrumentation instrumentation,
-            ActivityTestRule<?> activityTestRule,
-            int dragStartX, int dragStartY, int dragAmountX, int dragAmountY) {
-        emulateLongPressOnScreen(instrumentation, activityTestRule, dragStartX, dragStartY,
-                0 /* touchSlop */, 0 /* extraWaitMs */, false /* upGesture */);
-        emulateDragGesture(instrumentation, activityTestRule, dragStartX, dragStartY, dragAmountX,
-                dragAmountY);
-    }
-
-    /**
-     * Emulates a long press on the screen.
-     *
-     * @param instrumentation the instrumentation used to run the test
-     * @param xOnScreen X position on screen for the "long press"
-     * @param yOnScreen Y position on screen for the "long press"
-     * @param extraWaitMs extra duration of emulated long press in milliseconds added
-     *        after the system-level "long press" timeout.
-     * @param upGesture whether to include an up event.
-     */
-    private static void emulateLongPressOnScreen(Instrumentation instrumentation,
-            ActivityTestRule<?> activityTestRule,
-            int xOnScreen, int yOnScreen, int touchSlop, long extraWaitMs, boolean upGesture) {
-        final UiAutomation uiAutomation = instrumentation.getUiAutomation();
-        final long downTime = SystemClock.uptimeMillis();
-
-        injectDownEvent(uiAutomation, downTime, xOnScreen, yOnScreen, null);
-        injectMoveEventForTap(uiAutomation, downTime, touchSlop, xOnScreen, yOnScreen);
-        SystemClock.sleep((long) (ViewConfiguration.getLongPressTimeout() * 1.5f) + extraWaitMs);
-        if (upGesture) {
-            injectUpEvent(uiAutomation, downTime, false, xOnScreen, yOnScreen, null);
-        }
-
-        // Wait for the system to process all events in the queue
-        if (activityTestRule != null) {
-            WidgetTestUtils.runOnMainAndDrawSync(activityTestRule,
-                    activityTestRule.getActivity().getWindow().getDecorView(), null);
-        } else {
-            instrumentation.waitForIdleSync();
-        }
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/DeviceConfigStateChangerRule.java b/common/device-side/util/src/com/android/compatibility/common/util/DeviceConfigStateChangerRule.java
deleted file mode 100644
index a13da52..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/DeviceConfigStateChangerRule.java
+++ /dev/null
@@ -1,53 +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.
- */
-package com.android.compatibility.common.util;
-
-import android.content.Context;
-import android.provider.DeviceConfig;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * JUnit rule used to set a {@link DeviceConfig} preference before the test is run.
- *
- * <p>It stores the current value before the test, changes it (if necessary), then restores it after
- * the test (if necessary).
- */
-public class DeviceConfigStateChangerRule extends StateChangerRule<String> {
-
-    /**
-     * Default constructor.
-     *
-     * @param context context used to retrieve the {@link DeviceConfig} provider.
-     * @param namespace {@code DeviceConfig} namespace.
-     * @param key prefence key.
-     * @param value value to be set before the test is run.
-     */
-    public DeviceConfigStateChangerRule(@NonNull Context context, @NonNull String namespace,
-            @NonNull String key, @Nullable String value) {
-        this(new DeviceConfigStateManager(context, namespace, key), value);
-    }
-
-    /**
-     * Alternative constructor used when then test case already defines a
-     * {@link DeviceConfigStateManager}.
-     */
-    public DeviceConfigStateChangerRule(@NonNull DeviceConfigStateManager dcsm,
-            @Nullable String value) {
-        super(dcsm, value);
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/DeviceConfigStateKeeperRule.java b/common/device-side/util/src/com/android/compatibility/common/util/DeviceConfigStateKeeperRule.java
deleted file mode 100644
index 6f186de..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/DeviceConfigStateKeeperRule.java
+++ /dev/null
@@ -1,41 +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.
- */
-package com.android.compatibility.common.util;
-
-import android.content.Context;
-import android.provider.DeviceConfig;
-
-import androidx.annotation.NonNull;
-
-/**
- * JUnit rule used to restore a {@link DeviceConfig} preference after the test is run.
- *
- * <p>It stores the current value before the test, and restores it after the test (if necessary).
- */
-public class DeviceConfigStateKeeperRule extends StateKeeperRule<String> {
-
-    /**
-     * Default constructor.
-     *
-     * @param context context used to retrieve the {@link DeviceConfig} provider.
-     * @param namespace {@code DeviceConfig} namespace.
-     * @param key prefence key.
-     */
-    public DeviceConfigStateKeeperRule(@NonNull Context context, @NonNull String namespace,
-            @NonNull String key) {
-        super(new DeviceConfigStateManager(context, namespace, key));
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/DeviceConfigStateManager.java b/common/device-side/util/src/com/android/compatibility/common/util/DeviceConfigStateManager.java
deleted file mode 100644
index 040641c..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/DeviceConfigStateManager.java
+++ /dev/null
@@ -1,100 +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.
- */
-
-package com.android.compatibility.common.util;
-
-import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
-
-import android.content.Context;
-import android.provider.DeviceConfig;
-import android.provider.Settings;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.google.common.base.Preconditions;
-
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * Manages the state of a preference backed by {@link DeviceConfig}.
- */
-public final class DeviceConfigStateManager implements StateManager<String> {
-
-    private static final String TAG = DeviceConfigStateManager.class.getSimpleName();
-
-    private final Context mContext;
-    private final String mNamespace;
-    private final String mKey;
-
-    /**
-     * Default constructor.
-     *
-     * @param context context used to retrieve the {@link Settings} provider.
-     * @param namespace settings namespace.
-     * @param key prefence key.
-     */
-    public DeviceConfigStateManager(@NonNull Context context, @NonNull String namespace,
-            @NonNull String key) {
-        debug("DeviceConfigStateManager", "namespace=%s, key=%s", namespace, key);
-
-        mContext = Preconditions.checkNotNull(context);
-        mNamespace = Preconditions.checkNotNull(namespace);
-        mKey = Preconditions.checkNotNull(key);
-    }
-
-    @Override
-    public void set(@Nullable String value) {
-        debug("set", "new value is %s", value);
-        runWithShellPermissionIdentity(() -> setWithPermissionsGranted(value),
-                "android.permission.READ_DEVICE_CONFIG", "android.permission.WRITE_DEVICE_CONFIG");
-    }
-
-    private void setWithPermissionsGranted(@Nullable String value) {
-        final OneTimeDeviceConfigListener listener = new OneTimeDeviceConfigListener(mNamespace,
-                mKey);
-        DeviceConfig.addOnPropertiesChangedListener(mNamespace, mContext.getMainExecutor(),
-                listener);
-
-        DeviceConfig.setProperty(mNamespace, mKey, value, /* makeDefault= */ false);
-        listener.assertCalled();
-    }
-
-    @Override
-    @Nullable
-    public String get() {
-        final AtomicReference<String> reference = new AtomicReference<>();
-        runWithShellPermissionIdentity(()
-                -> reference.set(DeviceConfig.getProperty(mNamespace, mKey)),
-                "android.permission.READ_DEVICE_CONFIG");
-        debug("get", "returning %s", reference.get());
-
-        return reference.get();
-    }
-
-    private void debug(@NonNull String methodName, @NonNull String msg, Object...args) {
-        if (!Log.isLoggable(TAG, Log.DEBUG)) return;
-
-        final String prefix = String.format("%s(%s:%s): ", methodName, mNamespace, mKey);
-        Log.d(TAG, prefix + String.format(msg, args));
-    }
-
-    @Override
-    public String toString() {
-        return "DeviceConfigStateManager[namespace=" + mNamespace + ", key=" + mKey + "]";
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/DeviceInfoStore.java b/common/device-side/util/src/com/android/compatibility/common/util/DeviceInfoStore.java
deleted file mode 100644
index 03f69fa..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/DeviceInfoStore.java
+++ /dev/null
@@ -1,260 +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.compatibility.common.util;
-
-import android.util.JsonWriter;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.List;
-
-public class DeviceInfoStore extends InfoStore {
-
-    protected File mJsonFile;
-    protected JsonWriter mJsonWriter = null;
-
-    public DeviceInfoStore() {
-        mJsonFile = null;
-    }
-
-    public DeviceInfoStore(File file) throws Exception {
-        mJsonFile = file;
-    }
-
-    /**
-     * Opens the file for storage and creates the writer.
-     */
-    @Override
-    public void open() throws IOException {
-        FileOutputStream out = new FileOutputStream(mJsonFile);
-        mJsonWriter = new JsonWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8));
-        // TODO(agathaman): remove to make json output less pretty
-        mJsonWriter.setIndent("  ");
-        mJsonWriter.beginObject();
-    }
-
-    /**
-     * Closes the writer.
-     */
-    @Override
-    public void close() throws Exception {
-        mJsonWriter.endObject();
-        mJsonWriter.flush();
-        mJsonWriter.close();
-    }
-
-    /**
-     * Start a new group of result.
-     */
-    @Override
-    public void startGroup() throws IOException {
-        mJsonWriter.beginObject();
-    }
-
-    /**
-     * Start a new group of result with specified name.
-     */
-    @Override
-    public void startGroup(String name) throws IOException {
-        mJsonWriter.name(name);
-        mJsonWriter.beginObject();
-    }
-
-    /**
-     * Complete adding result to the last started group.
-     */
-    @Override
-    public void endGroup() throws IOException {
-        mJsonWriter.endObject();
-    }
-
-    /**
-     * Start a new array of result.
-     */
-    @Override
-    public void startArray() throws IOException {
-        mJsonWriter.beginArray();
-    }
-
-    /**
-     * Start a new array of result with specified name.
-     */
-    @Override
-    public void startArray(String name) throws IOException {
-        checkName(name);
-        mJsonWriter.name(name);
-        mJsonWriter.beginArray();
-    }
-
-    /**
-     * Complete adding result to the last started array.
-     */
-    @Override
-    public void endArray() throws IOException {
-        mJsonWriter.endArray();
-    }
-
-    /**
-     * Adds a int value to the InfoStore
-     */
-    @Override
-    public void addResult(String name, int value) throws IOException {
-        checkName(name);
-        mJsonWriter.name(name);
-        mJsonWriter.value(value);
-    }
-
-    /**
-     * Adds a long value to the InfoStore
-     */
-    @Override
-    public void addResult(String name, long value) throws IOException {
-        checkName(name);
-        mJsonWriter.name(name);
-        mJsonWriter.value(value);
-    }
-
-    /**
-     * Adds a float value to the InfoStore
-     */
-    @Override
-    public void addResult(String name, float value) throws IOException {
-        addResult(name, (double) value);
-    }
-
-    /**
-     * Adds a double value to the InfoStore
-     */
-    @Override
-    public void addResult(String name, double value) throws IOException {
-        checkName(name);
-        if (isDoubleNaNOrInfinite(value)) {
-            return;
-        } else {
-            mJsonWriter.name(name);
-            mJsonWriter.value(value);
-        }
-    }
-
-    /**
-     * Adds a boolean value to the InfoStore
-     */
-    @Override
-    public void addResult(String name, boolean value) throws IOException {
-        checkName(name);
-        mJsonWriter.name(name);
-        mJsonWriter.value(value);
-    }
-
-    /**
-     * Adds a String value to the InfoStore
-     */
-    @Override
-    public void addResult(String name, String value) throws IOException {
-        checkName(name);
-        mJsonWriter.name(name);
-        mJsonWriter.value(checkString(value));
-    }
-
-    /**
-     * Adds a int array to the InfoStore
-     */
-    @Override
-    public void addArrayResult(String name, int[] array) throws IOException {
-        checkName(name);
-        mJsonWriter.name(name);
-        mJsonWriter.beginArray();
-        for (int value : checkArray(array)) {
-            mJsonWriter.value(value);
-        }
-        mJsonWriter.endArray();
-    }
-
-    /**
-     * Adds a long array to the InfoStore
-     */
-    @Override
-    public void addArrayResult(String name, long[] array) throws IOException {
-        checkName(name);
-        mJsonWriter.name(name);
-        mJsonWriter.beginArray();
-        for (long value : checkArray(array)) {
-            mJsonWriter.value(value);
-        }
-        mJsonWriter.endArray();
-    }
-
-    /**
-     * Adds a float array to the InfoStore
-     */
-    @Override
-    public void addArrayResult(String name, float[] array) throws IOException {
-        double[] doubleArray = new double[array.length];
-        for (int i = 0; i < array.length; i++) {
-            doubleArray[i] = array[i];
-        }
-        addArrayResult(name, doubleArray);
-    }
-
-    /**
-     * Adds a double array to the InfoStore
-     */
-    @Override
-    public void addArrayResult(String name, double[] array) throws IOException {
-        checkName(name);
-        mJsonWriter.name(name);
-        mJsonWriter.beginArray();
-        for (double value : checkArray(array)) {
-            if (isDoubleNaNOrInfinite(value)) {
-                continue;
-            }
-            mJsonWriter.value(value);
-        }
-        mJsonWriter.endArray();
-    }
-
-    /**
-     * Adds a boolean array to the InfoStore
-     */
-    @Override
-    public void addArrayResult(String name, boolean[] array) throws IOException {
-        checkName(name);
-        mJsonWriter.name(name);
-        mJsonWriter.beginArray();
-        for (boolean value : checkArray(array)) {
-            mJsonWriter.value(value);
-        }
-        mJsonWriter.endArray();
-    }
-
-    /**
-     * Adds a List of String to the InfoStore
-     */
-    @Override
-    public void addListResult(String name, List<String> list) throws IOException {
-        checkName(name);
-        mJsonWriter.name(name);
-        mJsonWriter.beginArray();
-        for (String value : checkStringList(list)) {
-            mJsonWriter.value(checkString(value));
-        }
-        mJsonWriter.endArray();
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/DeviceReportLog.java b/common/device-side/util/src/com/android/compatibility/common/util/DeviceReportLog.java
deleted file mode 100644
index d170263..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/DeviceReportLog.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import android.app.Instrumentation;
-import android.os.Bundle;
-import android.os.Environment;
-import android.util.Log;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-
-/**
- * Handles adding results to the report for device side tests.
- *
- * NOTE: tests MUST call {@link #submit(Instrumentation)} if and only if the test passes in order to
- * send the results to the runner.
- */
-public class DeviceReportLog extends ReportLog {
-    private static final String TAG = DeviceReportLog.class.getSimpleName();
-    private static final String RESULT = "COMPATIBILITY_TEST_RESULT";
-    private static final int INST_STATUS_ERROR = -1;
-    private static final int INST_STATUS_IN_PROGRESS = 2;
-
-    private ReportLogDeviceInfoStore store;
-
-    public DeviceReportLog(String reportLogName, String streamName) {
-        this(reportLogName, streamName,
-                new File(Environment.getExternalStorageDirectory(), "report-log-files"));
-    }
-
-    public DeviceReportLog(String reportLogName, String streamName, File logDirectory) {
-        super(reportLogName, streamName);
-        try {
-            // dir value must match the src-dir value configured in ReportLogCollector target
-            // preparer in cts/harness/tools/cts-tradefed/res/config/cts-preconditions.xml
-            if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
-                throw new IOException("External storage is not mounted");
-            } else if ((!logDirectory.exists() && !logDirectory.mkdirs())
-                    || (logDirectory.exists() && !logDirectory.isDirectory())) {
-                throw new IOException("Cannot create directory for device info files");
-            } else {
-                File jsonFile = new File(logDirectory, mReportLogName + ".reportlog.json");
-                store = new ReportLogDeviceInfoStore(jsonFile, mStreamName);
-                store.open();
-            }
-        } catch (Exception e) {
-            Log.e(TAG, "Could not create report log file.", e);
-        }
-    }
-
-    /**
-     * Adds a double metric to the report.
-     */
-    @Override
-    public void addValue(String source, String message, double value, ResultType type,
-            ResultUnit unit) {
-        super.addValue(source, message, value, type, unit);
-        try {
-            store.addResult(message, value);
-        } catch (Exception e) {
-            Log.e(TAG, "Could not log metric.", e);
-        }
-    }
-
-    /**
-     * Adds a double metric to the report.
-     */
-    @Override
-    public void addValue(String message, double value, ResultType type, ResultUnit unit) {
-        super.addValue(message, value, type, unit);
-        try {
-            store.addResult(message, value);
-        } catch (Exception e) {
-            Log.e(TAG, "Could not log metric.", e);
-        }
-    }
-
-    /**
-     * Adds a double array of metrics to the report.
-     */
-    @Override
-    public void addValues(String source, String message, double[] values, ResultType type,
-            ResultUnit unit) {
-        super.addValues(source, message, values, type, unit);
-        try {
-            store.addArrayResult(message, values);
-        } catch (Exception e) {
-            Log.e(TAG, "Could not log metric.", e);
-        }
-    }
-
-    /**
-     * Adds a double array of metrics to the report.
-     */
-    @Override
-    public void addValues(String message, double[] values, ResultType type, ResultUnit unit) {
-        super.addValues(message, values, type, unit);
-        try {
-            store.addArrayResult(message, values);
-        } catch (Exception e) {
-            Log.e(TAG, "Could not log metric.", e);
-        }
-    }
-
-    /**
-     * Adds an int metric to the report.
-     */
-    @Override
-    public void addValue(String message, int value, ResultType type, ResultUnit unit) {
-        try {
-            store.addResult(message, value);
-        } catch (Exception e) {
-            Log.e(TAG, "Could not log metric.", e);
-        }
-    }
-
-    /**
-     * Adds a long metric to the report.
-     */
-    @Override
-    public void addValue(String message, long value, ResultType type, ResultUnit unit) {
-        try {
-            store.addResult(message, value);
-        } catch (Exception e) {
-            Log.e(TAG, "Could not log metric.", e);
-        }
-    }
-
-    /**
-     * Adds a float metric to the report.
-     */
-    @Override
-    public void addValue(String message, float value, ResultType type, ResultUnit unit) {
-        try {
-            store.addResult(message, value);
-        } catch (Exception e) {
-            Log.e(TAG, "Could not log metric.", e);
-        }
-    }
-
-    /**
-     * Adds a boolean metric to the report.
-     */
-    @Override
-    public void addValue(String message, boolean value, ResultType type, ResultUnit unit) {
-        try {
-            store.addResult(message, value);
-        } catch (Exception e) {
-            Log.e(TAG, "Could not log metric.", e);
-        }
-    }
-
-    /**
-     * Adds a String metric to the report.
-     */
-    @Override
-    public void addValue(String message, String value, ResultType type, ResultUnit unit) {
-        try {
-            store.addResult(message, value);
-        } catch (Exception e) {
-            Log.e(TAG, "Could not log metric.", e);
-        }
-    }
-
-    /**
-     * Adds an int array of metrics to the report.
-     */
-    @Override
-    public void addValues(String message, int[] values, ResultType type, ResultUnit unit) {
-        try {
-            store.addArrayResult(message, values);
-        } catch (Exception e) {
-            Log.e(TAG, "Could not log metric.", e);
-        }
-    }
-
-    /**
-     * Adds a long array of metrics to the report.
-     */
-    @Override
-    public void addValues(String message, long[] values, ResultType type, ResultUnit unit) {
-        try {
-            store.addArrayResult(message, values);
-        } catch (Exception e) {
-            Log.e(TAG, "Could not log metric.", e);
-        }
-    }
-
-    /**
-     * Adds a float array of metrics to the report.
-     */
-    @Override
-    public void addValues(String message, float[] values, ResultType type, ResultUnit unit) {
-        try {
-            store.addArrayResult(message, values);
-        } catch (Exception e) {
-            Log.e(TAG, "Could not log metric.", e);
-        }
-    }
-
-    /**
-     * Adds a boolean array of metrics to the report.
-     */
-    @Override
-    public void addValues(String message, boolean[] values, ResultType type, ResultUnit unit) {
-        try {
-            store.addArrayResult(message, values);
-        } catch (Exception e) {
-            Log.e(TAG, "Could not log metric.", e);
-        }
-    }
-
-    /**
-     * Adds a String List of metrics to the report.
-     */
-    @Override
-    public void addValues(String message, List<String> values, ResultType type, ResultUnit unit) {
-        try {
-            store.addListResult(message, values);
-        } catch (Exception e) {
-            Log.e(TAG, "Could not log metric.", e);
-        }
-    }
-
-    /**
-     * Sets the summary double metric of the report.
-     *
-     * NOTE: messages over {@value Metric#MAX_MESSAGE_LENGTH} chars will be trimmed.
-     */
-    @Override
-    public void setSummary(String message, double value, ResultType type, ResultUnit unit) {
-        super.setSummary(message, value, type, unit);
-        try {
-            store.addResult(message, value);
-        } catch (Exception e) {
-            Log.e(TAG, "Could not log metric.", e);
-        }
-    }
-
-    /**
-     * Closes report file and submits report to instrumentation.
-     */
-    public void submit(Instrumentation instrumentation) {
-        try {
-            store.close();
-            Bundle output = new Bundle();
-            output.putString(RESULT, serialize(this));
-            instrumentation.sendStatus(INST_STATUS_IN_PROGRESS, output);
-        } catch (Exception e) {
-            Log.e(TAG, "ReportLog Submit Failed", e);
-            instrumentation.sendStatus(INST_STATUS_ERROR, null);
-        }
-    }
-
-    /**
-     * Closes report file. Static functions that do not have access to instrumentation can
-     * use this to close report logs. Summary, if present, is not reported to instrumentation, hence
-     * does not appear in the result XML.
-     */
-    public void submit() {
-        try {
-            store.close();
-        } catch (Exception e) {
-            Log.e(TAG, "ReportLog Submit Failed", e);
-        }
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/DoubleVisitor.java b/common/device-side/util/src/com/android/compatibility/common/util/DoubleVisitor.java
deleted file mode 100644
index 96fb3bb..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/DoubleVisitor.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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 com.android.compatibility.common.util;
-
-import androidx.annotation.NonNull;
-
-/**
- * Implements the Visitor design pattern to visit 2 related objects (like a view and the activity
- * hosting it).
- *
- * @param <V1> 1st visited object
- * @param <V2> 2nd visited object
- */
-public interface DoubleVisitor<V1, V2> {
-
-    /**
-     * Visit those objects.
-     */
-    void visit(@NonNull V1 visited1, @NonNull V2 visited2);
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/DummyActivity.java b/common/device-side/util/src/com/android/compatibility/common/util/DummyActivity.java
deleted file mode 100644
index 672106c..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/DummyActivity.java
+++ /dev/null
@@ -1,24 +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.
- */
-
-
-package com.android.compatibility.common.util;
-
-import android.app.Activity;
-
-public class DummyActivity extends Activity {
-
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/DynamicConfigDeviceSide.java b/common/device-side/util/src/com/android/compatibility/common/util/DynamicConfigDeviceSide.java
deleted file mode 100644
index 0e443fb..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/DynamicConfigDeviceSide.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.compatibility.common.util;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.net.Uri;
-import android.os.Environment;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-
-import android.support.test.InstrumentationRegistry;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.FileNotFoundException;
-
-/**
- * Load dynamic config for device side test cases
- */
-public class DynamicConfigDeviceSide extends DynamicConfig {
-
-    public static final String CONTENT_PROVIDER =
-            String.format("%s://android.tradefed.contentprovider", ContentResolver.SCHEME_CONTENT);
-
-    public DynamicConfigDeviceSide(String moduleName) throws XmlPullParserException, IOException {
-        this(moduleName, new File(CONFIG_FOLDER_ON_DEVICE));
-    }
-
-    public DynamicConfigDeviceSide(String moduleName, File configFolder)
-            throws XmlPullParserException, IOException {
-        if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
-            throw new IOException("External storage is not mounted");
-        }
-        // Use the content provider to get the config:
-        String uriPath = String.format("%s/%s/%s.dynamic", CONTENT_PROVIDER, configFolder.getAbsolutePath(), moduleName);
-        Uri sdcardUri = Uri.parse(uriPath);
-        Context appContext = InstrumentationRegistry.getTargetContext();
-        try {
-            ContentResolver resolver = appContext.getContentResolver();
-            ParcelFileDescriptor descriptor = resolver.openFileDescriptor(sdcardUri,"r");
-
-            initializeConfig(new ParcelFileDescriptor.AutoCloseInputStream(descriptor));
-            return;
-        } catch (FileNotFoundException e) {
-            // Log the error and use the fallback too
-            Log.e("DynamicConfigDeviceSide", "Error while using content provider for config", e);
-        }
-        // Fallback to the direct search
-        File configFile = getConfigFile(configFolder, moduleName);
-        initializeConfig(configFile);
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/FakeKeys.java b/common/device-side/util/src/com/android/compatibility/common/util/FakeKeys.java
deleted file mode 100644
index 85e06ea..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/FakeKeys.java
+++ /dev/null
@@ -1,469 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-// Copied from cts/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java
-
-public class FakeKeys {
-    /*
-     * The keys and certificates below are generated with:
-     *
-     * openssl req -new -x509 -days 3650 -extensions v3_ca -keyout cakey.pem -out cacert.pem
-     * openssl req -newkey rsa:1024 -keyout userkey.pem -nodes -days 3650 -out userkey.req
-     * mkdir -p demoCA/newcerts
-     * touch demoCA/index.txt
-     * echo "01" > demoCA/serial
-     * openssl ca -out usercert.pem -in userkey.req -cert cacert.pem -keyfile cakey.pem -days 3650
-     */
-    public static class FAKE_RSA_1 {
-        /**
-         * Generated from above and converted with:
-         *
-         * openssl pkcs8 -topk8 -outform d -in userkey.pem -nocrypt | xxd -i | sed 's/0x/(byte) 0x/g'
-         */
-        public static final byte[] privateKey = {
-            (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x78, (byte) 0x02, (byte) 0x01,
-            (byte) 0x00, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a,
-            (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01,
-            (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x04, (byte) 0x82,
-            (byte) 0x02, (byte) 0x62, (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x5e,
-            (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x81, (byte) 0x81,
-            (byte) 0x00, (byte) 0xce, (byte) 0x29, (byte) 0xeb, (byte) 0xf6, (byte) 0x5b,
-            (byte) 0x25, (byte) 0xdc, (byte) 0xa1, (byte) 0xa6, (byte) 0x2c, (byte) 0x66,
-            (byte) 0xcb, (byte) 0x20, (byte) 0x90, (byte) 0x27, (byte) 0x86, (byte) 0x8a,
-            (byte) 0x44, (byte) 0x71, (byte) 0x50, (byte) 0xda, (byte) 0xd3, (byte) 0x02,
-            (byte) 0x77, (byte) 0x55, (byte) 0xe9, (byte) 0xe8, (byte) 0x08, (byte) 0xf3,
-            (byte) 0x36, (byte) 0x9a, (byte) 0xae, (byte) 0xab, (byte) 0x04, (byte) 0x6d,
-            (byte) 0x00, (byte) 0x99, (byte) 0xbf, (byte) 0x7d, (byte) 0x0f, (byte) 0x67,
-            (byte) 0x8b, (byte) 0x1d, (byte) 0xd4, (byte) 0x2b, (byte) 0x7c, (byte) 0xcb,
-            (byte) 0xcd, (byte) 0x33, (byte) 0xc7, (byte) 0x84, (byte) 0x30, (byte) 0xe2,
-            (byte) 0x45, (byte) 0x21, (byte) 0xb3, (byte) 0x75, (byte) 0xf5, (byte) 0x79,
-            (byte) 0x02, (byte) 0xda, (byte) 0x50, (byte) 0xa3, (byte) 0x8b, (byte) 0xce,
-            (byte) 0xc3, (byte) 0x8e, (byte) 0x0f, (byte) 0x25, (byte) 0xeb, (byte) 0x08,
-            (byte) 0x2c, (byte) 0xdd, (byte) 0x1c, (byte) 0xcf, (byte) 0xff, (byte) 0x3b,
-            (byte) 0xde, (byte) 0xb6, (byte) 0xaa, (byte) 0x2a, (byte) 0xa9, (byte) 0xc4,
-            (byte) 0x8a, (byte) 0x24, (byte) 0x24, (byte) 0xe6, (byte) 0x29, (byte) 0x0d,
-            (byte) 0x98, (byte) 0x4c, (byte) 0x32, (byte) 0xa1, (byte) 0x7b, (byte) 0x23,
-            (byte) 0x2b, (byte) 0x42, (byte) 0x30, (byte) 0xee, (byte) 0x78, (byte) 0x08,
-            (byte) 0x47, (byte) 0xad, (byte) 0xf2, (byte) 0x96, (byte) 0xd5, (byte) 0xf1,
-            (byte) 0x62, (byte) 0x42, (byte) 0x2d, (byte) 0x35, (byte) 0x19, (byte) 0xb4,
-            (byte) 0x3c, (byte) 0xc9, (byte) 0xc3, (byte) 0x5f, (byte) 0x03, (byte) 0x16,
-            (byte) 0x3a, (byte) 0x23, (byte) 0xac, (byte) 0xcb, (byte) 0xce, (byte) 0x9e,
-            (byte) 0x51, (byte) 0x2e, (byte) 0x6d, (byte) 0x02, (byte) 0x03, (byte) 0x01,
-            (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x81, (byte) 0x80, (byte) 0x16,
-            (byte) 0x59, (byte) 0xc3, (byte) 0x24, (byte) 0x1d, (byte) 0x33, (byte) 0x98,
-            (byte) 0x9c, (byte) 0xc9, (byte) 0xc8, (byte) 0x2c, (byte) 0x88, (byte) 0xbf,
-            (byte) 0x0a, (byte) 0x01, (byte) 0xce, (byte) 0xfb, (byte) 0x34, (byte) 0x7a,
-            (byte) 0x58, (byte) 0x7a, (byte) 0xb0, (byte) 0xbf, (byte) 0xa6, (byte) 0xb2,
-            (byte) 0x60, (byte) 0xbe, (byte) 0x70, (byte) 0x21, (byte) 0xf5, (byte) 0xfc,
-            (byte) 0x85, (byte) 0x0d, (byte) 0x33, (byte) 0x58, (byte) 0xa1, (byte) 0xe5,
-            (byte) 0x09, (byte) 0x36, (byte) 0x84, (byte) 0xb2, (byte) 0x04, (byte) 0x0a,
-            (byte) 0x02, (byte) 0xd3, (byte) 0x88, (byte) 0x1f, (byte) 0x0c, (byte) 0x2b,
-            (byte) 0x1d, (byte) 0xe9, (byte) 0x3d, (byte) 0xe7, (byte) 0x79, (byte) 0xf9,
-            (byte) 0x32, (byte) 0x5c, (byte) 0x8a, (byte) 0x75, (byte) 0x49, (byte) 0x12,
-            (byte) 0xe4, (byte) 0x05, (byte) 0x26, (byte) 0xd4, (byte) 0x2e, (byte) 0x9e,
-            (byte) 0x1f, (byte) 0xcc, (byte) 0x54, (byte) 0xad, (byte) 0x33, (byte) 0x8d,
-            (byte) 0x99, (byte) 0x00, (byte) 0xdc, (byte) 0xf5, (byte) 0xb4, (byte) 0xa2,
-            (byte) 0x2f, (byte) 0xba, (byte) 0xe5, (byte) 0x62, (byte) 0x30, (byte) 0x6d,
-            (byte) 0xe6, (byte) 0x3d, (byte) 0xeb, (byte) 0x24, (byte) 0xc2, (byte) 0xdc,
-            (byte) 0x5f, (byte) 0xb7, (byte) 0x16, (byte) 0x35, (byte) 0xa3, (byte) 0x98,
-            (byte) 0x98, (byte) 0xa8, (byte) 0xef, (byte) 0xe8, (byte) 0xc4, (byte) 0x96,
-            (byte) 0x6d, (byte) 0x38, (byte) 0xab, (byte) 0x26, (byte) 0x6d, (byte) 0x30,
-            (byte) 0xc2, (byte) 0xa0, (byte) 0x44, (byte) 0xe4, (byte) 0xff, (byte) 0x7e,
-            (byte) 0xbe, (byte) 0x7c, (byte) 0x33, (byte) 0xa5, (byte) 0x10, (byte) 0xad,
-            (byte) 0xd7, (byte) 0x1e, (byte) 0x13, (byte) 0x20, (byte) 0xb3, (byte) 0x1f,
-            (byte) 0x41, (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xf1, (byte) 0x89,
-            (byte) 0x07, (byte) 0x0f, (byte) 0xe8, (byte) 0xcf, (byte) 0xab, (byte) 0x13,
-            (byte) 0x2a, (byte) 0x8f, (byte) 0x88, (byte) 0x80, (byte) 0x11, (byte) 0x9a,
-            (byte) 0x79, (byte) 0xb6, (byte) 0x59, (byte) 0x3a, (byte) 0x50, (byte) 0x6e,
-            (byte) 0x57, (byte) 0x37, (byte) 0xab, (byte) 0x2a, (byte) 0xd2, (byte) 0xaa,
-            (byte) 0xd9, (byte) 0x72, (byte) 0x73, (byte) 0xff, (byte) 0x8b, (byte) 0x47,
-            (byte) 0x76, (byte) 0xdd, (byte) 0xdc, (byte) 0xf5, (byte) 0x97, (byte) 0x44,
-            (byte) 0x3a, (byte) 0x78, (byte) 0xbe, (byte) 0x17, (byte) 0xb4, (byte) 0x22,
-            (byte) 0x6f, (byte) 0xe5, (byte) 0x23, (byte) 0x70, (byte) 0x1d, (byte) 0x10,
-            (byte) 0x5d, (byte) 0xba, (byte) 0x16, (byte) 0x81, (byte) 0xf1, (byte) 0x45,
-            (byte) 0xce, (byte) 0x30, (byte) 0xb4, (byte) 0xab, (byte) 0x80, (byte) 0xe4,
-            (byte) 0x98, (byte) 0x31, (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xda,
-            (byte) 0x82, (byte) 0x9d, (byte) 0x3f, (byte) 0xca, (byte) 0x2f, (byte) 0xe1,
-            (byte) 0xd4, (byte) 0x86, (byte) 0x77, (byte) 0x48, (byte) 0xa6, (byte) 0xab,
-            (byte) 0xab, (byte) 0x1c, (byte) 0x42, (byte) 0x5c, (byte) 0xd5, (byte) 0xc7,
-            (byte) 0x46, (byte) 0x59, (byte) 0x91, (byte) 0x3f, (byte) 0xfc, (byte) 0xcc,
-            (byte) 0xec, (byte) 0xc2, (byte) 0x40, (byte) 0x12, (byte) 0x2c, (byte) 0x8d,
-            (byte) 0x1f, (byte) 0xa2, (byte) 0x18, (byte) 0x88, (byte) 0xee, (byte) 0x82,
-            (byte) 0x4a, (byte) 0x5a, (byte) 0x5e, (byte) 0x88, (byte) 0x20, (byte) 0xe3,
-            (byte) 0x7b, (byte) 0xe0, (byte) 0xd8, (byte) 0x3a, (byte) 0x52, (byte) 0x9a,
-            (byte) 0x26, (byte) 0x6a, (byte) 0x04, (byte) 0xec, (byte) 0xe8, (byte) 0xb9,
-            (byte) 0x48, (byte) 0x40, (byte) 0xe1, (byte) 0xe1, (byte) 0x83, (byte) 0xa6,
-            (byte) 0x67, (byte) 0xa6, (byte) 0xfd, (byte) 0x02, (byte) 0x41, (byte) 0x00,
-            (byte) 0x89, (byte) 0x72, (byte) 0x3e, (byte) 0xb0, (byte) 0x90, (byte) 0xfd,
-            (byte) 0x4c, (byte) 0x0e, (byte) 0xd6, (byte) 0x13, (byte) 0x63, (byte) 0xcb,
-            (byte) 0xed, (byte) 0x38, (byte) 0x88, (byte) 0xb6, (byte) 0x79, (byte) 0xc4,
-            (byte) 0x33, (byte) 0x6c, (byte) 0xf6, (byte) 0xf8, (byte) 0xd8, (byte) 0xd0,
-            (byte) 0xbf, (byte) 0x9d, (byte) 0x35, (byte) 0xac, (byte) 0x69, (byte) 0xd2,
-            (byte) 0x2b, (byte) 0xc1, (byte) 0xf9, (byte) 0x24, (byte) 0x7b, (byte) 0xce,
-            (byte) 0xcd, (byte) 0xcb, (byte) 0xa7, (byte) 0xb2, (byte) 0x7a, (byte) 0x0a,
-            (byte) 0x27, (byte) 0x19, (byte) 0xc9, (byte) 0xaf, (byte) 0x0d, (byte) 0x21,
-            (byte) 0x89, (byte) 0x88, (byte) 0x7c, (byte) 0xad, (byte) 0x9e, (byte) 0x8d,
-            (byte) 0x47, (byte) 0x6d, (byte) 0x3f, (byte) 0xce, (byte) 0x7b, (byte) 0xa1,
-            (byte) 0x74, (byte) 0xf1, (byte) 0xa0, (byte) 0xa1, (byte) 0x02, (byte) 0x41,
-            (byte) 0x00, (byte) 0xd9, (byte) 0xa8, (byte) 0xf5, (byte) 0xfe, (byte) 0xce,
-            (byte) 0xe6, (byte) 0x77, (byte) 0x6b, (byte) 0xfe, (byte) 0x2d, (byte) 0xe0,
-            (byte) 0x1e, (byte) 0xb6, (byte) 0x2e, (byte) 0x12, (byte) 0x4e, (byte) 0x40,
-            (byte) 0xaf, (byte) 0x6a, (byte) 0x7b, (byte) 0x37, (byte) 0x49, (byte) 0x2a,
-            (byte) 0x96, (byte) 0x25, (byte) 0x83, (byte) 0x49, (byte) 0xd4, (byte) 0x0c,
-            (byte) 0xc6, (byte) 0x78, (byte) 0x25, (byte) 0x24, (byte) 0x90, (byte) 0x90,
-            (byte) 0x06, (byte) 0x15, (byte) 0x9e, (byte) 0xfe, (byte) 0xf9, (byte) 0xdf,
-            (byte) 0x5b, (byte) 0xf3, (byte) 0x7e, (byte) 0x38, (byte) 0x70, (byte) 0xeb,
-            (byte) 0x57, (byte) 0xd0, (byte) 0xd9, (byte) 0xa7, (byte) 0x0e, (byte) 0x14,
-            (byte) 0xf7, (byte) 0x95, (byte) 0x68, (byte) 0xd5, (byte) 0xc8, (byte) 0xab,
-            (byte) 0x9d, (byte) 0x3a, (byte) 0x2b, (byte) 0x51, (byte) 0xf9, (byte) 0x02,
-            (byte) 0x41, (byte) 0x00, (byte) 0x96, (byte) 0xdf, (byte) 0xe9, (byte) 0x67,
-            (byte) 0x6c, (byte) 0xdc, (byte) 0x90, (byte) 0x14, (byte) 0xb4, (byte) 0x1d,
-            (byte) 0x22, (byte) 0x33, (byte) 0x4a, (byte) 0x31, (byte) 0xc1, (byte) 0x9d,
-            (byte) 0x2e, (byte) 0xff, (byte) 0x9a, (byte) 0x2a, (byte) 0x95, (byte) 0x4b,
-            (byte) 0x27, (byte) 0x74, (byte) 0xcb, (byte) 0x21, (byte) 0xc3, (byte) 0xd2,
-            (byte) 0x0b, (byte) 0xb2, (byte) 0x46, (byte) 0x87, (byte) 0xf8, (byte) 0x28,
-            (byte) 0x01, (byte) 0x8b, (byte) 0xd8, (byte) 0xb9, (byte) 0x4b, (byte) 0xcd,
-            (byte) 0x9a, (byte) 0x96, (byte) 0x41, (byte) 0x0e, (byte) 0x36, (byte) 0x6d,
-            (byte) 0x40, (byte) 0x42, (byte) 0xbc, (byte) 0xd9, (byte) 0xd3, (byte) 0x7b,
-            (byte) 0xbc, (byte) 0xa7, (byte) 0x92, (byte) 0x90, (byte) 0xdd, (byte) 0xa1,
-            (byte) 0x9c, (byte) 0xce, (byte) 0xa1, (byte) 0x87, (byte) 0x11, (byte) 0x51
-        };
-
-        /**
-         * Generated from above and converted with:
-         *
-         * openssl x509 -outform d -in cacert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
-         */
-        public static final byte[] caCertificate = {
-            (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0xce, (byte) 0x30, (byte) 0x82,
-            (byte) 0x02, (byte) 0x37, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
-            (byte) 0x02, (byte) 0x02, (byte) 0x09, (byte) 0x00, (byte) 0xe1, (byte) 0x6a,
-            (byte) 0xa2, (byte) 0xf4, (byte) 0x2e, (byte) 0x55, (byte) 0x48, (byte) 0x0a,
-            (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
-            (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
-            (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x30, (byte) 0x4f, (byte) 0x31,
-            (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55,
-            (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53,
-            (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03,
-            (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x13, (byte) 0x02, (byte) 0x43,
-            (byte) 0x41, (byte) 0x31, (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06,
-            (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x13, (byte) 0x0d,
-            (byte) 0x4d, (byte) 0x6f, (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61,
-            (byte) 0x69, (byte) 0x6e, (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65,
-            (byte) 0x77, (byte) 0x31, (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06,
-            (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x13, (byte) 0x12,
-            (byte) 0x41, (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69,
-            (byte) 0x64, (byte) 0x20, (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74,
-            (byte) 0x20, (byte) 0x43, (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x73,
-            (byte) 0x30, (byte) 0x1e, (byte) 0x17, (byte) 0x0d, (byte) 0x31, (byte) 0x32,
-            (byte) 0x30, (byte) 0x38, (byte) 0x31, (byte) 0x34, (byte) 0x31, (byte) 0x36,
-            (byte) 0x35, (byte) 0x35, (byte) 0x34, (byte) 0x34, (byte) 0x5a, (byte) 0x17,
-            (byte) 0x0d, (byte) 0x32, (byte) 0x32, (byte) 0x30, (byte) 0x38, (byte) 0x31,
-            (byte) 0x32, (byte) 0x31, (byte) 0x36, (byte) 0x35, (byte) 0x35, (byte) 0x34,
-            (byte) 0x34, (byte) 0x5a, (byte) 0x30, (byte) 0x4f, (byte) 0x31, (byte) 0x0b,
-            (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
-            (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31,
-            (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55,
-            (byte) 0x04, (byte) 0x08, (byte) 0x13, (byte) 0x02, (byte) 0x43, (byte) 0x41,
-            (byte) 0x31, (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06, (byte) 0x03,
-            (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x13, (byte) 0x0d, (byte) 0x4d,
-            (byte) 0x6f, (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61, (byte) 0x69,
-            (byte) 0x6e, (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65, (byte) 0x77,
-            (byte) 0x31, (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03,
-            (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x13, (byte) 0x12, (byte) 0x41,
-            (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64,
-            (byte) 0x20, (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x20,
-            (byte) 0x43, (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x73, (byte) 0x30,
-            (byte) 0x81, (byte) 0x9f, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09,
-            (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d,
-            (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x03,
-            (byte) 0x81, (byte) 0x8d, (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0x89,
-            (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xa3, (byte) 0x72,
-            (byte) 0xab, (byte) 0xd0, (byte) 0xe4, (byte) 0xad, (byte) 0x2f, (byte) 0xe7,
-            (byte) 0xe2, (byte) 0x79, (byte) 0x07, (byte) 0x36, (byte) 0x3d, (byte) 0x0c,
-            (byte) 0x8d, (byte) 0x42, (byte) 0x9a, (byte) 0x0a, (byte) 0x33, (byte) 0x64,
-            (byte) 0xb3, (byte) 0xcd, (byte) 0xb2, (byte) 0xd7, (byte) 0x3a, (byte) 0x42,
-            (byte) 0x06, (byte) 0x77, (byte) 0x45, (byte) 0x29, (byte) 0xe9, (byte) 0xcb,
-            (byte) 0xb7, (byte) 0x4a, (byte) 0xd6, (byte) 0xee, (byte) 0xad, (byte) 0x01,
-            (byte) 0x91, (byte) 0x9b, (byte) 0x0c, (byte) 0x59, (byte) 0xa1, (byte) 0x03,
-            (byte) 0xfa, (byte) 0xf0, (byte) 0x5a, (byte) 0x7c, (byte) 0x4f, (byte) 0xf7,
-            (byte) 0x8d, (byte) 0x36, (byte) 0x0f, (byte) 0x1f, (byte) 0x45, (byte) 0x7d,
-            (byte) 0x1b, (byte) 0x31, (byte) 0xa1, (byte) 0x35, (byte) 0x0b, (byte) 0x00,
-            (byte) 0xed, (byte) 0x7a, (byte) 0xb6, (byte) 0xc8, (byte) 0x4e, (byte) 0xa9,
-            (byte) 0x86, (byte) 0x4c, (byte) 0x7b, (byte) 0x99, (byte) 0x57, (byte) 0x41,
-            (byte) 0x12, (byte) 0xef, (byte) 0x6b, (byte) 0xbc, (byte) 0x3d, (byte) 0x60,
-            (byte) 0xf2, (byte) 0x99, (byte) 0x1a, (byte) 0xcd, (byte) 0xed, (byte) 0x56,
-            (byte) 0xa4, (byte) 0xe5, (byte) 0x36, (byte) 0x9f, (byte) 0x24, (byte) 0x1f,
-            (byte) 0xdc, (byte) 0x89, (byte) 0x40, (byte) 0xc8, (byte) 0x99, (byte) 0x92,
-            (byte) 0xab, (byte) 0x4a, (byte) 0xb5, (byte) 0x61, (byte) 0x45, (byte) 0x62,
-            (byte) 0xff, (byte) 0xa3, (byte) 0x45, (byte) 0x65, (byte) 0xaf, (byte) 0xf6,
-            (byte) 0x27, (byte) 0x30, (byte) 0x51, (byte) 0x0e, (byte) 0x0e, (byte) 0xeb,
-            (byte) 0x79, (byte) 0x0c, (byte) 0xbe, (byte) 0xb3, (byte) 0x0a, (byte) 0x6f,
-            (byte) 0x29, (byte) 0x06, (byte) 0xdc, (byte) 0x2f, (byte) 0x6b, (byte) 0x51,
-            (byte) 0x02, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xa3,
-            (byte) 0x81, (byte) 0xb1, (byte) 0x30, (byte) 0x81, (byte) 0xae, (byte) 0x30,
-            (byte) 0x1d, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x0e,
-            (byte) 0x04, (byte) 0x16, (byte) 0x04, (byte) 0x14, (byte) 0x33, (byte) 0x05,
-            (byte) 0xee, (byte) 0xfe, (byte) 0x6f, (byte) 0x60, (byte) 0xc7, (byte) 0xf9,
-            (byte) 0xa9, (byte) 0xd2, (byte) 0x73, (byte) 0x5c, (byte) 0x8f, (byte) 0x6d,
-            (byte) 0xa2, (byte) 0x2f, (byte) 0x97, (byte) 0x8e, (byte) 0x5d, (byte) 0x51,
-            (byte) 0x30, (byte) 0x7f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d,
-            (byte) 0x23, (byte) 0x04, (byte) 0x78, (byte) 0x30, (byte) 0x76, (byte) 0x80,
-            (byte) 0x14, (byte) 0x33, (byte) 0x05, (byte) 0xee, (byte) 0xfe, (byte) 0x6f,
-            (byte) 0x60, (byte) 0xc7, (byte) 0xf9, (byte) 0xa9, (byte) 0xd2, (byte) 0x73,
-            (byte) 0x5c, (byte) 0x8f, (byte) 0x6d, (byte) 0xa2, (byte) 0x2f, (byte) 0x97,
-            (byte) 0x8e, (byte) 0x5d, (byte) 0x51, (byte) 0xa1, (byte) 0x53, (byte) 0xa4,
-            (byte) 0x51, (byte) 0x30, (byte) 0x4f, (byte) 0x31, (byte) 0x0b, (byte) 0x30,
-            (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06,
-            (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x0b,
-            (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
-            (byte) 0x08, (byte) 0x13, (byte) 0x02, (byte) 0x43, (byte) 0x41, (byte) 0x31,
-            (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06, (byte) 0x03, (byte) 0x55,
-            (byte) 0x04, (byte) 0x07, (byte) 0x13, (byte) 0x0d, (byte) 0x4d, (byte) 0x6f,
-            (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61, (byte) 0x69, (byte) 0x6e,
-            (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65, (byte) 0x77, (byte) 0x31,
-            (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03, (byte) 0x55,
-            (byte) 0x04, (byte) 0x0a, (byte) 0x13, (byte) 0x12, (byte) 0x41, (byte) 0x6e,
-            (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x20,
-            (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x20, (byte) 0x43,
-            (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x73, (byte) 0x82, (byte) 0x09,
-            (byte) 0x00, (byte) 0xe1, (byte) 0x6a, (byte) 0xa2, (byte) 0xf4, (byte) 0x2e,
-            (byte) 0x55, (byte) 0x48, (byte) 0x0a, (byte) 0x30, (byte) 0x0c, (byte) 0x06,
-            (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x13, (byte) 0x04, (byte) 0x05,
-            (byte) 0x30, (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0xff, (byte) 0x30,
-            (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48,
-            (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x05,
-            (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81, (byte) 0x81, (byte) 0x00,
-            (byte) 0x8c, (byte) 0x30, (byte) 0x42, (byte) 0xfa, (byte) 0xeb, (byte) 0x1a,
-            (byte) 0x26, (byte) 0xeb, (byte) 0xda, (byte) 0x56, (byte) 0x32, (byte) 0xf2,
-            (byte) 0x9d, (byte) 0xa5, (byte) 0x24, (byte) 0xd8, (byte) 0x3a, (byte) 0xda,
-            (byte) 0x30, (byte) 0xa6, (byte) 0x8b, (byte) 0x46, (byte) 0xfe, (byte) 0xfe,
-            (byte) 0xdb, (byte) 0xf1, (byte) 0xe6, (byte) 0xe1, (byte) 0x7c, (byte) 0x1b,
-            (byte) 0xe7, (byte) 0x77, (byte) 0x00, (byte) 0xa1, (byte) 0x1c, (byte) 0x19,
-            (byte) 0x17, (byte) 0x73, (byte) 0xb0, (byte) 0xf0, (byte) 0x9d, (byte) 0xf3,
-            (byte) 0x4f, (byte) 0xb6, (byte) 0xbc, (byte) 0xc7, (byte) 0x47, (byte) 0x85,
-            (byte) 0x2a, (byte) 0x4a, (byte) 0xa1, (byte) 0xa5, (byte) 0x58, (byte) 0xf5,
-            (byte) 0xc5, (byte) 0x1a, (byte) 0x51, (byte) 0xb1, (byte) 0x04, (byte) 0x80,
-            (byte) 0xee, (byte) 0x3a, (byte) 0xec, (byte) 0x2f, (byte) 0xe1, (byte) 0xfd,
-            (byte) 0x58, (byte) 0xeb, (byte) 0xed, (byte) 0x82, (byte) 0x9e, (byte) 0x38,
-            (byte) 0xa3, (byte) 0x24, (byte) 0x75, (byte) 0xf7, (byte) 0x3e, (byte) 0xc2,
-            (byte) 0xc5, (byte) 0x27, (byte) 0xeb, (byte) 0x6f, (byte) 0x7b, (byte) 0x50,
-            (byte) 0xda, (byte) 0x43, (byte) 0xdc, (byte) 0x3b, (byte) 0x0b, (byte) 0x6f,
-            (byte) 0x78, (byte) 0x8f, (byte) 0xb0, (byte) 0x66, (byte) 0xe1, (byte) 0x12,
-            (byte) 0x87, (byte) 0x5f, (byte) 0x97, (byte) 0x7b, (byte) 0xca, (byte) 0x14,
-            (byte) 0x79, (byte) 0xf7, (byte) 0xe8, (byte) 0x6c, (byte) 0x72, (byte) 0xdb,
-            (byte) 0x91, (byte) 0x65, (byte) 0x17, (byte) 0x54, (byte) 0xe0, (byte) 0x74,
-            (byte) 0x1d, (byte) 0xac, (byte) 0x47, (byte) 0x04, (byte) 0x12, (byte) 0xe0,
-            (byte) 0xc3, (byte) 0x66, (byte) 0x19, (byte) 0x05, (byte) 0x2e, (byte) 0x7e,
-            (byte) 0xf1, (byte) 0x61
-        };
-    }
-
-    /*
-     * The keys and certificates below are generated with:
-     *
-     * openssl req -new -x509 -days 3650 -extensions v3_ca -keyout cakey.pem -out cacert.pem
-     * openssl dsaparam -out dsaparam.pem 1024
-     * openssl req -newkey dsa:dsaparam.pem -keyout userkey.pem -nodes -days 3650 -out userkey.req
-     * mkdir -p demoCA/newcerts
-     * touch demoCA/index.txt
-     * echo "01" > demoCA/serial
-     * openssl ca -out usercert.pem -in userkey.req -cert cacert.pem -keyfile cakey.pem -days 3650
-     */
-    public static class FAKE_DSA_1 {
-        /**
-         * Generated from above and converted with: openssl pkcs8 -topk8 -outform d
-         * -in userkey.pem -nocrypt | xxd -i | sed 's/0x/(byte) 0x/g'
-         */
-        public static final byte[] privateKey = {
-            (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0x4c, (byte) 0x02, (byte) 0x01,
-            (byte) 0x00, (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0x2c, (byte) 0x06,
-            (byte) 0x07, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0xce, (byte) 0x38,
-            (byte) 0x04, (byte) 0x01, (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0x1f,
-            (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xb3, (byte) 0x23,
-            (byte) 0xf7, (byte) 0x86, (byte) 0xbd, (byte) 0x3b, (byte) 0x86, (byte) 0xcc,
-            (byte) 0xc3, (byte) 0x91, (byte) 0xc0, (byte) 0x30, (byte) 0x32, (byte) 0x02,
-            (byte) 0x47, (byte) 0x35, (byte) 0x01, (byte) 0xef, (byte) 0xee, (byte) 0x98,
-            (byte) 0x13, (byte) 0x56, (byte) 0x49, (byte) 0x47, (byte) 0xb5, (byte) 0x20,
-            (byte) 0xa8, (byte) 0x60, (byte) 0xcb, (byte) 0xc0, (byte) 0xd5, (byte) 0x77,
-            (byte) 0xc1, (byte) 0x69, (byte) 0xcd, (byte) 0x18, (byte) 0x34, (byte) 0x92,
-            (byte) 0xf2, (byte) 0x6a, (byte) 0x2a, (byte) 0x10, (byte) 0x59, (byte) 0x1c,
-            (byte) 0x91, (byte) 0x20, (byte) 0x51, (byte) 0xca, (byte) 0x37, (byte) 0xb2,
-            (byte) 0x87, (byte) 0xa6, (byte) 0x8a, (byte) 0x02, (byte) 0xfd, (byte) 0x45,
-            (byte) 0x46, (byte) 0xf9, (byte) 0x76, (byte) 0xb1, (byte) 0x35, (byte) 0x38,
-            (byte) 0x8d, (byte) 0xff, (byte) 0x4c, (byte) 0x5d, (byte) 0x75, (byte) 0x8f,
-            (byte) 0x66, (byte) 0x15, (byte) 0x7d, (byte) 0x7b, (byte) 0xda, (byte) 0xdb,
-            (byte) 0x57, (byte) 0x39, (byte) 0xff, (byte) 0x91, (byte) 0x3f, (byte) 0xdd,
-            (byte) 0xe2, (byte) 0xb4, (byte) 0x22, (byte) 0x60, (byte) 0x4c, (byte) 0x32,
-            (byte) 0x3b, (byte) 0x9d, (byte) 0x34, (byte) 0x9f, (byte) 0xb9, (byte) 0x5d,
-            (byte) 0x75, (byte) 0xb9, (byte) 0xd3, (byte) 0x7f, (byte) 0x11, (byte) 0xba,
-            (byte) 0xb7, (byte) 0xc8, (byte) 0x32, (byte) 0xc6, (byte) 0xce, (byte) 0x71,
-            (byte) 0x91, (byte) 0xd3, (byte) 0x32, (byte) 0xaf, (byte) 0x4d, (byte) 0x7e,
-            (byte) 0x7c, (byte) 0x15, (byte) 0xf7, (byte) 0x71, (byte) 0x2c, (byte) 0x52,
-            (byte) 0x65, (byte) 0x4d, (byte) 0xa9, (byte) 0x81, (byte) 0x25, (byte) 0x35,
-            (byte) 0xce, (byte) 0x0b, (byte) 0x5b, (byte) 0x56, (byte) 0xfe, (byte) 0xf1,
-            (byte) 0x02, (byte) 0x15, (byte) 0x00, (byte) 0xeb, (byte) 0x4e, (byte) 0x7f,
-            (byte) 0x7a, (byte) 0x31, (byte) 0xb3, (byte) 0x7d, (byte) 0x8d, (byte) 0xb2,
-            (byte) 0xf7, (byte) 0xaf, (byte) 0xad, (byte) 0xb1, (byte) 0x42, (byte) 0x92,
-            (byte) 0xf3, (byte) 0x6c, (byte) 0xe4, (byte) 0xed, (byte) 0x8b, (byte) 0x02,
-            (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0x81, (byte) 0xc8, (byte) 0x36,
-            (byte) 0x48, (byte) 0xdb, (byte) 0x71, (byte) 0x2b, (byte) 0x91, (byte) 0xce,
-            (byte) 0x6d, (byte) 0xbc, (byte) 0xb8, (byte) 0xf9, (byte) 0xcb, (byte) 0x50,
-            (byte) 0x91, (byte) 0x10, (byte) 0x8a, (byte) 0xf8, (byte) 0x37, (byte) 0x50,
-            (byte) 0xda, (byte) 0x4f, (byte) 0xc8, (byte) 0x4d, (byte) 0x73, (byte) 0xcb,
-            (byte) 0x4d, (byte) 0xb0, (byte) 0x19, (byte) 0x54, (byte) 0x5a, (byte) 0xf3,
-            (byte) 0x6c, (byte) 0xc9, (byte) 0xd8, (byte) 0x96, (byte) 0xd9, (byte) 0xb0,
-            (byte) 0x54, (byte) 0x7e, (byte) 0x7d, (byte) 0xe2, (byte) 0x58, (byte) 0x0e,
-            (byte) 0x5f, (byte) 0xc0, (byte) 0xce, (byte) 0xb9, (byte) 0x5c, (byte) 0xe3,
-            (byte) 0xd3, (byte) 0xdf, (byte) 0xcf, (byte) 0x45, (byte) 0x74, (byte) 0xfb,
-            (byte) 0xe6, (byte) 0x20, (byte) 0xe7, (byte) 0xfc, (byte) 0x0f, (byte) 0xca,
-            (byte) 0xdb, (byte) 0xc0, (byte) 0x0b, (byte) 0xe1, (byte) 0x5a, (byte) 0x16,
-            (byte) 0x1d, (byte) 0xb3, (byte) 0x2e, (byte) 0xe5, (byte) 0x5f, (byte) 0x89,
-            (byte) 0x17, (byte) 0x73, (byte) 0x50, (byte) 0xd1, (byte) 0x4a, (byte) 0x60,
-            (byte) 0xb7, (byte) 0xaa, (byte) 0xf0, (byte) 0xc7, (byte) 0xc5, (byte) 0x03,
-            (byte) 0x4e, (byte) 0x36, (byte) 0x51, (byte) 0x9e, (byte) 0x2f, (byte) 0xfa,
-            (byte) 0xf3, (byte) 0xd6, (byte) 0x58, (byte) 0x14, (byte) 0x02, (byte) 0xb4,
-            (byte) 0x41, (byte) 0xd6, (byte) 0x72, (byte) 0x6f, (byte) 0x58, (byte) 0x5b,
-            (byte) 0x2d, (byte) 0x23, (byte) 0xc0, (byte) 0x75, (byte) 0x4f, (byte) 0x39,
-            (byte) 0xa8, (byte) 0x6a, (byte) 0xdf, (byte) 0x79, (byte) 0x21, (byte) 0xf2,
-            (byte) 0x77, (byte) 0x91, (byte) 0x3f, (byte) 0x1c, (byte) 0x4d, (byte) 0x48,
-            (byte) 0x78, (byte) 0xcd, (byte) 0xed, (byte) 0x79, (byte) 0x23, (byte) 0x04,
-            (byte) 0x17, (byte) 0x02, (byte) 0x15, (byte) 0x00, (byte) 0xc7, (byte) 0xe7,
-            (byte) 0xe2, (byte) 0x6b, (byte) 0x14, (byte) 0xe6, (byte) 0x31, (byte) 0x12,
-            (byte) 0xb2, (byte) 0x1e, (byte) 0xd4, (byte) 0xf2, (byte) 0x9b, (byte) 0x2c,
-            (byte) 0xf6, (byte) 0x54, (byte) 0x4c, (byte) 0x12, (byte) 0xe8, (byte) 0x22
-
-        };
-
-        /**
-         * Generated from above and converted with:
-         *
-         * openssl x509 -outform d -in cacert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
-         */
-        public static final byte[] caCertificate = new byte[] {
-            (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x8a, (byte) 0x30, (byte) 0x82,
-            (byte) 0x01, (byte) 0xf3, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
-            (byte) 0x02, (byte) 0x02, (byte) 0x09, (byte) 0x00, (byte) 0x87, (byte) 0xc0,
-            (byte) 0x68, (byte) 0x7f, (byte) 0x42, (byte) 0x92, (byte) 0x0b, (byte) 0x7a,
-            (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
-            (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
-            (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x30, (byte) 0x5e, (byte) 0x31,
-            (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55,
-            (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x41, (byte) 0x55,
-            (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03,
-            (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0c, (byte) 0x0a, (byte) 0x53,
-            (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53, (byte) 0x74,
-            (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x31, (byte) 0x21, (byte) 0x30,
-            (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a,
-            (byte) 0x0c, (byte) 0x18, (byte) 0x49, (byte) 0x6e, (byte) 0x74, (byte) 0x65,
-            (byte) 0x72, (byte) 0x6e, (byte) 0x65, (byte) 0x74, (byte) 0x20, (byte) 0x57,
-            (byte) 0x69, (byte) 0x64, (byte) 0x67, (byte) 0x69, (byte) 0x74, (byte) 0x73,
-            (byte) 0x20, (byte) 0x50, (byte) 0x74, (byte) 0x79, (byte) 0x20, (byte) 0x4c,
-            (byte) 0x74, (byte) 0x64, (byte) 0x31, (byte) 0x17, (byte) 0x30, (byte) 0x15,
-            (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x03, (byte) 0x0c,
-            (byte) 0x0e, (byte) 0x63, (byte) 0x61, (byte) 0x2e, (byte) 0x65, (byte) 0x78,
-            (byte) 0x61, (byte) 0x6d, (byte) 0x70, (byte) 0x6c, (byte) 0x65, (byte) 0x2e,
-            (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x30, (byte) 0x1e, (byte) 0x17,
-            (byte) 0x0d, (byte) 0x31, (byte) 0x33, (byte) 0x30, (byte) 0x38, (byte) 0x32,
-            (byte) 0x37, (byte) 0x32, (byte) 0x33, (byte) 0x33, (byte) 0x31, (byte) 0x32,
-            (byte) 0x39, (byte) 0x5a, (byte) 0x17, (byte) 0x0d, (byte) 0x32, (byte) 0x33,
-            (byte) 0x30, (byte) 0x38, (byte) 0x32, (byte) 0x35, (byte) 0x32, (byte) 0x33,
-            (byte) 0x33, (byte) 0x31, (byte) 0x32, (byte) 0x39, (byte) 0x5a, (byte) 0x30,
-            (byte) 0x5e, (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06,
-            (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02,
-            (byte) 0x41, (byte) 0x55, (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11,
-            (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0c,
-            (byte) 0x0a, (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d,
-            (byte) 0x53, (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x31,
-            (byte) 0x21, (byte) 0x30, (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55,
-            (byte) 0x04, (byte) 0x0a, (byte) 0x0c, (byte) 0x18, (byte) 0x49, (byte) 0x6e,
-            (byte) 0x74, (byte) 0x65, (byte) 0x72, (byte) 0x6e, (byte) 0x65, (byte) 0x74,
-            (byte) 0x20, (byte) 0x57, (byte) 0x69, (byte) 0x64, (byte) 0x67, (byte) 0x69,
-            (byte) 0x74, (byte) 0x73, (byte) 0x20, (byte) 0x50, (byte) 0x74, (byte) 0x79,
-            (byte) 0x20, (byte) 0x4c, (byte) 0x74, (byte) 0x64, (byte) 0x31, (byte) 0x17,
-            (byte) 0x30, (byte) 0x15, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
-            (byte) 0x03, (byte) 0x0c, (byte) 0x0e, (byte) 0x63, (byte) 0x61, (byte) 0x2e,
-            (byte) 0x65, (byte) 0x78, (byte) 0x61, (byte) 0x6d, (byte) 0x70, (byte) 0x6c,
-            (byte) 0x65, (byte) 0x2e, (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x30,
-            (byte) 0x81, (byte) 0x9f, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09,
-            (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d,
-            (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x03,
-            (byte) 0x81, (byte) 0x8d, (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0x89,
-            (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xa4, (byte) 0xc7,
-            (byte) 0x06, (byte) 0xba, (byte) 0xdf, (byte) 0x2b, (byte) 0xee, (byte) 0xd2,
-            (byte) 0xb9, (byte) 0xe4, (byte) 0x52, (byte) 0x21, (byte) 0x68, (byte) 0x2b,
-            (byte) 0x83, (byte) 0xdf, (byte) 0xe3, (byte) 0x9c, (byte) 0x08, (byte) 0x73,
-            (byte) 0xdd, (byte) 0x90, (byte) 0xea, (byte) 0x97, (byte) 0x0c, (byte) 0x96,
-            (byte) 0x20, (byte) 0xb1, (byte) 0xee, (byte) 0x11, (byte) 0xd5, (byte) 0xd4,
-            (byte) 0x7c, (byte) 0x44, (byte) 0x96, (byte) 0x2e, (byte) 0x6e, (byte) 0xa2,
-            (byte) 0xb2, (byte) 0xa3, (byte) 0x4b, (byte) 0x0f, (byte) 0x32, (byte) 0x90,
-            (byte) 0xaf, (byte) 0x5c, (byte) 0x6f, (byte) 0x00, (byte) 0x88, (byte) 0x45,
-            (byte) 0x4e, (byte) 0x9b, (byte) 0x26, (byte) 0xc1, (byte) 0x94, (byte) 0x3c,
-            (byte) 0xfe, (byte) 0x10, (byte) 0xbd, (byte) 0xda, (byte) 0xf2, (byte) 0x8d,
-            (byte) 0x03, (byte) 0x52, (byte) 0x32, (byte) 0x11, (byte) 0xff, (byte) 0xf6,
-            (byte) 0xf9, (byte) 0x6e, (byte) 0x8f, (byte) 0x0f, (byte) 0xc8, (byte) 0x0a,
-            (byte) 0x48, (byte) 0x39, (byte) 0x33, (byte) 0xb9, (byte) 0x0c, (byte) 0xb3,
-            (byte) 0x2b, (byte) 0xab, (byte) 0x7d, (byte) 0x79, (byte) 0x6f, (byte) 0x57,
-            (byte) 0x5b, (byte) 0xb8, (byte) 0x84, (byte) 0xb6, (byte) 0xcc, (byte) 0xe8,
-            (byte) 0x30, (byte) 0x78, (byte) 0xff, (byte) 0x92, (byte) 0xe5, (byte) 0x43,
-            (byte) 0x2e, (byte) 0xef, (byte) 0x66, (byte) 0x98, (byte) 0xb4, (byte) 0xfe,
-            (byte) 0xa2, (byte) 0x40, (byte) 0xf2, (byte) 0x1f, (byte) 0xd0, (byte) 0x86,
-            (byte) 0x16, (byte) 0xc8, (byte) 0x45, (byte) 0xc4, (byte) 0x52, (byte) 0xcb,
-            (byte) 0x31, (byte) 0x5c, (byte) 0x9f, (byte) 0x32, (byte) 0x3b, (byte) 0xf7,
-            (byte) 0x19, (byte) 0x08, (byte) 0xc7, (byte) 0x00, (byte) 0x21, (byte) 0x7d,
-            (byte) 0x02, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xa3,
-            (byte) 0x50, (byte) 0x30, (byte) 0x4e, (byte) 0x30, (byte) 0x1d, (byte) 0x06,
-            (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x0e, (byte) 0x04, (byte) 0x16,
-            (byte) 0x04, (byte) 0x14, (byte) 0x47, (byte) 0x82, (byte) 0xa3, (byte) 0xf1,
-            (byte) 0xc2, (byte) 0x7e, (byte) 0x3a, (byte) 0xde, (byte) 0x4f, (byte) 0x30,
-            (byte) 0x4c, (byte) 0x7f, (byte) 0x72, (byte) 0x81, (byte) 0x15, (byte) 0x32,
-            (byte) 0xda, (byte) 0x7f, (byte) 0x58, (byte) 0x18, (byte) 0x30, (byte) 0x1f,
-            (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x23, (byte) 0x04,
-            (byte) 0x18, (byte) 0x30, (byte) 0x16, (byte) 0x80, (byte) 0x14, (byte) 0x47,
-            (byte) 0x82, (byte) 0xa3, (byte) 0xf1, (byte) 0xc2, (byte) 0x7e, (byte) 0x3a,
-            (byte) 0xde, (byte) 0x4f, (byte) 0x30, (byte) 0x4c, (byte) 0x7f, (byte) 0x72,
-            (byte) 0x81, (byte) 0x15, (byte) 0x32, (byte) 0xda, (byte) 0x7f, (byte) 0x58,
-            (byte) 0x18, (byte) 0x30, (byte) 0x0c, (byte) 0x06, (byte) 0x03, (byte) 0x55,
-            (byte) 0x1d, (byte) 0x13, (byte) 0x04, (byte) 0x05, (byte) 0x30, (byte) 0x03,
-            (byte) 0x01, (byte) 0x01, (byte) 0xff, (byte) 0x30, (byte) 0x0d, (byte) 0x06,
-            (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7,
-            (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x05, (byte) 0x00,
-            (byte) 0x03, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0x08, (byte) 0x7f,
-            (byte) 0x6a, (byte) 0x48, (byte) 0x90, (byte) 0x7b, (byte) 0x9b, (byte) 0x72,
-            (byte) 0x13, (byte) 0xa7, (byte) 0xef, (byte) 0x6b, (byte) 0x0b, (byte) 0x59,
-            (byte) 0xe5, (byte) 0x49, (byte) 0x72, (byte) 0x3a, (byte) 0xc8, (byte) 0x84,
-            (byte) 0xcc, (byte) 0x23, (byte) 0x18, (byte) 0x4c, (byte) 0xec, (byte) 0xc7,
-            (byte) 0xef, (byte) 0xcb, (byte) 0xa7, (byte) 0xbe, (byte) 0xe4, (byte) 0xef,
-            (byte) 0x8f, (byte) 0xc6, (byte) 0x06, (byte) 0x8c, (byte) 0xc0, (byte) 0xe4,
-            (byte) 0x2f, (byte) 0x2a, (byte) 0xc0, (byte) 0x35, (byte) 0x7d, (byte) 0x5e,
-            (byte) 0x19, (byte) 0x29, (byte) 0x8c, (byte) 0xb9, (byte) 0xf1, (byte) 0x1e,
-            (byte) 0xaf, (byte) 0x82, (byte) 0xd8, (byte) 0xe3, (byte) 0x88, (byte) 0xe1,
-            (byte) 0x31, (byte) 0xc8, (byte) 0x82, (byte) 0x1f, (byte) 0x83, (byte) 0xa9,
-            (byte) 0xde, (byte) 0xfe, (byte) 0x4b, (byte) 0xe2, (byte) 0x78, (byte) 0x64,
-            (byte) 0xed, (byte) 0xa4, (byte) 0x7b, (byte) 0xee, (byte) 0x8d, (byte) 0x71,
-            (byte) 0x1b, (byte) 0x44, (byte) 0xe6, (byte) 0xb7, (byte) 0xe8, (byte) 0xc5,
-            (byte) 0x9a, (byte) 0x93, (byte) 0x92, (byte) 0x6f, (byte) 0x6f, (byte) 0xdb,
-            (byte) 0xbd, (byte) 0xd7, (byte) 0x03, (byte) 0x85, (byte) 0xa9, (byte) 0x5f,
-            (byte) 0x53, (byte) 0x5f, (byte) 0x5d, (byte) 0x30, (byte) 0xc6, (byte) 0xd9,
-            (byte) 0xce, (byte) 0x34, (byte) 0xa8, (byte) 0xbe, (byte) 0x31, (byte) 0x47,
-            (byte) 0x1c, (byte) 0xa4, (byte) 0x7f, (byte) 0xc0, (byte) 0x2c, (byte) 0xbc,
-            (byte) 0xfe, (byte) 0x1a, (byte) 0x31, (byte) 0xd8, (byte) 0x77, (byte) 0x4d,
-            (byte) 0xfc, (byte) 0x45, (byte) 0x84, (byte) 0xfc, (byte) 0x45, (byte) 0x12,
-            (byte) 0xab, (byte) 0x50, (byte) 0xe4, (byte) 0x45, (byte) 0xe5, (byte) 0x11
-        };
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/FeatureUtil.java b/common/device-side/util/src/com/android/compatibility/common/util/FeatureUtil.java
deleted file mode 100644
index c9681d2..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/FeatureUtil.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.compatibility.common.util;
-
-import android.content.Context;
-import android.content.pm.FeatureInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.os.Build;
-import android.support.test.InstrumentationRegistry;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Device-side utility class for detecting system features
- */
-public class FeatureUtil {
-
-    public static final String AUTOMOTIVE_FEATURE = "android.hardware.type.automotive";
-    public static final String LEANBACK_FEATURE = "android.software.leanback";
-    public static final String LOW_RAM_FEATURE = "android.hardware.ram.low";
-    public static final String TELEPHONY_FEATURE = "android.hardware.telephony";
-    public static final String TV_FEATURE = "android.hardware.type.television";
-    public static final String WATCH_FEATURE = "android.hardware.type.watch";
-
-
-    /** Returns true if the device has a given system feature */
-    public static boolean hasSystemFeature(String feature) {
-        return getPackageManager().hasSystemFeature(feature);
-    }
-
-    /** Returns true if the device has any feature in a given collection of system features */
-    public static boolean hasAnySystemFeature(String... features) {
-        PackageManager pm = getPackageManager();
-        for (String feature : features) {
-            if (pm.hasSystemFeature(feature)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /** Returns true if the device has all features in a given collection of system features */
-    public static boolean hasAllSystemFeatures(String... features) {
-        PackageManager pm = getPackageManager();
-        for (String feature : features) {
-            if (!pm.hasSystemFeature(feature)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /** Returns all system features of the device */
-    public static Set<String> getAllFeatures() {
-        Set<String> allFeatures = new HashSet<String>();
-        for (FeatureInfo fi : getPackageManager().getSystemAvailableFeatures()) {
-            allFeatures.add(fi.name);
-        }
-        return allFeatures;
-    }
-
-    /** Returns true if the device has feature TV_FEATURE or feature LEANBACK_FEATURE */
-    public static boolean isTV() {
-        return hasAnySystemFeature(TV_FEATURE, LEANBACK_FEATURE);
-    }
-
-    /** Returns true if the device has feature WATCH_FEATURE */
-    public static boolean isWatch() {
-        return hasSystemFeature(WATCH_FEATURE);
-    }
-
-    /** Returns true if the device has feature AUTOMOTIVE_FEATURE */
-    public static boolean isAutomotive() {
-        return hasSystemFeature(AUTOMOTIVE_FEATURE);
-    }
-
-    public static boolean isVrHeadset() {
-        int maskedUiMode = (getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK);
-        return (maskedUiMode == Configuration.UI_MODE_TYPE_VR_HEADSET);
-    }
-
-    /** Returns true if the device is a low ram device:
-     *  1. API level &gt;= O_MR1
-     *  2. device has feature LOW_RAM_FEATURE
-     */
-    public static boolean isLowRam() {
-        return ApiLevelUtil.isAtLeast(Build.VERSION_CODES.O_MR1) &&
-                hasSystemFeature(LOW_RAM_FEATURE);
-    }
-
-    private static Context getContext() {
-        return InstrumentationRegistry.getInstrumentation().getTargetContext();
-    }
-
-    private static PackageManager getPackageManager() {
-        return getContext().getPackageManager();
-    }
-
-    private static Configuration getConfiguration() {
-        return getContext().getResources().getConfiguration();
-    }
-
-    /** Returns true if the device has feature TELEPHONY_FEATURE */
-    public static boolean hasTelephony() {
-        return hasSystemFeature(TELEPHONY_FEATURE);
-    }
-
-    /** Returns true if the device has feature FEATURE_MICROPHONE */
-    public static boolean hasMicrophone() {
-        return hasSystemFeature(getPackageManager().FEATURE_MICROPHONE);
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/FileCopyHelper.java b/common/device-side/util/src/com/android/compatibility/common/util/FileCopyHelper.java
deleted file mode 100644
index f58dbd0..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/FileCopyHelper.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.compatibility.common.util;
-
-import android.content.Context;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-
-/**
- * FileCopyHelper is used to copy files from resources to the
- * application directory and responsible for deleting the files.
- *
- * @see MediaStore_VideoTest
- * @see MediaStore_Images_MediaTest
- * @see MediaStore_Images_ThumbnailsTest
- */
-public class FileCopyHelper {
-    /** The context. */
-    private Context mContext;
-
-    /** The files added. */
-    private ArrayList<String> mFilesList;
-
-    /**
-     * Instantiates a new file copy helper.
-     *
-     * @param context the context
-     */
-    public FileCopyHelper(Context context) {
-        mContext = context;
-        mFilesList = new ArrayList<String>();
-    }
-
-    /**
-     * Copy the file from the resources with a filename.
-     *
-     * @param resId the res id
-     * @param fileName the file name
-     *
-     * @return the absolute path of the destination file
-     * @throws IOException
-     */
-    public String copy(int resId, String fileName) throws IOException {
-        InputStream source = mContext.getResources().openRawResource(resId);
-        OutputStream target = mContext.openFileOutput(fileName, Context.MODE_WORLD_READABLE);
-        copyFile(source, target);
-        mFilesList.add(fileName);
-        return mContext.getFileStreamPath(fileName).getAbsolutePath();
-    }
-
-    public void copyToExternalStorage(int resId, File path) throws IOException {
-        InputStream source = mContext.getResources().openRawResource(resId);
-        OutputStream target = new FileOutputStream(path);
-        copyFile(source, target);
-    }
-
-    private void copyFile(InputStream source, OutputStream target) throws IOException {
-        try {
-            byte[] buffer = new byte[1024];
-            for (int len = source.read(buffer); len > 0; len = source.read(buffer)) {
-                target.write(buffer, 0, len);
-            }
-        } finally {
-            if (source != null) {
-                source.close();
-            }
-            if (target != null) {
-                target.close();
-            }
-        }
-    }
-
-    /**
-     * Delete all the files copied by the helper.
-     */
-    public void clear(){
-        for (String path : mFilesList) {
-            mContext.deleteFile(path);
-        }
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/FileUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/FileUtils.java
deleted file mode 100644
index ceada01..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/FileUtils.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.compatibility.common.util;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/** Bits and pieces copied from hidden API of android.os.FileUtils. */
-public class FileUtils {
-
-    public static final int S_IFMT  = 0170000;
-    public static final int S_IFSOCK = 0140000;
-    public static final int S_IFLNK = 0120000;
-    public static final int S_IFREG = 0100000;
-    public static final int S_IFBLK = 0060000;
-    public static final int S_IFDIR = 0040000;
-    public static final int S_IFCHR = 0020000;
-    public static final int S_IFIFO = 0010000;
-
-    public static final int S_ISUID = 0004000;
-    public static final int S_ISGID = 0002000;
-    public static final int S_ISVTX = 0001000;
-
-    public static final int S_IRWXU = 00700;
-    public static final int S_IRUSR = 00400;
-    public static final int S_IWUSR = 00200;
-    public static final int S_IXUSR = 00100;
-
-    public static final int S_IRWXG = 00070;
-    public static final int S_IRGRP = 00040;
-    public static final int S_IWGRP = 00020;
-    public static final int S_IXGRP = 00010;
-
-    public static final int S_IRWXO = 00007;
-    public static final int S_IROTH = 00004;
-    public static final int S_IWOTH = 00002;
-    public static final int S_IXOTH = 00001;
-
-    static {
-        System.loadLibrary("cts_jni");
-    }
-
-    public static class FileStatus {
-
-        public int dev;
-        public int ino;
-        public int mode;
-        public int nlink;
-        public int uid;
-        public int gid;
-        public int rdev;
-        public long size;
-        public int blksize;
-        public long blocks;
-        public long atime;
-        public long mtime;
-        public long ctime;
-
-        public boolean hasModeFlag(int flag) {
-            if (((S_IRWXU | S_IRWXG | S_IRWXO) & flag) != flag) {
-                throw new IllegalArgumentException("Inappropriate flag " + flag);
-            }
-            return (mode & flag) == flag;
-        }
-
-        public boolean isOfType(int type) {
-            if ((type & S_IFMT) != type) {
-                throw new IllegalArgumentException("Unknown type " + type);
-            }
-            return (mode & S_IFMT) == type;
-        }
-    }
-
-    /**
-     * @param path of the file to stat
-     * @param status object to set the fields on
-     * @param statLinks or don't stat links (lstat vs stat)
-     * @return whether or not we were able to stat the file
-     */
-    public native static boolean getFileStatus(String path, FileStatus status, boolean statLinks);
-
-    public native static String getUserName(int uid);
-
-    public native static String getGroupName(int gid);
-
-    public native static int setPermissions(String file, int mode);
-
-    /**
-     * Copy data from a source stream to destFile.
-     * Return true if succeed, return false if failed.
-     */
-    public static boolean copyToFile(InputStream inputStream, File destFile) {
-        try {
-            if (destFile.exists()) {
-                destFile.delete();
-            }
-            FileOutputStream out = new FileOutputStream(destFile);
-            try {
-                byte[] buffer = new byte[4096];
-                int bytesRead;
-                while ((bytesRead = inputStream.read(buffer)) >= 0) {
-                    out.write(buffer, 0, bytesRead);
-                }
-            } finally {
-                out.flush();
-                try {
-                    out.getFD().sync();
-                } catch (IOException e) {
-                }
-                out.close();
-            }
-            return true;
-        } catch (IOException e) {
-            return false;
-        }
-    }
-
-    public static void createFile(File file, int numBytes) throws IOException {
-        File parentFile = file.getParentFile();
-        if (parentFile != null) {
-            parentFile.mkdirs();
-        }
-        byte[] buffer = new byte[numBytes];
-        FileOutputStream output = new FileOutputStream(file);
-        try {
-            output.write(buffer);
-        } finally {
-            output.close();
-        }
-    }
-
-    public static byte[] readInputStreamFully(InputStream is) {
-        ByteArrayOutputStream os = new ByteArrayOutputStream();
-        byte[] buffer = new byte[32768];
-        int count;
-        try {
-            while ((count = is.read(buffer)) != -1) {
-                os.write(buffer, 0, count);
-            }
-            is.close();
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-        return os.toByteArray();
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/IBinderParcelable.java b/common/device-side/util/src/com/android/compatibility/common/util/IBinderParcelable.java
deleted file mode 100644
index f3c53fe..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/IBinderParcelable.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.compatibility.common.util;
-
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-public class IBinderParcelable implements Parcelable {
-    public IBinder binder;
-
-    public IBinderParcelable(IBinder source) {
-        binder = source;
-    }
-
-    public int describeContents() {
-        return 0;
-    }
-
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeStrongBinder(binder);
-    }
-
-    public static final Parcelable.Creator<IBinderParcelable>
-        CREATOR = new Parcelable.Creator<IBinderParcelable>() {
-
-        public IBinderParcelable createFromParcel(Parcel source) {
-            return new IBinderParcelable(source);
-        }
-
-        public IBinderParcelable[] newArray(int size) {
-            return new IBinderParcelable[size];
-        }
-    };
-
-    private IBinderParcelable(Parcel source) {
-        binder = source.readStrongBinder();
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ImeAwareEditText.java b/common/device-side/util/src/com/android/compatibility/common/util/ImeAwareEditText.java
deleted file mode 100644
index db148bf..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ImeAwareEditText.java
+++ /dev/null
@@ -1,92 +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.compatibility.common.util;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-
-public class ImeAwareEditText extends EditText {
-    private boolean mHasPendingShowSoftInputRequest;
-    final Runnable mRunShowSoftInputIfNecessary = () -> showSoftInputIfNecessary();
-
-    public ImeAwareEditText(Context context) {
-        super(context, null);
-    }
-
-    public ImeAwareEditText(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public ImeAwareEditText(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    public ImeAwareEditText(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    /**
-     * This method is called back by the system when the system is about to establish a connection
-     * to the current input method.
-     *
-     * <p>This is a good and reliable signal to schedule a pending task to call
-     * {@link InputMethodManager#showSoftInput(View, int)}.</p>
-     *
-     * @param editorInfo context about the text input field.
-     * @return {@link InputConnection} to be passed to the input method.
-     */
-    @Override
-    public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
-        final InputConnection ic = super.onCreateInputConnection(editorInfo);
-        if (mHasPendingShowSoftInputRequest) {
-            removeCallbacks(mRunShowSoftInputIfNecessary);
-            post(mRunShowSoftInputIfNecessary);
-        }
-        return ic;
-    }
-
-    private void showSoftInputIfNecessary() {
-        if (mHasPendingShowSoftInputRequest) {
-            final InputMethodManager imm =
-                    getContext().getSystemService(InputMethodManager.class);
-            imm.showSoftInput(this, 0);
-            mHasPendingShowSoftInputRequest = false;
-        }
-    }
-
-    public void scheduleShowSoftInput() {
-        final InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
-        if (imm.isActive(this)) {
-            // This means that ImeAwareEditText is already connected to the IME.
-            // InputMethodManager#showSoftInput() is guaranteed to pass client-side focus check.
-            mHasPendingShowSoftInputRequest = false;
-            removeCallbacks(mRunShowSoftInputIfNecessary);
-            imm.showSoftInput(this, 0);
-            return;
-        }
-
-        // Otherwise, InputMethodManager#showSoftInput() should be deferred after
-        // onCreateInputConnection().
-        mHasPendingShowSoftInputRequest = true;
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/LocationUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/LocationUtils.java
deleted file mode 100644
index f233851..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/LocationUtils.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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
- */
-
-package com.android.compatibility.common.util;
-
-import android.app.Instrumentation;
-import android.util.Log;
-
-import java.io.IOException;
-
-public class LocationUtils {
-    private static String TAG = "LocationUtils";
-
-    public static void registerMockLocationProvider(Instrumentation instrumentation,
-            boolean enable) {
-        StringBuilder command = new StringBuilder();
-        command.append("appops set ");
-        command.append(instrumentation.getContext().getPackageName());
-        command.append(" android:mock_location ");
-        command.append(enable ? "allow" : "deny");
-        try {
-            SystemUtil.runShellCommand(instrumentation, command.toString());
-        } catch (IOException e) {
-            Log.e(TAG, "Error managing mock location app. Command: " + command, e);
-        }
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/MediaPerfUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/MediaPerfUtils.java
deleted file mode 100644
index 469e99a..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/MediaPerfUtils.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright 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.compatibility.common.util;
-
-import android.media.MediaFormat;
-import android.util.Range;
-
-import com.android.compatibility.common.util.DeviceReportLog;
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
-
-import java.util.Arrays;
-import android.util.Log;
-
-public class MediaPerfUtils {
-    private static final String TAG = "MediaPerfUtils";
-
-    private static final int MOVING_AVERAGE_NUM_FRAMES = 10;
-    private static final int MOVING_AVERAGE_WINDOW_MS = 1000;
-
-    // allow a variance of 2x for measured frame rates (e.g. half of lower-limit to double of
-    // upper-limit of the published values). Also allow an extra 10% margin. This also acts as
-    // a limit for the size of the published rates (e.g. upper-limit / lower-limit <= tolerance).
-    private static final double FRAMERATE_TOLERANCE = 2.0 * 1.1;
-
-    /*
-     *  ------------------ HELPER METHODS FOR ACHIEVABLE FRAME RATES ------------------
-     */
-
-    /** removes brackets from format to be included in JSON. */
-    private static String formatForReport(MediaFormat format) {
-        String asString = "" + format;
-        return asString.substring(1, asString.length() - 1);
-    }
-
-    /**
-     * Adds performance header info to |log| for |codecName|, |round|, |configFormat|, |inputFormat|
-     * and |outputFormat|. Also appends same to |message| and returns the resulting base message
-     * for logging purposes.
-     */
-    public static String addPerformanceHeadersToLog(
-            DeviceReportLog log, String message, int round, String codecName,
-            MediaFormat configFormat, MediaFormat inputFormat, MediaFormat outputFormat) {
-        String mime = configFormat.getString(MediaFormat.KEY_MIME);
-        int width = configFormat.getInteger(MediaFormat.KEY_WIDTH);
-        int height = configFormat.getInteger(MediaFormat.KEY_HEIGHT);
-
-        log.addValue("round", round, ResultType.NEUTRAL, ResultUnit.NONE);
-        log.addValue("codec_name", codecName, ResultType.NEUTRAL, ResultUnit.NONE);
-        log.addValue("mime_type", mime, ResultType.NEUTRAL, ResultUnit.NONE);
-        log.addValue("width", width, ResultType.NEUTRAL, ResultUnit.NONE);
-        log.addValue("height", height, ResultType.NEUTRAL, ResultUnit.NONE);
-        log.addValue("config_format", formatForReport(configFormat),
-                ResultType.NEUTRAL, ResultUnit.NONE);
-        log.addValue("input_format", formatForReport(inputFormat),
-                ResultType.NEUTRAL, ResultUnit.NONE);
-        log.addValue("output_format", formatForReport(outputFormat),
-                ResultType.NEUTRAL, ResultUnit.NONE);
-
-        message += " codec=" + codecName + " round=" + round + " configFormat=" + configFormat
-                + " inputFormat=" + inputFormat + " outputFormat=" + outputFormat;
-
-        Range<Double> reported =
-            MediaUtils.getVideoCapabilities(codecName, mime)
-                    .getAchievableFrameRatesFor(width, height);
-        if (reported != null) {
-            log.addValue("reported_low", reported.getLower(), ResultType.NEUTRAL, ResultUnit.FPS);
-            log.addValue("reported_high", reported.getUpper(), ResultType.NEUTRAL, ResultUnit.FPS);
-            message += " reported=" + reported.getLower() + "-" + reported.getUpper();
-        }
-
-        return message;
-    }
-
-    /**
-     * Adds performance statistics based on the raw |stats| to |log|. Also prints the same into
-     * logcat. Returns the "final fps" value.
-     */
-    public static double addPerformanceStatsToLog(
-            DeviceReportLog log, MediaUtils.Stats durationsUsStats, String message) {
-
-        MediaUtils.Stats frameAvgUsStats =
-            durationsUsStats.movingAverage(MOVING_AVERAGE_NUM_FRAMES);
-        log.addValue(
-                "window_frames", MOVING_AVERAGE_NUM_FRAMES, ResultType.NEUTRAL, ResultUnit.COUNT);
-        logPerformanceStats(log, frameAvgUsStats, "frame_avg_stats",
-                message + " window=" + MOVING_AVERAGE_NUM_FRAMES);
-
-        MediaUtils.Stats timeAvgUsStats =
-            durationsUsStats.movingAverageOverSum(MOVING_AVERAGE_WINDOW_MS * 1000);
-        log.addValue("window_time", MOVING_AVERAGE_WINDOW_MS, ResultType.NEUTRAL, ResultUnit.MS);
-        double fps = logPerformanceStats(log, timeAvgUsStats, "time_avg_stats",
-                message + " windowMs=" + MOVING_AVERAGE_WINDOW_MS);
-
-        log.setSummary("fps", fps, ResultType.HIGHER_BETTER, ResultUnit.FPS);
-        return fps;
-    }
-
-    /**
-     * Adds performance statistics based on the processed |stats| to |log| using |prefix|.
-     * Also prints the same into logcat using |message| as the base message. Returns the fps value
-     * for |stats|. |prefix| must be lowercase alphanumeric underscored format.
-     */
-    private static double logPerformanceStats(
-            DeviceReportLog log, MediaUtils.Stats statsUs, String prefix, String message) {
-        final String[] labels = {
-            "min", "p5", "p10", "p20", "p30", "p40", "p50", "p60", "p70", "p80", "p90", "p95", "max"
-        };
-        final double[] points = {
-             0,     5,    10,    20,    30,    40,    50,    60,    70,    80,    90,    95,    100
-        };
-
-        int num = statsUs.getNum();
-        long avg = Math.round(statsUs.getAverage());
-        long stdev = Math.round(statsUs.getStdev());
-        log.addValue(prefix + "_num", num, ResultType.NEUTRAL, ResultUnit.COUNT);
-        log.addValue(prefix + "_avg", avg / 1000., ResultType.LOWER_BETTER, ResultUnit.MS);
-        log.addValue(prefix + "_stdev", stdev / 1000., ResultType.LOWER_BETTER, ResultUnit.MS);
-        message += " num=" + num + " avg=" + avg + " stdev=" + stdev;
-        final double[] percentiles = statsUs.getPercentiles(points);
-        for (int i = 0; i < labels.length; ++i) {
-            long p = Math.round(percentiles[i]);
-            message += " " + labels[i] + "=" + p;
-            log.addValue(prefix + "_" + labels[i], p / 1000., ResultType.NEUTRAL, ResultUnit.MS);
-        }
-
-        // print result to logcat in case test aborts before logs are written
-        Log.i(TAG, message);
-
-        return 1e6 / percentiles[points.length - 2];
-    }
-
-    /** Verifies |measuredFps| against reported achievable rates. Returns null if at least
-     *  one measurement falls within the margins of the reported range. Otherwise, returns
-     *  an error message to display.*/
-    public static String verifyAchievableFrameRates(
-            String name, String mime, int w, int h, double... measuredFps) {
-        Range<Double> reported =
-            MediaUtils.getVideoCapabilities(name, mime).getAchievableFrameRatesFor(w, h);
-        String kind = "achievable frame rates for " + name + " " + mime + " " + w + "x" + h;
-        if (reported == null) {
-            return "Failed to get " + kind;
-        }
-        double lowerBoundary1 = reported.getLower() / FRAMERATE_TOLERANCE;
-        double upperBoundary1 = reported.getUpper() * FRAMERATE_TOLERANCE;
-        double lowerBoundary2 = reported.getUpper() / Math.pow(FRAMERATE_TOLERANCE, 2);
-        double upperBoundary2 = reported.getLower() * Math.pow(FRAMERATE_TOLERANCE, 2);
-        Log.d(TAG, name + " " + mime + " " + w + "x" + h +
-                " lowerBoundary1 " + lowerBoundary1 + " upperBoundary1 " + upperBoundary1 +
-                " lowerBoundary2 " + lowerBoundary2 + " upperBoundary2 " + upperBoundary2 +
-                " measured " + Arrays.toString(measuredFps));
-
-        for (double measured : measuredFps) {
-            if (measured >= lowerBoundary1 && measured <= upperBoundary1
-                    && measured >= lowerBoundary2 && measured <= upperBoundary2) {
-                return null;
-            }
-        }
-
-        return "Expected " + kind + ": " + reported + ".\n"
-                + "Measured frame rate: " + Arrays.toString(measuredFps) + ".\n";
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/MediaUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/MediaUtils.java
deleted file mode 100644
index c81f648..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/MediaUtils.java
+++ /dev/null
@@ -1,1243 +0,0 @@
-/*
- * Copyright 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.drm.DrmConvertedStatus;
-import android.drm.DrmManagerClient;
-import android.graphics.ImageFormat;
-import android.graphics.Rect;
-import android.media.Image;
-import android.media.Image.Plane;
-import android.media.MediaCodec;
-import android.media.MediaCodec.BufferInfo;
-import android.media.MediaCodecInfo;
-import android.media.MediaCodecInfo.CodecCapabilities;
-import android.media.MediaCodecInfo.VideoCapabilities;
-import android.media.MediaCodecList;
-import android.media.MediaExtractor;
-import android.media.MediaFormat;
-import android.net.Uri;
-import android.util.Log;
-import android.util.Range;
-
-import com.android.compatibility.common.util.DeviceReportLog;
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
-
-import java.lang.reflect.Method;
-import java.nio.ByteBuffer;
-import java.security.MessageDigest;
-
-import static java.lang.reflect.Modifier.isPublic;
-import static java.lang.reflect.Modifier.isStatic;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-
-import static junit.framework.Assert.assertTrue;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.RandomAccessFile;
-
-public class MediaUtils {
-    private static final String TAG = "MediaUtils";
-
-    /*
-     *  ----------------------- HELPER METHODS FOR SKIPPING TESTS -----------------------
-     */
-    private static final int ALL_AV_TRACKS = -1;
-
-    private static final MediaCodecList sMCL = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
-
-    /**
-     * Returns the test name (heuristically).
-     *
-     * Since it uses heuristics, this method has only been verified for media
-     * tests. This centralizes the way to signal errors during a test.
-     */
-    public static String getTestName() {
-        return getTestName(false /* withClass */);
-    }
-
-    /**
-     * Returns the test name with the full class (heuristically).
-     *
-     * Since it uses heuristics, this method has only been verified for media
-     * tests. This centralizes the way to signal errors during a test.
-     */
-    public static String getTestNameWithClass() {
-        return getTestName(true /* withClass */);
-    }
-
-    private static String getTestName(boolean withClass) {
-        int bestScore = -1;
-        String testName = "test???";
-        Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces();
-        for (Map.Entry<Thread, StackTraceElement[]> entry : traces.entrySet()) {
-            StackTraceElement[] stack = entry.getValue();
-            for (int index = 0; index < stack.length; ++index) {
-                // method name must start with "test"
-                String methodName = stack[index].getMethodName();
-                if (!methodName.startsWith("test")) {
-                    continue;
-                }
-
-                int score = 0;
-                // see if there is a public non-static void method that takes no argument
-                Class<?> clazz;
-                try {
-                    clazz = Class.forName(stack[index].getClassName());
-                    ++score;
-                    for (final Method method : clazz.getDeclaredMethods()) {
-                        if (method.getName().equals(methodName)
-                                && isPublic(method.getModifiers())
-                                && !isStatic(method.getModifiers())
-                                && method.getParameterTypes().length == 0
-                                && method.getReturnType().equals(Void.TYPE)) {
-                            ++score;
-                            break;
-                        }
-                    }
-                    if (score == 1) {
-                        // if we could read the class, but method is not public void, it is
-                        // not a candidate
-                        continue;
-                    }
-                } catch (ClassNotFoundException e) {
-                }
-
-                // even if we cannot verify the method signature, there are signals in the stack
-
-                // usually test method is invoked by reflection
-                int depth = 1;
-                while (index + depth < stack.length
-                        && stack[index + depth].getMethodName().equals("invoke")
-                        && stack[index + depth].getClassName().equals(
-                                "java.lang.reflect.Method")) {
-                    ++depth;
-                }
-                if (depth > 1) {
-                    ++score;
-                    // and usually test method is run by runMethod method in android.test package
-                    if (index + depth < stack.length) {
-                        if (stack[index + depth].getClassName().startsWith("android.test.")) {
-                            ++score;
-                        }
-                        if (stack[index + depth].getMethodName().equals("runMethod")) {
-                            ++score;
-                        }
-                    }
-                }
-
-                if (score > bestScore) {
-                    bestScore = score;
-                    testName = methodName;
-                    if (withClass) {
-                        testName = stack[index].getClassName() + "." + testName;
-                    }
-                }
-            }
-        }
-        return testName;
-    }
-
-    /**
-     * Finds test name (heuristically) and prints out standard skip message.
-     *
-     * Since it uses heuristics, this method has only been verified for media
-     * tests. This centralizes the way to signal a skipped test.
-     */
-    public static void skipTest(String tag, String reason) {
-        Log.i(tag, "SKIPPING " + getTestName() + "(): " + reason);
-        DeviceReportLog log = new DeviceReportLog("CtsMediaSkippedTests", "test_skipped");
-        try {
-            log.addValue("reason", reason, ResultType.NEUTRAL, ResultUnit.NONE);
-            log.addValue(
-                    "test", getTestNameWithClass(), ResultType.NEUTRAL, ResultUnit.NONE);
-            log.submit();
-        } catch (NullPointerException e) { }
-    }
-
-    /**
-     * Finds test name (heuristically) and prints out standard skip message.
-     *
-     * Since it uses heuristics, this method has only been verified for media
-     * tests.  This centralizes the way to signal a skipped test.
-     */
-    public static void skipTest(String reason) {
-        skipTest(TAG, reason);
-    }
-
-    public static boolean check(boolean result, String message) {
-        if (!result) {
-            skipTest(message);
-        }
-        return result;
-    }
-
-    /*
-     *  ------------------- HELPER METHODS FOR CHECKING CODEC SUPPORT -------------------
-     */
-
-    public static boolean isGoogle(String codecName) {
-        codecName = codecName.toLowerCase();
-        return codecName.startsWith("omx.google.")
-                || codecName.startsWith("c2.android.")
-                || codecName.startsWith("c2.google.");
-    }
-
-    // returns the list of codecs that support any one of the formats
-    private static String[] getCodecNames(
-            boolean isEncoder, Boolean isGoog, MediaFormat... formats) {
-        MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
-        ArrayList<String> result = new ArrayList<>();
-        for (MediaCodecInfo info : mcl.getCodecInfos()) {
-            if (info.isEncoder() != isEncoder) {
-                continue;
-            }
-            if (isGoog != null && isGoogle(info.getName()) != isGoog) {
-                continue;
-            }
-
-            for (MediaFormat format : formats) {
-                String mime = format.getString(MediaFormat.KEY_MIME);
-
-                CodecCapabilities caps = null;
-                try {
-                    caps = info.getCapabilitiesForType(mime);
-                } catch (IllegalArgumentException e) {  // mime is not supported
-                    continue;
-                }
-                if (caps.isFormatSupported(format)) {
-                    result.add(info.getName());
-                    break;
-                }
-            }
-        }
-        return result.toArray(new String[result.size()]);
-    }
-
-    /* Use isGoog = null to query all decoders */
-    public static String[] getDecoderNames(/* Nullable */ Boolean isGoog, MediaFormat... formats) {
-        return getCodecNames(false /* isEncoder */, isGoog, formats);
-    }
-
-    public static String[] getDecoderNames(MediaFormat... formats) {
-        return getCodecNames(false /* isEncoder */, null /* isGoog */, formats);
-    }
-
-    /* Use isGoog = null to query all decoders */
-    public static String[] getEncoderNames(/* Nullable */ Boolean isGoog, MediaFormat... formats) {
-        return getCodecNames(true /* isEncoder */, isGoog, formats);
-    }
-
-    public static String[] getEncoderNames(MediaFormat... formats) {
-        return getCodecNames(true /* isEncoder */, null /* isGoog */, formats);
-    }
-
-    public static String[] getDecoderNamesForMime(String mime) {
-        MediaFormat format = new MediaFormat();
-        format.setString(MediaFormat.KEY_MIME, mime);
-        return getCodecNames(false /* isEncoder */, null /* isGoog */, format);
-    }
-
-    public static String[] getEncoderNamesForMime(String mime) {
-        MediaFormat format = new MediaFormat();
-        format.setString(MediaFormat.KEY_MIME, mime);
-        return getCodecNames(true /* isEncoder */, null /* isGoog */, format);
-    }
-
-    public static void verifyNumCodecs(
-            int count, boolean isEncoder, Boolean isGoog, MediaFormat... formats) {
-        String desc = (isEncoder ? "encoders" : "decoders") + " for "
-                + (formats.length == 1 ? formats[0].toString() : Arrays.toString(formats));
-        if (isGoog != null) {
-            desc = (isGoog ? "Google " : "non-Google ") + desc;
-        }
-
-        String[] codecs = getCodecNames(isEncoder, isGoog, formats);
-        assertTrue("test can only verify " + count + " " + desc + "; found " + codecs.length + ": "
-                + Arrays.toString(codecs), codecs.length <= count);
-    }
-
-    public static MediaCodec getDecoder(MediaFormat format) {
-        String decoder = sMCL.findDecoderForFormat(format);
-        if (decoder != null) {
-            try {
-                return MediaCodec.createByCodecName(decoder);
-            } catch (IOException e) {
-            }
-        }
-        return null;
-    }
-
-    public static boolean canEncode(MediaFormat format) {
-        if (sMCL.findEncoderForFormat(format) == null) {
-            Log.i(TAG, "no encoder for " + format);
-            return false;
-        }
-        return true;
-    }
-
-    public static boolean canDecode(MediaFormat format) {
-        if (sMCL.findDecoderForFormat(format) == null) {
-            Log.i(TAG, "no decoder for " + format);
-            return false;
-        }
-        return true;
-    }
-
-    public static boolean supports(String codecName, String mime, int w, int h) {
-        // While this could be simply written as such, give more graceful feedback.
-        // MediaFormat format = MediaFormat.createVideoFormat(mime, w, h);
-        // return supports(codecName, format);
-
-        VideoCapabilities vidCap = getVideoCapabilities(codecName, mime);
-        if (vidCap == null) {
-            return false;
-        } else if (vidCap.isSizeSupported(w, h)) {
-            return true;
-        }
-
-        Log.w(TAG, "unsupported size " + w + "x" + h);
-        return false;
-    }
-
-    public static boolean supports(String codecName, MediaFormat format) {
-        MediaCodec codec;
-        try {
-            codec = MediaCodec.createByCodecName(codecName);
-        } catch (IOException e) {
-            Log.w(TAG, "codec not found: " + codecName);
-            return false;
-        }
-
-        String mime = format.getString(MediaFormat.KEY_MIME);
-        CodecCapabilities cap = null;
-        try {
-            cap = codec.getCodecInfo().getCapabilitiesForType(mime);
-            return cap.isFormatSupported(format);
-        } catch (IllegalArgumentException e) {
-            Log.w(TAG, "not supported mime: " + mime);
-            return false;
-        } finally {
-            codec.release();
-        }
-    }
-
-    public static boolean hasCodecForTrack(MediaExtractor ex, int track) {
-        int count = ex.getTrackCount();
-        if (track < 0 || track >= count) {
-            throw new IndexOutOfBoundsException(track + " not in [0.." + (count - 1) + "]");
-        }
-        return canDecode(ex.getTrackFormat(track));
-    }
-
-    /**
-     * return true iff all audio and video tracks are supported
-     */
-    public static boolean hasCodecsForMedia(MediaExtractor ex) {
-        for (int i = 0; i < ex.getTrackCount(); ++i) {
-            MediaFormat format = ex.getTrackFormat(i);
-            // only check for audio and video codecs
-            String mime = format.getString(MediaFormat.KEY_MIME).toLowerCase();
-            if (!mime.startsWith("audio/") && !mime.startsWith("video/")) {
-                continue;
-            }
-            if (!canDecode(format)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * return true iff any track starting with mimePrefix is supported
-     */
-    public static boolean hasCodecForMediaAndDomain(MediaExtractor ex, String mimePrefix) {
-        mimePrefix = mimePrefix.toLowerCase();
-        for (int i = 0; i < ex.getTrackCount(); ++i) {
-            MediaFormat format = ex.getTrackFormat(i);
-            String mime = format.getString(MediaFormat.KEY_MIME);
-            if (mime.toLowerCase().startsWith(mimePrefix)) {
-                if (canDecode(format)) {
-                    return true;
-                }
-                Log.i(TAG, "no decoder for " + format);
-            }
-        }
-        return false;
-    }
-
-    private static boolean hasCodecsForResourceCombo(
-            Context context, int resourceId, int track, String mimePrefix) {
-        try {
-            AssetFileDescriptor afd = null;
-            MediaExtractor ex = null;
-            try {
-                afd = context.getResources().openRawResourceFd(resourceId);
-                ex = new MediaExtractor();
-                ex.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
-                if (mimePrefix != null) {
-                    return hasCodecForMediaAndDomain(ex, mimePrefix);
-                } else if (track == ALL_AV_TRACKS) {
-                    return hasCodecsForMedia(ex);
-                } else {
-                    return hasCodecForTrack(ex, track);
-                }
-            } finally {
-                if (ex != null) {
-                    ex.release();
-                }
-                if (afd != null) {
-                    afd.close();
-                }
-            }
-        } catch (IOException e) {
-            Log.i(TAG, "could not open resource");
-        }
-        return false;
-    }
-
-    /**
-     * return true iff all audio and video tracks are supported
-     */
-    public static boolean hasCodecsForResource(Context context, int resourceId) {
-        return hasCodecsForResourceCombo(context, resourceId, ALL_AV_TRACKS, null /* mimePrefix */);
-    }
-
-    public static boolean checkCodecsForResource(Context context, int resourceId) {
-        return check(hasCodecsForResource(context, resourceId), "no decoder found");
-    }
-
-    /**
-     * return true iff track is supported.
-     */
-    public static boolean hasCodecForResource(Context context, int resourceId, int track) {
-        return hasCodecsForResourceCombo(context, resourceId, track, null /* mimePrefix */);
-    }
-
-    public static boolean checkCodecForResource(Context context, int resourceId, int track) {
-        return check(hasCodecForResource(context, resourceId, track), "no decoder found");
-    }
-
-    /**
-     * return true iff any track starting with mimePrefix is supported
-     */
-    public static boolean hasCodecForResourceAndDomain(
-            Context context, int resourceId, String mimePrefix) {
-        return hasCodecsForResourceCombo(context, resourceId, ALL_AV_TRACKS, mimePrefix);
-    }
-
-    /**
-     * return true iff all audio and video tracks are supported
-     */
-    public static boolean hasCodecsForPath(Context context, String path) {
-        MediaExtractor ex = null;
-        try {
-            ex = getExtractorForPath(context, path);
-            return hasCodecsForMedia(ex);
-        } catch (IOException e) {
-            Log.i(TAG, "could not open path " + path);
-        } finally {
-            if (ex != null) {
-                ex.release();
-            }
-        }
-        return true;
-    }
-
-    private static MediaExtractor getExtractorForPath(Context context, String path)
-            throws IOException {
-        Uri uri = Uri.parse(path);
-        String scheme = uri.getScheme();
-        MediaExtractor ex = new MediaExtractor();
-        try {
-            if (scheme == null) { // file
-                ex.setDataSource(path);
-            } else if (scheme.equalsIgnoreCase("file")) {
-                ex.setDataSource(uri.getPath());
-            } else {
-                ex.setDataSource(context, uri, null);
-            }
-        } catch (IOException e) {
-            ex.release();
-            throw e;
-        }
-        return ex;
-    }
-
-    public static boolean checkCodecsForPath(Context context, String path) {
-        return check(hasCodecsForPath(context, path), "no decoder found");
-    }
-
-    public static boolean hasCodecForDomain(boolean encoder, String domain) {
-        for (MediaCodecInfo info : sMCL.getCodecInfos()) {
-            if (encoder != info.isEncoder()) {
-                continue;
-            }
-
-            for (String type : info.getSupportedTypes()) {
-                if (type.toLowerCase().startsWith(domain.toLowerCase() + "/")) {
-                    Log.i(TAG, "found codec " + info.getName() + " for mime " + type);
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    public static boolean checkCodecForDomain(boolean encoder, String domain) {
-        return check(hasCodecForDomain(encoder, domain),
-                "no " + domain + (encoder ? " encoder" : " decoder") + " found");
-    }
-
-    private static boolean hasCodecForMime(boolean encoder, String mime) {
-        for (MediaCodecInfo info : sMCL.getCodecInfos()) {
-            if (encoder != info.isEncoder()) {
-                continue;
-            }
-
-            for (String type : info.getSupportedTypes()) {
-                if (type.equalsIgnoreCase(mime)) {
-                    Log.i(TAG, "found codec " + info.getName() + " for mime " + mime);
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    private static boolean hasCodecForMimes(boolean encoder, String[] mimes) {
-        for (String mime : mimes) {
-            if (!hasCodecForMime(encoder, mime)) {
-                Log.i(TAG, "no " + (encoder ? "encoder" : "decoder") + " for mime " + mime);
-                return false;
-            }
-        }
-        return true;
-    }
-
-
-    public static boolean hasEncoder(String... mimes) {
-        return hasCodecForMimes(true /* encoder */, mimes);
-    }
-
-    public static boolean hasDecoder(String... mimes) {
-        return hasCodecForMimes(false /* encoder */, mimes);
-    }
-
-    public static boolean checkDecoder(String... mimes) {
-        return check(hasCodecForMimes(false /* encoder */, mimes), "no decoder found");
-    }
-
-    public static boolean checkEncoder(String... mimes) {
-        return check(hasCodecForMimes(true /* encoder */, mimes), "no encoder found");
-    }
-
-    public static boolean canDecodeVideo(String mime, int width, int height, float rate) {
-        MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
-        format.setFloat(MediaFormat.KEY_FRAME_RATE, rate);
-        return canDecode(format);
-    }
-
-    public static boolean canDecodeVideo(
-            String mime, int width, int height, float rate,
-            Integer profile, Integer level, Integer bitrate) {
-        MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
-        format.setFloat(MediaFormat.KEY_FRAME_RATE, rate);
-        if (profile != null) {
-            format.setInteger(MediaFormat.KEY_PROFILE, profile);
-            if (level != null) {
-                format.setInteger(MediaFormat.KEY_LEVEL, level);
-            }
-        }
-        if (bitrate != null) {
-            format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
-        }
-        return canDecode(format);
-    }
-
-    public static boolean checkEncoderForFormat(MediaFormat format) {
-        return check(canEncode(format), "no encoder for " + format);
-    }
-
-    public static boolean checkDecoderForFormat(MediaFormat format) {
-        return check(canDecode(format), "no decoder for " + format);
-    }
-
-    /*
-     *  ----------------------- HELPER METHODS FOR MEDIA HANDLING -----------------------
-     */
-
-    public static VideoCapabilities getVideoCapabilities(String codecName, String mime) {
-        for (MediaCodecInfo info : sMCL.getCodecInfos()) {
-            if (!info.getName().equalsIgnoreCase(codecName)) {
-                continue;
-            }
-            CodecCapabilities caps;
-            try {
-                caps = info.getCapabilitiesForType(mime);
-            } catch (IllegalArgumentException e) {
-                // mime is not supported
-                Log.w(TAG, "not supported mime: " + mime);
-                return null;
-            }
-            VideoCapabilities vidCaps = caps.getVideoCapabilities();
-            if (vidCaps == null) {
-                Log.w(TAG, "not a video codec: " + codecName);
-            }
-            return vidCaps;
-        }
-        Log.w(TAG, "codec not found: " + codecName);
-        return null;
-    }
-
-    public static MediaFormat getTrackFormatForResource(
-            Context context,
-            int resourceId,
-            String mimeTypePrefix) throws IOException {
-        MediaExtractor extractor = new MediaExtractor();
-        AssetFileDescriptor afd = context.getResources().openRawResourceFd(resourceId);
-        try {
-            extractor.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
-        } finally {
-            afd.close();
-        }
-        return getTrackFormatForExtractor(extractor, mimeTypePrefix);
-    }
-
-    public static MediaFormat getTrackFormatForPath(
-            Context context, String path, String mimeTypePrefix)
-            throws IOException {
-      MediaExtractor extractor = getExtractorForPath(context, path);
-      return getTrackFormatForExtractor(extractor, mimeTypePrefix);
-    }
-
-    private static MediaFormat getTrackFormatForExtractor(
-            MediaExtractor extractor,
-            String mimeTypePrefix) {
-      int trackIndex;
-      MediaFormat format = null;
-      for (trackIndex = 0; trackIndex < extractor.getTrackCount(); trackIndex++) {
-          MediaFormat trackMediaFormat = extractor.getTrackFormat(trackIndex);
-          if (trackMediaFormat.getString(MediaFormat.KEY_MIME).startsWith(mimeTypePrefix)) {
-              format = trackMediaFormat;
-              break;
-          }
-      }
-      extractor.release();
-      if (format == null) {
-          throw new RuntimeException("couldn't get a track for " + mimeTypePrefix);
-      }
-
-      return format;
-    }
-
-    public static MediaExtractor createMediaExtractorForMimeType(
-            Context context, int resourceId, String mimeTypePrefix)
-            throws IOException {
-        MediaExtractor extractor = new MediaExtractor();
-        AssetFileDescriptor afd = context.getResources().openRawResourceFd(resourceId);
-        try {
-            extractor.setDataSource(
-                    afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
-        } finally {
-            afd.close();
-        }
-        int trackIndex;
-        for (trackIndex = 0; trackIndex < extractor.getTrackCount(); trackIndex++) {
-            MediaFormat trackMediaFormat = extractor.getTrackFormat(trackIndex);
-            if (trackMediaFormat.getString(MediaFormat.KEY_MIME).startsWith(mimeTypePrefix)) {
-                extractor.selectTrack(trackIndex);
-                break;
-            }
-        }
-        if (trackIndex == extractor.getTrackCount()) {
-            extractor.release();
-            throw new IllegalStateException("couldn't get a track for " + mimeTypePrefix);
-        }
-
-        return extractor;
-    }
-
-    /*
-     *  ---------------------- HELPER METHODS FOR CODEC CONFIGURATION
-     */
-
-    /** Format must contain mime, width and height.
-     *  Throws Exception if encoder does not support this width and height */
-    public static void setMaxEncoderFrameAndBitrates(
-            MediaCodec encoder, MediaFormat format, int maxFps) {
-        String mime = format.getString(MediaFormat.KEY_MIME);
-
-        VideoCapabilities vidCaps =
-            encoder.getCodecInfo().getCapabilitiesForType(mime).getVideoCapabilities();
-        setMaxEncoderFrameAndBitrates(vidCaps, format, maxFps);
-    }
-
-    public static void setMaxEncoderFrameAndBitrates(
-            VideoCapabilities vidCaps, MediaFormat format, int maxFps) {
-        int width = format.getInteger(MediaFormat.KEY_WIDTH);
-        int height = format.getInteger(MediaFormat.KEY_HEIGHT);
-
-        int maxWidth = vidCaps.getSupportedWidths().getUpper();
-        int maxHeight = vidCaps.getSupportedHeightsFor(maxWidth).getUpper();
-        int frameRate = Math.min(
-                maxFps, vidCaps.getSupportedFrameRatesFor(width, height).getUpper().intValue());
-        format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
-
-        int bitrate = vidCaps.getBitrateRange().clamp(
-            (int)(vidCaps.getBitrateRange().getUpper() /
-                  Math.sqrt((double)maxWidth * maxHeight / width / height)));
-        format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
-    }
-
-    /*
-     *  ------------------ HELPER METHODS FOR STATISTICS AND REPORTING ------------------
-     */
-
-    // TODO: migrate this into com.android.compatibility.common.util.Stat
-    public static class Stats {
-        /** does not support NaN or Inf in |data| */
-        public Stats(double[] data) {
-            mData = data;
-            if (mData != null) {
-                mNum = mData.length;
-            }
-        }
-
-        public int getNum() {
-            return mNum;
-        }
-
-        /** calculate mSumX and mSumXX */
-        private void analyze() {
-            if (mAnalyzed) {
-                return;
-            }
-
-            if (mData != null) {
-                for (double x : mData) {
-                    if (!(x >= mMinX)) { // mMinX may be NaN
-                        mMinX = x;
-                    }
-                    if (!(x <= mMaxX)) { // mMaxX may be NaN
-                        mMaxX = x;
-                    }
-                    mSumX += x;
-                    mSumXX += x * x;
-                }
-            }
-            mAnalyzed = true;
-        }
-
-        /** returns the maximum or NaN if it does not exist */
-        public double getMin() {
-            analyze();
-            return mMinX;
-        }
-
-        /** returns the minimum or NaN if it does not exist */
-        public double getMax() {
-            analyze();
-            return mMaxX;
-        }
-
-        /** returns the average or NaN if it does not exist. */
-        public double getAverage() {
-            analyze();
-            if (mNum == 0) {
-                return Double.NaN;
-            } else {
-                return mSumX / mNum;
-            }
-        }
-
-        /** returns the standard deviation or NaN if it does not exist. */
-        public double getStdev() {
-            analyze();
-            if (mNum == 0) {
-                return Double.NaN;
-            } else {
-                double average = mSumX / mNum;
-                return Math.sqrt(mSumXX / mNum - average * average);
-            }
-        }
-
-        /** returns the statistics for the moving average over n values */
-        public Stats movingAverage(int n) {
-            if (n < 1 || mNum < n) {
-                return new Stats(null);
-            } else if (n == 1) {
-                return this;
-            }
-
-            double[] avgs = new double[mNum - n + 1];
-            double sum = 0;
-            for (int i = 0; i < mNum; ++i) {
-                sum += mData[i];
-                if (i >= n - 1) {
-                    avgs[i - n + 1] = sum / n;
-                    sum -= mData[i - n + 1];
-                }
-            }
-            return new Stats(avgs);
-        }
-
-        /** returns the statistics for the moving average over a window over the
-         *  cumulative sum. Basically, moves a window from: [0, window] to
-         *  [sum - window, sum] over the cumulative sum, over ((sum - window) / average)
-         *  steps, and returns the average value over each window.
-         *  This method is used to average time-diff data over a window of a constant time.
-         */
-        public Stats movingAverageOverSum(double window) {
-            if (window <= 0 || mNum < 1) {
-                return new Stats(null);
-            }
-
-            analyze();
-            double average = mSumX / mNum;
-            if (window >= mSumX) {
-                return new Stats(new double[] { average });
-            }
-            int samples = (int)Math.ceil((mSumX - window) / average);
-            double[] avgs = new double[samples];
-
-            // A somewhat brute force approach to calculating the moving average.
-            // TODO: add support for weights in Stats, so we can do a more refined approach.
-            double sum = 0; // sum of elements in the window
-            int num = 0; // number of elements in the moving window
-            int bi = 0; // index of the first element in the moving window
-            int ei = 0; // index of the last element in the moving window
-            double space = window; // space at the end of the window
-            double foot = 0; // space at the beginning of the window
-
-            // invariants: foot + sum + space == window
-            //             bi + num == ei
-            //
-            //  window:             |-------------------------------|
-            //                      |    <-----sum------>           |
-            //                      <foot>               <---space-->
-            //                           |               |
-            //  intervals:   |-----------|-------|-------|--------------------|--------|
-            //                           ^bi             ^ei
-
-            int ix = 0; // index in the result
-            while (ix < samples) {
-                // add intervals while there is space in the window
-                while (ei < mData.length && mData[ei] <= space) {
-                    space -= mData[ei];
-                    sum += mData[ei];
-                    num++;
-                    ei++;
-                }
-
-                // calculate average over window and deal with odds and ends (e.g. if there are no
-                // intervals in the current window: pick whichever element overlaps the window
-                // most.
-                if (num > 0) {
-                    avgs[ix++] = sum / num;
-                } else if (bi > 0 && foot > space) {
-                    // consider previous
-                    avgs[ix++] = mData[bi - 1];
-                } else if (ei == mData.length) {
-                    break;
-                } else {
-                    avgs[ix++] = mData[ei];
-                }
-
-                // move the window to the next position
-                foot -= average;
-                space += average;
-
-                // remove intervals that are now partially or wholly outside of the window
-                while (bi < ei && foot < 0) {
-                    foot += mData[bi];
-                    sum -= mData[bi];
-                    num--;
-                    bi++;
-                }
-            }
-            return new Stats(Arrays.copyOf(avgs, ix));
-        }
-
-        /** calculate mSortedData */
-        private void sort() {
-            if (mSorted || mNum == 0) {
-                return;
-            }
-            mSortedData = Arrays.copyOf(mData, mNum);
-            Arrays.sort(mSortedData);
-            mSorted = true;
-        }
-
-        /** returns an array of percentiles for the points using nearest rank */
-        public double[] getPercentiles(double... points) {
-            sort();
-            double[] res = new double[points.length];
-            for (int i = 0; i < points.length; ++i) {
-                if (mNum < 1 || points[i] < 0 || points[i] > 100) {
-                    res[i] = Double.NaN;
-                } else {
-                    res[i] = mSortedData[(int)Math.round(points[i] / 100 * (mNum - 1))];
-                }
-            }
-            return res;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (o instanceof Stats) {
-                Stats other = (Stats)o;
-                if (other.mNum != mNum) {
-                    return false;
-                } else if (mNum == 0) {
-                    return true;
-                }
-                return Arrays.equals(mData, other.mData);
-            }
-            return false;
-        }
-
-        private double[] mData;
-        private double mSumX = 0;
-        private double mSumXX = 0;
-        private double mMinX = Double.NaN;
-        private double mMaxX = Double.NaN;
-        private int mNum = 0;
-        private boolean mAnalyzed = false;
-        private double[] mSortedData;
-        private boolean mSorted = false;
-    }
-
-    /**
-     * Convert a forward lock .dm message stream to a .fl file
-     * @param context Context to use
-     * @param dmStream The .dm message
-     * @param flFile The output file to be written
-     * @return success
-     */
-    public static boolean convertDmToFl(
-            Context context,
-            InputStream dmStream,
-            RandomAccessFile flFile) {
-        final String MIMETYPE_DRM_MESSAGE = "application/vnd.oma.drm.message";
-        byte[] dmData = new byte[10000];
-        int totalRead = 0;
-        int numRead;
-        while (true) {
-            try {
-                numRead = dmStream.read(dmData, totalRead, dmData.length - totalRead);
-            } catch (IOException e) {
-                Log.w(TAG, "Failed to read from input file");
-                return false;
-            }
-            if (numRead == -1) {
-                break;
-            }
-            totalRead += numRead;
-            if (totalRead == dmData.length) {
-                // grow array
-                dmData = Arrays.copyOf(dmData, dmData.length + 10000);
-            }
-        }
-        byte[] fileData = Arrays.copyOf(dmData, totalRead);
-
-        DrmManagerClient drmClient = null;
-        try {
-            drmClient = new DrmManagerClient(context);
-        } catch (IllegalArgumentException e) {
-            Log.w(TAG, "DrmManagerClient instance could not be created, context is Illegal.");
-            return false;
-        } catch (IllegalStateException e) {
-            Log.w(TAG, "DrmManagerClient didn't initialize properly.");
-            return false;
-        }
-
-        try {
-            int convertSessionId = -1;
-            try {
-                convertSessionId = drmClient.openConvertSession(MIMETYPE_DRM_MESSAGE);
-            } catch (IllegalArgumentException e) {
-                Log.w(TAG, "Conversion of Mimetype: " + MIMETYPE_DRM_MESSAGE
-                        + " is not supported.", e);
-                return false;
-            } catch (IllegalStateException e) {
-                Log.w(TAG, "Could not access Open DrmFramework.", e);
-                return false;
-            }
-
-            if (convertSessionId < 0) {
-                Log.w(TAG, "Failed to open session.");
-                return false;
-            }
-
-            DrmConvertedStatus convertedStatus = null;
-            try {
-                convertedStatus = drmClient.convertData(convertSessionId, fileData);
-            } catch (IllegalArgumentException e) {
-                Log.w(TAG, "Buffer with data to convert is illegal. Convertsession: "
-                        + convertSessionId, e);
-                return false;
-            } catch (IllegalStateException e) {
-                Log.w(TAG, "Could not convert data. Convertsession: " + convertSessionId, e);
-                return false;
-            }
-
-            if (convertedStatus == null ||
-                    convertedStatus.statusCode != DrmConvertedStatus.STATUS_OK ||
-                    convertedStatus.convertedData == null) {
-                Log.w(TAG, "Error in converting data. Convertsession: " + convertSessionId);
-                try {
-                    DrmConvertedStatus result = drmClient.closeConvertSession(convertSessionId);
-                    if (result.statusCode != DrmConvertedStatus.STATUS_OK) {
-                        Log.w(TAG, "Conversion failed with status: " + result.statusCode);
-                        return false;
-                    }
-                } catch (IllegalStateException e) {
-                    Log.w(TAG, "Could not close session. Convertsession: " +
-                           convertSessionId, e);
-                }
-                return false;
-            }
-
-            try {
-                flFile.write(convertedStatus.convertedData, 0, convertedStatus.convertedData.length);
-            } catch (IOException e) {
-                Log.w(TAG, "Failed to write to output file: " + e);
-                return false;
-            }
-
-            try {
-                convertedStatus = drmClient.closeConvertSession(convertSessionId);
-            } catch (IllegalStateException e) {
-                Log.w(TAG, "Could not close convertsession. Convertsession: " +
-                        convertSessionId, e);
-                return false;
-            }
-
-            if (convertedStatus == null ||
-                    convertedStatus.statusCode != DrmConvertedStatus.STATUS_OK ||
-                    convertedStatus.convertedData == null) {
-                Log.w(TAG, "Error in closing session. Convertsession: " + convertSessionId);
-                return false;
-            }
-
-            try {
-                flFile.seek(convertedStatus.offset);
-                flFile.write(convertedStatus.convertedData);
-            } catch (IOException e) {
-                Log.w(TAG, "Could not update file.", e);
-                return false;
-            }
-
-            return true;
-        } finally {
-            drmClient.close();
-        }
-    }
-
-    /**
-     * @param decoder new MediaCodec object
-     * @param ex MediaExtractor after setDataSource and selectTrack
-     * @param frameMD5Sums reference MD5 checksum for decoded frames
-     * @return true if decoded frames checksums matches reference checksums
-     * @throws IOException
-     */
-    public static boolean verifyDecoder(
-            MediaCodec decoder, MediaExtractor ex, List<String> frameMD5Sums)
-            throws IOException {
-
-        int trackIndex = ex.getSampleTrackIndex();
-        MediaFormat format = ex.getTrackFormat(trackIndex);
-        decoder.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
-        decoder.start();
-
-        boolean sawInputEOS = false;
-        boolean sawOutputEOS = false;
-        final long kTimeOutUs = 5000; // 5ms timeout
-        int decodedFrameCount = 0;
-        int expectedFrameCount = frameMD5Sums.size();
-        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
-
-        while (!sawOutputEOS) {
-            // handle input
-            if (!sawInputEOS) {
-                int inIdx = decoder.dequeueInputBuffer(kTimeOutUs);
-                if (inIdx >= 0) {
-                    ByteBuffer buffer = decoder.getInputBuffer(inIdx);
-                    int sampleSize = ex.readSampleData(buffer, 0);
-                    if (sampleSize < 0) {
-                        final int flagEOS = MediaCodec.BUFFER_FLAG_END_OF_STREAM;
-                        decoder.queueInputBuffer(inIdx, 0, 0, 0, flagEOS);
-                        sawInputEOS = true;
-                    } else {
-                        decoder.queueInputBuffer(inIdx, 0, sampleSize, ex.getSampleTime(), 0);
-                        ex.advance();
-                    }
-                }
-            }
-
-            // handle output
-            int outputBufIndex = decoder.dequeueOutputBuffer(info, kTimeOutUs);
-            if (outputBufIndex >= 0) {
-                try {
-                    if (info.size > 0) {
-                        // Disregard 0-sized buffers at the end.
-                        String md5CheckSum = "";
-                        Image image = decoder.getOutputImage(outputBufIndex);
-                        md5CheckSum = getImageMD5Checksum(image);
-
-                        if (!md5CheckSum.equals(frameMD5Sums.get(decodedFrameCount))) {
-                            Log.d(TAG,
-                                    String.format(
-                                            "Frame %d md5sum mismatch: %s(actual) vs %s(expected)",
-                                            decodedFrameCount, md5CheckSum,
-                                            frameMD5Sums.get(decodedFrameCount)));
-                            return false;
-                        }
-
-                        decodedFrameCount++;
-                    }
-                } catch (Exception e) {
-                    Log.e(TAG, "getOutputImage md5CheckSum failed", e);
-                    return false;
-                } finally {
-                    decoder.releaseOutputBuffer(outputBufIndex, false /* render */);
-                }
-                if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
-                    sawOutputEOS = true;
-                }
-            } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
-                MediaFormat decOutputFormat = decoder.getOutputFormat();
-                Log.d(TAG, "output format " + decOutputFormat);
-            } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
-                Log.i(TAG, "Skip handling MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED");
-            } else if (outputBufIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
-                continue;
-            } else {
-                Log.w(TAG, "decoder.dequeueOutputBuffer() unrecognized index: " + outputBufIndex);
-                return false;
-            }
-        }
-
-        if (decodedFrameCount != expectedFrameCount) {
-            return false;
-        }
-
-        return true;
-    }
-
-    public static String getImageMD5Checksum(Image image) throws Exception {
-        int format = image.getFormat();
-        if (ImageFormat.YUV_420_888 != format) {
-            Log.w(TAG, "unsupported image format");
-            return "";
-        }
-
-        MessageDigest md = MessageDigest.getInstance("MD5");
-
-        Rect crop = image.getCropRect();
-        int cropLeft = crop.left;
-        int cropRight = crop.right;
-        int cropTop = crop.top;
-        int cropBottom = crop.bottom;
-
-        int imageWidth = cropRight - cropLeft;
-        int imageHeight = cropBottom - cropTop;
-
-        Image.Plane[] planes = image.getPlanes();
-        for (int i = 0; i < planes.length; ++i) {
-            ByteBuffer buf = planes[i].getBuffer();
-
-            int width, height, rowStride, pixelStride, x, y, top, left;
-            rowStride = planes[i].getRowStride();
-            pixelStride = planes[i].getPixelStride();
-            if (i == 0) {
-                width = imageWidth;
-                height = imageHeight;
-                left = cropLeft;
-                top = cropTop;
-            } else {
-                width = imageWidth / 2;
-                height = imageHeight /2;
-                left = cropLeft / 2;
-                top = cropTop / 2;
-            }
-            // local contiguous pixel buffer
-            byte[] bb = new byte[width * height];
-            if (buf.hasArray()) {
-                byte b[] = buf.array();
-                int offs = buf.arrayOffset() + left * pixelStride;
-                if (pixelStride == 1) {
-                    for (y = 0; y < height; ++y) {
-                        System.arraycopy(bb, y * width, b, (top + y) * rowStride + offs, width);
-                    }
-                } else {
-                    // do it pixel-by-pixel
-                    for (y = 0; y < height; ++y) {
-                        int lineOffset = offs + (top + y) * rowStride;
-                        for (x = 0; x < width; ++x) {
-                            bb[y * width + x] = b[lineOffset + x * pixelStride];
-                        }
-                    }
-                }
-            } else { // almost always ends up here due to direct buffers
-                int pos = buf.position();
-                if (pixelStride == 1) {
-                    for (y = 0; y < height; ++y) {
-                        buf.position(pos + left + (top + y) * rowStride);
-                        buf.get(bb, y * width, width);
-                    }
-                } else {
-                    // local line buffer
-                    byte[] lb = new byte[rowStride];
-                    // do it pixel-by-pixel
-                    for (y = 0; y < height; ++y) {
-                        buf.position(pos + left * pixelStride + (top + y) * rowStride);
-                        // we're only guaranteed to have pixelStride * (width - 1) + 1 bytes
-                        buf.get(lb, 0, pixelStride * (width - 1) + 1);
-                        for (x = 0; x < width; ++x) {
-                            bb[y * width + x] = lb[x * pixelStride];
-                        }
-                    }
-                }
-                buf.position(pos);
-            }
-            md.update(bb, 0, width * height);
-        }
-
-        return convertByteArrayToHEXString(md.digest());
-    }
-
-    private static String convertByteArrayToHEXString(byte[] ba) throws Exception {
-        StringBuilder result = new StringBuilder();
-        for (int i = 0; i < ba.length; i++) {
-            result.append(Integer.toString((ba[i] & 0xff) + 0x100, 16).substring(1));
-        }
-        return result.toString();
-    }
-
-
-    /*
-     *  -------------------------------------- END --------------------------------------
-     */
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/MoreMatchers.java b/common/device-side/util/src/com/android/compatibility/common/util/MoreMatchers.java
deleted file mode 100644
index cee610e..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/MoreMatchers.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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 com.android.compatibility.common.util;
-
-import org.mockito.ArgumentMatchers;
-
-public class MoreMatchers {
-    private MoreMatchers() {
-    }
-
-    public static <T> T anyOrNull(Class<T> clazz) {
-        return ArgumentMatchers.argThat(value -> true);
-    }
-
-    public static String anyStringOrNull() {
-        return ArgumentMatchers.argThat(value -> true);
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/NullWebViewUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/NullWebViewUtils.java
deleted file mode 100644
index 3153adb..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/NullWebViewUtils.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.compatibility.common.util;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-
-/**
- * Utilities to enable the android.webkit.* CTS tests (and others that rely on a functioning
- * android.webkit.WebView implementation) to determine whether a functioning WebView is present
- * on the device or not.
- *
- * Test cases that require android.webkit.* classes should wrap their first usage of WebView in a
- * try catch block, and pass any exception that is thrown to
- * NullWebViewUtils.determineIfWebViewAvailable. The return value of
- * NullWebViewUtils.isWebViewAvailable will then determine if the test should expect to be able to
- * use a WebView.
- */
-public class NullWebViewUtils {
-
-    private static boolean sWebViewUnavailable;
-
-    /**
-     * @param context Current Activity context, used to query the PackageManager.
-     * @param t       An exception thrown by trying to invoke android.webkit.* APIs.
-     */
-    public static void determineIfWebViewAvailable(Context context, Throwable t) {
-        sWebViewUnavailable = !hasWebViewFeature(context) && checkCauseWasUnsupportedOperation(t);
-    }
-
-    /**
-     * After calling determineIfWebViewAvailable, this returns whether a WebView is available on the
-     * device and wheter the test can rely on it.
-     * @return True iff. PackageManager determined that there is no WebView on the device and the
-     *         exception thrown from android.webkit.* was UnsupportedOperationException.
-     */
-    public static boolean isWebViewAvailable() {
-        return !sWebViewUnavailable;
-    }
-
-    private static boolean hasWebViewFeature(Context context) {
-        // Query the system property that determins if there is a functional WebView on the device.
-        PackageManager pm = context.getPackageManager();
-        return pm.hasSystemFeature(PackageManager.FEATURE_WEBVIEW);
-    }
-
-    private static boolean checkCauseWasUnsupportedOperation(Throwable t) {
-        if (t == null) return false;
-        while (t.getCause() != null) {
-            t = t.getCause();
-        }
-        return t instanceof UnsupportedOperationException;
-    }
-
-    /**
-     * Some CTS tests (by design) first use android.webkit.* from a background thread. This helper
-     * allows the test to catch the UnsupportedOperationException from that background thread, and
-     * then query the result from the test main thread.
-     */
-    public static class NullWebViewFromThreadExceptionHandler
-            implements Thread.UncaughtExceptionHandler {
-        private Throwable mPendingException;
-
-        @Override
-        public void uncaughtException(Thread t, Throwable e) {
-            mPendingException = e;
-        }
-
-        public boolean isWebViewAvailable(Context context) {
-            return hasWebViewFeature(context) ||
-                    !checkCauseWasUnsupportedOperation(mPendingException);
-        }
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/OnFailureRule.java b/common/device-side/util/src/com/android/compatibility/common/util/OnFailureRule.java
deleted file mode 100644
index 585c145..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/OnFailureRule.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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 com.android.compatibility.common.util;
-
-import android.util.Log;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-/**
- * Custom JUnit4 rule that provides a callback upon test failures.
- */
-public abstract class OnFailureRule implements TestRule {
-    private String mLogTag = "OnFailureRule";
-
-    public OnFailureRule() {
-    }
-
-    public OnFailureRule(String logTag) {
-        mLogTag = logTag;
-    }
-
-    @Override
-    public Statement apply(Statement base, Description description) {
-        return new Statement() {
-
-            @Override
-            public void evaluate() throws Throwable {
-                try {
-                    base.evaluate();
-                } catch (Throwable t) {
-                    Log.e(mLogTag, "Test failed: description=" +  description + "\nThrowable=" + t);
-                    onTestFailure(base, description, t);
-                    throw t;
-                }
-            }
-        };
-    }
-
-    protected abstract void onTestFailure(Statement base, Description description, Throwable t);
-}
\ No newline at end of file
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/OneTimeDeviceConfigListener.java b/common/device-side/util/src/com/android/compatibility/common/util/OneTimeDeviceConfigListener.java
deleted file mode 100644
index e5be3f41..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/OneTimeDeviceConfigListener.java
+++ /dev/null
@@ -1,88 +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.
- */
-package com.android.compatibility.common.util;
-
-import android.os.SystemClock;
-import android.provider.DeviceConfig;
-import android.provider.DeviceConfig.OnPropertiesChangedListener;
-import android.provider.DeviceConfig.Properties;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import com.google.common.base.Preconditions;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Helper used to block tests until a device config value has been updated.
- */
-public final class OneTimeDeviceConfigListener implements OnPropertiesChangedListener {
-
-    public static final long DEFAULT_TIMEOUT_MS = 5_000;
-
-    private static final String TAG = OneTimeDeviceConfigListener.class.getSimpleName();
-
-    private final String mNamespace;
-    private final String mKey;
-    private final long mTimeoutMs;
-    private final long mStarted = SystemClock.elapsedRealtime();
-
-    private final CountDownLatch mLatch = new CountDownLatch(1);
-
-    public OneTimeDeviceConfigListener(@NonNull String namespace, @NonNull String key) {
-        this(namespace, key, DEFAULT_TIMEOUT_MS);
-    }
-
-    public OneTimeDeviceConfigListener(@NonNull String namespace, @NonNull String key,
-            long timeoutMs) {
-        mNamespace = Preconditions.checkNotNull(namespace);
-        mKey = Preconditions.checkNotNull(key);
-        mTimeoutMs = timeoutMs;
-    }
-
-    @Override
-    public void onPropertiesChanged(@NonNull Properties properties) {
-        if (!properties.getNamespace().equals(mNamespace)
-                || !properties.getKeyset().contains(mKey)) {
-            Log.d(TAG, "ignoring callback for namespace: " + properties.getNamespace());
-            return;
-        }
-        mLatch.countDown();
-        DeviceConfig.removeOnPropertiesChangedListener(this);
-    }
-
-    /**
-     * Blocks for a few seconds until it's called.
-     *
-     * @throws IllegalStateException if it's not called.
-     */
-    public void assertCalled() {
-        try {
-            final boolean updated = mLatch.await(mTimeoutMs, TimeUnit.MILLISECONDS);
-            if (!updated) {
-                throw new RetryableException(
-                        "Settings " + mKey + " not called in " + mTimeoutMs + "ms");
-            }
-            final long delta = SystemClock.elapsedRealtime() - mStarted;
-            Log.v(TAG, TestNameUtils.getCurrentTestName() + "/" + mKey + ": " + delta + "ms");
-        } catch (InterruptedException e) {
-            Thread.currentThread().interrupt();
-            throw new IllegalStateException("Interrupted", e);
-        }
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/OneTimeSettingsListener.java b/common/device-side/util/src/com/android/compatibility/common/util/OneTimeSettingsListener.java
deleted file mode 100644
index 79c80c9..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/OneTimeSettingsListener.java
+++ /dev/null
@@ -1,94 +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.compatibility.common.util;
-
-import static com.android.compatibility.common.util.SettingsUtils.NAMESPACE_GLOBAL;
-import static com.android.compatibility.common.util.SettingsUtils.NAMESPACE_SECURE;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.util.Log;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Helper used to block tests until a secure settings value has been updated.
- */
-public final class OneTimeSettingsListener extends ContentObserver {
-
-    private static final String TAG = "OneTimeSettingsListener";
-    public static final long DEFAULT_TIMEOUT_MS = 30_000;
-
-    private final CountDownLatch mLatch = new CountDownLatch(1);
-    private final ContentResolver mResolver;
-    private final String mKey;
-    private final long mStarted;
-
-    public OneTimeSettingsListener(Context context, String namespace, String key) {
-        super(new Handler(Looper.getMainLooper()));
-        mStarted = SystemClock.elapsedRealtime();
-        mKey = key;
-        mResolver = context.getContentResolver();
-        final Uri uri;
-        switch (namespace) {
-            case NAMESPACE_SECURE:
-                uri = Settings.Secure.getUriFor(key);
-                break;
-            case NAMESPACE_GLOBAL:
-                uri = Settings.Global.getUriFor(key);
-                break;
-            default:
-                throw new IllegalArgumentException("invalid namespace: " + namespace);
-        }
-        mResolver.registerContentObserver(uri, false, this);
-    }
-
-    @Override
-    public void onChange(boolean selfChange, Uri uri) {
-        mResolver.unregisterContentObserver(this);
-        mLatch.countDown();
-    }
-
-    /**
-     * Blocks for a few seconds until it's called.
-     *
-     * @throws IllegalStateException if it's not called.
-     */
-    public void assertCalled() {
-        try {
-            final boolean updated = mLatch.await(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-            if (!updated) {
-                throw new RetryableException(
-                        "Settings " + mKey + " not called in " + DEFAULT_TIMEOUT_MS + "ms");
-            }
-            final long delta = SystemClock.elapsedRealtime() - mStarted;
-            // TODO: usually it's notified in ~50-150ms, but for some reason it takes ~10s
-            // on some ViewAttributesTest methods, hence the 30s limit
-            Log.v(TAG, TestNameUtils.getCurrentTestName() + "/" + mKey + ": " + delta + "ms");
-        } catch (InterruptedException e) {
-            Thread.currentThread().interrupt();
-            throw new IllegalStateException("Interrupted", e);
-        }
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/PackageUtil.java b/common/device-side/util/src/com/android/compatibility/common/util/PackageUtil.java
deleted file mode 100644
index e7b6976..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/PackageUtil.java
+++ /dev/null
@@ -1,134 +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.compatibility.common.util;
-
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.os.Build;
-import android.support.test.InstrumentationRegistry;
-import android.util.Log;
-
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-
-/**
- * Device-side utility class for PackageManager-related operations
- */
-public class PackageUtil {
-
-    private static final String TAG = PackageUtil.class.getSimpleName();
-
-    private static final int SYSTEM_APP_MASK =
-            ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
-    private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
-
-    /** Returns true if a package with the given name exists on the device */
-    public static boolean exists(String packageName) {
-        try {
-            return (getPackageManager().getApplicationInfo(packageName,
-                    PackageManager.GET_META_DATA) != null);
-        } catch(PackageManager.NameNotFoundException e) {
-            return false;
-        }
-    }
-
-    /** Returns true if a package with the given name AND SHA digest exists on the device */
-    public static boolean exists(String packageName, String sha) {
-        try {
-            if (getPackageManager().getApplicationInfo(
-                    packageName, PackageManager.GET_META_DATA) == null) {
-                return false;
-            }
-            return sha.equals(computePackageSignatureDigest(packageName));
-        } catch (NoSuchAlgorithmException | PackageManager.NameNotFoundException e) {
-            return false;
-        }
-    }
-
-    /** Returns true if the app for the given package name is a system app for this device */
-    public static boolean isSystemApp(String packageName) {
-        try {
-            ApplicationInfo ai = getPackageManager().getApplicationInfo(packageName,
-                    PackageManager.GET_META_DATA);
-            return ai != null && ((ai.flags & SYSTEM_APP_MASK) != 0);
-        } 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 {
-            PackageInfo info = getPackageManager().getPackageInfo(packageName,
-                    PackageManager.GET_META_DATA);
-            return info.versionName;
-        } catch (PackageManager.NameNotFoundException | NullPointerException e) {
-            Log.w(TAG, "Could not find version string for package " + packageName);
-            return null;
-        }
-    }
-
-    /**
-     * Compute the signature SHA digest for a package.
-     * @param package the name of the package for which the signature SHA digest is requested
-     * @return the signature SHA digest
-     */
-    public static String computePackageSignatureDigest(String packageName)
-            throws NoSuchAlgorithmException, PackageManager.NameNotFoundException {
-        PackageInfo packageInfo = getPackageManager()
-                .getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
-        MessageDigest messageDigest = MessageDigest.getInstance("SHA256");
-        messageDigest.update(packageInfo.signatures[0].toByteArray());
-
-        final byte[] digest = messageDigest.digest();
-        final int digestLength = digest.length;
-        final int charCount = 3 * digestLength - 1;
-
-        final char[] chars = new char[charCount];
-        for (int i = 0; i < digestLength; i++) {
-            final int byteHex = digest[i] & 0xFF;
-            chars[i * 3] = HEX_ARRAY[byteHex >>> 4];
-            chars[i * 3 + 1] = HEX_ARRAY[byteHex & 0x0F];
-            if (i < digestLength - 1) {
-                chars[i * 3 + 2] = ':';
-            }
-        }
-        return new String(chars);
-    }
-
-    private static PackageManager getPackageManager() {
-        return InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
-    }
-
-    private static boolean hasDeviceFeature(final String requiredFeature) {
-        return InstrumentationRegistry.getContext()
-                .getPackageManager()
-                .hasSystemFeature(requiredFeature);
-    }
-
-    /**
-     * Rotation support is indicated by explicitly having both landscape and portrait
-     * features or not listing either at all.
-     */
-    public static boolean supportsRotation() {
-        final boolean supportsLandscape = hasDeviceFeature(PackageManager.FEATURE_SCREEN_LANDSCAPE);
-        final boolean supportsPortrait = hasDeviceFeature(PackageManager.FEATURE_SCREEN_PORTRAIT);
-        return (supportsLandscape && supportsPortrait)
-                || (!supportsLandscape && !supportsPortrait);
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ParcelUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/ParcelUtils.java
deleted file mode 100644
index ecaa722..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ParcelUtils.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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 com.android.compatibility.common.util;
-
-import static org.junit.Assert.assertNotNull;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-public class ParcelUtils {
-    private ParcelUtils() {
-    }
-
-    /** Convert a Parcelable into a byte[]. */
-    public static byte[] toBytes(Parcelable p) {
-        assertNotNull(p);
-
-        final Parcel parcel = Parcel.obtain();
-        parcel.writeParcelable(p, 0);
-        byte[] data = parcel.marshall();
-        parcel.recycle();
-
-        return data;
-    }
-
-    /** Decode a byte[] into a Parcelable. */
-    public static <T extends Parcelable> T fromBytes(byte[] data) {
-        assertNotNull(data);
-
-        final Parcel parcel = Parcel.obtain();
-        parcel.unmarshall(data, 0, data.length);
-        parcel.setDataPosition(0);
-        T ret = parcel.readParcelable(ParcelUtils.class.getClassLoader());
-        parcel.recycle();
-
-        return ret;
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/PollingCheck.java b/common/device-side/util/src/com/android/compatibility/common/util/PollingCheck.java
deleted file mode 100644
index bcc3530..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/PollingCheck.java
+++ /dev/null
@@ -1,94 +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.compatibility.common.util;
-
-import java.util.concurrent.Callable;
-
-import junit.framework.Assert;
-
-public abstract class PollingCheck {
-    private static final long TIME_SLICE = 50;
-    private long mTimeout = 3000;
-
-    public static interface PollingCheckCondition {
-        boolean canProceed();
-    }
-
-    public PollingCheck() {
-    }
-
-    public PollingCheck(long timeout) {
-        mTimeout = timeout;
-    }
-
-    protected abstract boolean check();
-
-    public void run() {
-        if (check()) {
-            return;
-        }
-
-        long timeout = mTimeout;
-        while (timeout > 0) {
-            try {
-                Thread.sleep(TIME_SLICE);
-            } catch (InterruptedException e) {
-                Assert.fail("unexpected InterruptedException");
-            }
-
-            if (check()) {
-                return;
-            }
-
-            timeout -= TIME_SLICE;
-        }
-
-        Assert.fail("unexpected timeout");
-    }
-
-    public static void check(CharSequence message, long timeout, Callable<Boolean> condition)
-            throws Exception {
-        while (timeout > 0) {
-            if (condition.call()) {
-                return;
-            }
-
-            Thread.sleep(TIME_SLICE);
-            timeout -= TIME_SLICE;
-        }
-
-        Assert.fail(message.toString());
-    }
-
-    public static void waitFor(final PollingCheckCondition condition) {
-        new PollingCheck() {
-            @Override
-            protected boolean check() {
-                return condition.canProceed();
-            }
-        }.run();
-    }
-
-    public static void waitFor(long timeout, final PollingCheckCondition condition) {
-        new PollingCheck(timeout) {
-            @Override
-            protected boolean check() {
-                return condition.canProceed();
-            }
-        }.run();
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/PropertyUtil.java b/common/device-side/util/src/com/android/compatibility/common/util/PropertyUtil.java
deleted file mode 100644
index c95b8df..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/PropertyUtil.java
+++ /dev/null
@@ -1,173 +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.compatibility.common.util;
-
-import android.os.Build;
-import android.support.test.InstrumentationRegistry;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Device-side utility class for reading properties and gathering information for testing
- * Android device compatibility.
- */
-public class PropertyUtil {
-
-    /**
-     * Name of read-only property detailing the first API level for which the product was
-     * shipped. Property should be undefined for factory ROM products.
-     */
-    public static final String FIRST_API_LEVEL = "ro.product.first_api_level";
-    private static final String BUILD_TYPE_PROPERTY = "ro.build.type";
-    private static final String MANUFACTURER_PROPERTY = "ro.product.manufacturer";
-    private static final String TAG_DEV_KEYS = "dev-keys";
-    private static final String VNDK_VERSION = "ro.vndk.version";
-
-    public static final String GOOGLE_SETTINGS_QUERY =
-            "content query --uri content://com.google.settings/partner";
-
-    /** Value to be returned by getPropertyInt() if property is not found */
-    public static int INT_VALUE_IF_UNSET = -1;
-
-    /** Returns whether the device build is a user build */
-    public static boolean isUserBuild() {
-        return propertyEquals(BUILD_TYPE_PROPERTY, "user");
-    }
-
-    /** Returns whether this build is built with dev-keys */
-    public static boolean isDevKeysBuild() {
-        for (String tag : Build.TAGS.split(",")) {
-            if (TAG_DEV_KEYS.equals(tag.trim())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Return the first API level for this product. If the read-only property is unset,
-     * this means the first API level is the current API level, and the current API level
-     * is returned.
-     */
-    public static int getFirstApiLevel() {
-        int firstApiLevel = getPropertyInt(FIRST_API_LEVEL);
-        return (firstApiLevel == INT_VALUE_IF_UNSET) ? Build.VERSION.SDK_INT : firstApiLevel;
-    }
-
-    /**
-     * Return whether the SDK version of the vendor partiton is newer than the given API level.
-     * If the property is set to non-integer value, this means the vendor partition is using
-     * current API level and true is returned.
-     */
-    public static boolean isVendorApiLevelNewerThan(int apiLevel) {
-        int vendorApiLevel = getPropertyInt(VNDK_VERSION);
-        if (vendorApiLevel == INT_VALUE_IF_UNSET) {
-            return true;
-        }
-        return vendorApiLevel > apiLevel;
-    }
-
-    /**
-     * Return the manufacturer of this product. If unset, return null.
-     */
-    public static String getManufacturer() {
-        return getProperty(MANUFACTURER_PROPERTY);
-    }
-
-    /** Returns a mapping from client ID names to client ID values */
-    public static Map<String, String> getClientIds() throws IOException {
-        Map<String,String> clientIds = new HashMap<>();
-        String queryOutput = SystemUtil.runShellCommand(
-                InstrumentationRegistry.getInstrumentation(), GOOGLE_SETTINGS_QUERY);
-        for (String line : queryOutput.split("[\\r?\\n]+")) {
-            // Expected line format: "Row: 1 _id=123, name=<property_name>, value=<property_value>"
-            Pattern pattern = Pattern.compile("name=([a-z_]*), value=(.*)$");
-            Matcher matcher = pattern.matcher(line);
-            if (matcher.find()) {
-                String name = matcher.group(1);
-                String value = matcher.group(2);
-                if (name.contains("client_id")) {
-                    clientIds.put(name, value); // only add name-value pair for client ids
-                }
-            }
-        }
-        return clientIds;
-    }
-
-    /** Returns whether the property exists on this device */
-    public static boolean propertyExists(String property) {
-        return getProperty(property) != null;
-    }
-
-    /** Returns whether the property value is equal to a given string */
-    public static boolean propertyEquals(String property, String value) {
-        if (value == null) {
-            return !propertyExists(property); // null value implies property does not exist
-        }
-        return value.equals(getProperty(property));
-    }
-
-    /**
-     * Returns whether the property value matches a given regular expression. The method uses
-     * String.matches(), requiring a complete match (i.e. expression matches entire value string)
-     */
-    public static boolean propertyMatches(String property, String regex) {
-        if (regex == null || regex.isEmpty()) {
-            // null or empty pattern implies property does not exist
-            return !propertyExists(property);
-        }
-        String value = getProperty(property);
-        return (value == null) ? false : value.matches(regex);
-    }
-
-    /**
-     * Retrieves the desired integer property, returning INT_VALUE_IF_UNSET if not found.
-     */
-    public static int getPropertyInt(String property) {
-        String value = getProperty(property);
-        if (value == null) {
-            return INT_VALUE_IF_UNSET;
-        }
-        try {
-            return Integer.parseInt(value);
-        } catch (NumberFormatException e) {
-            return INT_VALUE_IF_UNSET;
-        }
-    }
-
-    /** Retrieves the desired property value in string form */
-    public static String getProperty(String property) {
-        Scanner scanner = null;
-        try {
-            Process process = new ProcessBuilder("getprop", property).start();
-            scanner = new Scanner(process.getInputStream());
-            String value = scanner.nextLine().trim();
-            return (value.isEmpty()) ? null : value;
-        } catch (IOException e) {
-            return null;
-        } finally {
-            if (scanner != null) {
-                scanner.close();
-            }
-        }
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ReadElf.java b/common/device-side/util/src/com/android/compatibility/common/util/ReadElf.java
deleted file mode 100644
index feaa9cd..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ReadElf.java
+++ /dev/null
@@ -1,494 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.compatibility.common.util;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * A poor man's implementation of the readelf command. This program is designed
- * to parse ELF (Executable and Linkable Format) files.
- */
-public class ReadElf implements AutoCloseable {
-    /** The magic values for the ELF identification. */
-    private static final byte[] ELFMAG = {
-            (byte) 0x7F, (byte) 'E', (byte) 'L', (byte) 'F', };
-
-    private static final int EI_NIDENT = 16;
-
-    private static final int EI_CLASS = 4;
-    private static final int EI_DATA = 5;
-
-    private static final int EM_386 = 3;
-    private static final int EM_MIPS = 8;
-    private static final int EM_ARM = 40;
-    private static final int EM_X86_64 = 62;
-    // http://en.wikipedia.org/wiki/Qualcomm_Hexagon
-    private static final int EM_QDSP6 = 164;
-    private static final int EM_AARCH64 = 183;
-
-    private static final int ELFCLASS32 = 1;
-    private static final int ELFCLASS64 = 2;
-
-    private static final int ELFDATA2LSB = 1;
-    private static final int ELFDATA2MSB = 2;
-
-    private static final int EV_CURRENT = 1;
-
-    private static final long PT_LOAD = 1;
-
-    private static final int SHT_SYMTAB = 2;
-    private static final int SHT_STRTAB = 3;
-    private static final int SHT_DYNAMIC = 6;
-    private static final int SHT_DYNSYM = 11;
-
-    public static class Symbol {
-        public static final int STB_LOCAL = 0;
-        public static final int STB_GLOBAL = 1;
-        public static final int STB_WEAK = 2;
-        public static final int STB_LOPROC = 13;
-        public static final int STB_HIPROC = 15;
-
-        public static final int STT_NOTYPE = 0;
-        public static final int STT_OBJECT = 1;
-        public static final int STT_FUNC = 2;
-        public static final int STT_SECTION = 3;
-        public static final int STT_FILE = 4;
-        public static final int STT_COMMON = 5;
-        public static final int STT_TLS = 6;
-
-        public final String name;
-        public final int bind;
-        public final int type;
-
-        Symbol(String name, int st_info) {
-            this.name = name;
-            this.bind = (st_info >> 4) & 0x0F;
-            this.type = st_info & 0x0F;
-        }
-
-        @Override
-        public String toString() {
-            return "Symbol[" + name + "," + toBind() + "," + toType() + "]";
-        }
-
-        private String toBind() {
-            switch (bind) {
-                case STB_LOCAL:
-                    return "LOCAL";
-                case STB_GLOBAL:
-                    return "GLOBAL";
-                case STB_WEAK:
-                    return "WEAK";
-            }
-            return "STB_??? (" + bind + ")";
-        }
-
-        private String toType() {
-            switch (type) {
-                case STT_NOTYPE:
-                    return "NOTYPE";
-                case STT_OBJECT:
-                    return "OBJECT";
-                case STT_FUNC:
-                    return "FUNC";
-                case STT_SECTION:
-                    return "SECTION";
-                case STT_FILE:
-                    return "FILE";
-                case STT_COMMON:
-                    return "COMMON";
-                case STT_TLS:
-                    return "TLS";
-            }
-            return "STT_??? (" + type + ")";
-        }
-    }
-
-    private final String mPath;
-    private final RandomAccessFile mFile;
-    private final byte[] mBuffer = new byte[512];
-    private int mEndian;
-    private boolean mIsDynamic;
-    private boolean mIsPIE;
-    private int mType;
-    private int mAddrSize;
-
-    /** Symbol Table offset */
-    private long mSymTabOffset;
-
-    /** Symbol Table size */
-    private long mSymTabSize;
-
-    /** Dynamic Symbol Table offset */
-    private long mDynSymOffset;
-
-    /** Dynamic Symbol Table size */
-    private long mDynSymSize;
-
-    /** Section Header String Table offset */
-    private long mShStrTabOffset;
-
-    /** Section Header String Table size */
-    private long mShStrTabSize;
-
-    /** String Table offset */
-    private long mStrTabOffset;
-
-    /** String Table size */
-    private long mStrTabSize;
-
-    /** Dynamic String Table offset */
-    private long mDynStrOffset;
-
-    /** Dynamic String Table size */
-    private long mDynStrSize;
-
-    /** Symbol Table symbol names */
-    private Map<String, Symbol> mSymbols;
-
-    /** Dynamic Symbol Table symbol names */
-    private Map<String, Symbol> mDynamicSymbols;
-
-    public static ReadElf read(File file) throws IOException {
-        return new ReadElf(file);
-    }
-
-    public static void main(String[] args) throws IOException {
-        for (String arg : args) {
-            ReadElf re = new ReadElf(new File(arg));
-            re.getSymbol("x");
-            re.getDynamicSymbol("x");
-            re.close();
-        }
-    }
-
-    public boolean isDynamic() {
-        return mIsDynamic;
-    }
-
-    public int getType() {
-        return mType;
-    }
-
-    public boolean isPIE() {
-        return mIsPIE;
-    }
-
-    private ReadElf(File file) throws IOException {
-        mPath = file.getPath();
-        mFile = new RandomAccessFile(file, "r");
-
-        if (mFile.length() < EI_NIDENT) {
-            throw new IllegalArgumentException("Too small to be an ELF file: " + file);
-        }
-
-        readHeader();
-    }
-
-    @Override
-    public void close() {
-        try {
-            mFile.close();
-        } catch (IOException ignored) {
-        }
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            close();
-        } finally {
-            super.finalize();
-        }
-    }
-
-    private void readHeader() throws IOException {
-        mFile.seek(0);
-        mFile.readFully(mBuffer, 0, EI_NIDENT);
-
-        if (mBuffer[0] != ELFMAG[0] || mBuffer[1] != ELFMAG[1] ||
-                mBuffer[2] != ELFMAG[2] || mBuffer[3] != ELFMAG[3]) {
-            throw new IllegalArgumentException("Invalid ELF file: " + mPath);
-        }
-
-        int elfClass = mBuffer[EI_CLASS];
-        if (elfClass == ELFCLASS32) {
-            mAddrSize = 4;
-        } else if (elfClass == ELFCLASS64) {
-            mAddrSize = 8;
-        } else {
-            throw new IOException("Invalid ELF EI_CLASS: " + elfClass + ": " + mPath);
-        }
-
-        mEndian = mBuffer[EI_DATA];
-        if (mEndian == ELFDATA2LSB) {
-        } else if (mEndian == ELFDATA2MSB) {
-            throw new IOException("Unsupported ELFDATA2MSB file: " + mPath);
-        } else {
-            throw new IOException("Invalid ELF EI_DATA: " + mEndian + ": " + mPath);
-        }
-
-        mType = readHalf();
-
-        int e_machine = readHalf();
-        if (e_machine != EM_386 && e_machine != EM_X86_64 &&
-                e_machine != EM_AARCH64 && e_machine != EM_ARM &&
-                e_machine != EM_MIPS &&
-                e_machine != EM_QDSP6) {
-            throw new IOException("Invalid ELF e_machine: " + e_machine + ": " + mPath);
-        }
-
-        // AbiTest relies on us rejecting any unsupported combinations.
-        if ((e_machine == EM_386 && elfClass != ELFCLASS32) ||
-                (e_machine == EM_X86_64 && elfClass != ELFCLASS64) ||
-                (e_machine == EM_AARCH64 && elfClass != ELFCLASS64) ||
-                (e_machine == EM_ARM && elfClass != ELFCLASS32) ||
-                (e_machine == EM_QDSP6 && elfClass != ELFCLASS32)) {
-            throw new IOException("Invalid e_machine/EI_CLASS ELF combination: " +
-                    e_machine + "/" + elfClass + ": " + mPath);
-        }
-
-        long e_version = readWord();
-        if (e_version != EV_CURRENT) {
-            throw new IOException("Invalid e_version: " + e_version + ": " + mPath);
-        }
-
-        long e_entry = readAddr();
-
-        long ph_off = readOff();
-        long sh_off = readOff();
-
-        long e_flags = readWord();
-        int e_ehsize = readHalf();
-        int e_phentsize = readHalf();
-        int e_phnum = readHalf();
-        int e_shentsize = readHalf();
-        int e_shnum = readHalf();
-        int e_shstrndx = readHalf();
-
-        readSectionHeaders(sh_off, e_shnum, e_shentsize, e_shstrndx);
-        readProgramHeaders(ph_off, e_phnum, e_phentsize);
-    }
-
-    private void readSectionHeaders(long sh_off, int e_shnum, int e_shentsize, int e_shstrndx)
-            throws IOException {
-        // Read the Section Header String Table offset first.
-        {
-            mFile.seek(sh_off + e_shstrndx * e_shentsize);
-
-            long sh_name = readWord();
-            long sh_type = readWord();
-            long sh_flags = readX(mAddrSize);
-            long sh_addr = readAddr();
-            long sh_offset = readOff();
-            long sh_size = readX(mAddrSize);
-            // ...
-
-            if (sh_type == SHT_STRTAB) {
-                mShStrTabOffset = sh_offset;
-                mShStrTabSize = sh_size;
-            }
-        }
-
-        for (int i = 0; i < e_shnum; ++i) {
-            // Don't bother to re-read the Section Header StrTab.
-            if (i == e_shstrndx) {
-                continue;
-            }
-
-            mFile.seek(sh_off + i * e_shentsize);
-
-            long sh_name = readWord();
-            long sh_type = readWord();
-            long sh_flags = readX(mAddrSize);
-            long sh_addr = readAddr();
-            long sh_offset = readOff();
-            long sh_size = readX(mAddrSize);
-
-            if (sh_type == SHT_SYMTAB || sh_type == SHT_DYNSYM) {
-                final String symTabName = readShStrTabEntry(sh_name);
-                if (".symtab".equals(symTabName)) {
-                    mSymTabOffset = sh_offset;
-                    mSymTabSize = sh_size;
-                } else if (".dynsym".equals(symTabName)) {
-                    mDynSymOffset = sh_offset;
-                    mDynSymSize = sh_size;
-                }
-            } else if (sh_type == SHT_STRTAB) {
-                final String strTabName = readShStrTabEntry(sh_name);
-                if (".strtab".equals(strTabName)) {
-                    mStrTabOffset = sh_offset;
-                    mStrTabSize = sh_size;
-                } else if (".dynstr".equals(strTabName)) {
-                    mDynStrOffset = sh_offset;
-                    mDynStrSize = sh_size;
-                }
-            } else if (sh_type == SHT_DYNAMIC) {
-                mIsDynamic = true;
-            }
-        }
-    }
-
-    private void readProgramHeaders(long ph_off, int e_phnum, int e_phentsize) throws IOException {
-        for (int i = 0; i < e_phnum; ++i) {
-            mFile.seek(ph_off + i * e_phentsize);
-
-            long p_type = readWord();
-            if (p_type == PT_LOAD) {
-                if (mAddrSize == 8) {
-                    // Only in Elf64_phdr; in Elf32_phdr p_flags is at the end.
-                    long p_flags = readWord();
-                }
-                long p_offset = readOff();
-                long p_vaddr = readAddr();
-                // ...
-
-                if (p_vaddr == 0) {
-                    mIsPIE = true;
-                }
-            }
-        }
-    }
-
-    private HashMap<String, Symbol> readSymbolTable(long symStrOffset, long symStrSize,
-            long tableOffset, long tableSize) throws IOException {
-        HashMap<String, Symbol> result = new HashMap<String, Symbol>();
-        mFile.seek(tableOffset);
-        while (mFile.getFilePointer() < tableOffset + tableSize) {
-            long st_name = readWord();
-            int st_info;
-            if (mAddrSize == 8) {
-                st_info = readByte();
-                int st_other = readByte();
-                int st_shndx = readHalf();
-                long st_value = readAddr();
-                long st_size = readX(mAddrSize);
-            } else {
-                long st_value = readAddr();
-                long st_size = readWord();
-                st_info = readByte();
-                int st_other = readByte();
-                int st_shndx = readHalf();
-            }
-            if (st_name == 0) {
-                continue;
-            }
-
-            final String symName = readStrTabEntry(symStrOffset, symStrSize, st_name);
-            if (symName != null) {
-                Symbol s = new Symbol(symName, st_info);
-                result.put(symName, s);
-            }
-        }
-        return result;
-    }
-
-    private String readShStrTabEntry(long strOffset) throws IOException {
-        if (mShStrTabOffset == 0 || strOffset < 0 || strOffset >= mShStrTabSize) {
-            return null;
-        }
-        return readString(mShStrTabOffset + strOffset);
-    }
-
-    private String readStrTabEntry(long tableOffset, long tableSize, long strOffset)
-            throws IOException {
-        if (tableOffset == 0 || strOffset < 0 || strOffset >= tableSize) {
-            return null;
-        }
-        return readString(tableOffset + strOffset);
-    }
-
-    private int readHalf() throws IOException {
-        return (int) readX(2);
-    }
-
-    private long readWord() throws IOException {
-        return readX(4);
-    }
-
-    private long readOff() throws IOException {
-        return readX(mAddrSize);
-    }
-
-    private long readAddr() throws IOException {
-        return readX(mAddrSize);
-    }
-
-    private long readX(int byteCount) throws IOException {
-        mFile.readFully(mBuffer, 0, byteCount);
-
-        int answer = 0;
-        if (mEndian == ELFDATA2LSB) {
-            for (int i = byteCount - 1; i >= 0; i--) {
-                answer = (answer << 8) | (mBuffer[i] & 0xff);
-            }
-        } else {
-            final int N = byteCount - 1;
-            for (int i = 0; i <= N; ++i) {
-                answer = (answer << 8) | (mBuffer[i] & 0xff);
-            }
-        }
-
-        return answer;
-    }
-
-    private String readString(long offset) throws IOException {
-        long originalOffset = mFile.getFilePointer();
-        mFile.seek(offset);
-        mFile.readFully(mBuffer, 0, (int) Math.min(mBuffer.length, mFile.length() - offset));
-        mFile.seek(originalOffset);
-
-        for (int i = 0; i < mBuffer.length; ++i) {
-            if (mBuffer[i] == 0) {
-                return new String(mBuffer, 0, i);
-            }
-        }
-
-        return null;
-    }
-
-    private int readByte() throws IOException {
-        return mFile.read() & 0xff;
-    }
-
-    public Symbol getSymbol(String name) {
-        if (mSymbols == null) {
-            try {
-                mSymbols = readSymbolTable(mStrTabOffset, mStrTabSize, mSymTabOffset, mSymTabSize);
-            } catch (IOException e) {
-                return null;
-            }
-        }
-        return mSymbols.get(name);
-    }
-
-    public Symbol getDynamicSymbol(String name) {
-        if (mDynamicSymbols == null) {
-            try {
-                mDynamicSymbols = readSymbolTable(
-                        mDynStrOffset, mDynStrSize, mDynSymOffset, mDynSymSize);
-            } catch (IOException e) {
-                return null;
-            }
-        }
-        return mDynamicSymbols.get(name);
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ReportLogDeviceInfoStore.java b/common/device-side/util/src/com/android/compatibility/common/util/ReportLogDeviceInfoStore.java
deleted file mode 100644
index 538881d..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ReportLogDeviceInfoStore.java
+++ /dev/null
@@ -1,96 +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.compatibility.common.util;
-
-import android.util.JsonWriter;
-
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-
-public class ReportLogDeviceInfoStore extends DeviceInfoStore {
-
-    private final String mStreamName;
-    private File tempJsonFile;
-
-    public ReportLogDeviceInfoStore(File jsonFile, String streamName) throws Exception {
-        mJsonFile = jsonFile;
-        mStreamName = streamName;
-    }
-
-    /**
-     * Creates the writer and starts the JSON Object for the metric stream.
-     */
-    @Override
-    public void open() throws IOException {
-        // Write new metrics to a temp file to avoid invalid JSON files due to failed tests.
-        BufferedWriter formatWriter;
-        tempJsonFile = File.createTempFile(mStreamName, "-temp-report-log");
-        formatWriter = new BufferedWriter(new FileWriter(tempJsonFile));
-        if (mJsonFile.exists()) {
-            BufferedReader jsonReader = new BufferedReader(new FileReader(mJsonFile));
-            String currentLine;
-            String nextLine = jsonReader.readLine();
-            while ((currentLine = nextLine) != null) {
-                nextLine = jsonReader.readLine();
-                if (nextLine == null && currentLine.charAt(currentLine.length() - 1) == '}') {
-                    // Reopen overall JSON object to write new metrics.
-                    currentLine = currentLine.substring(0, currentLine.length() - 1) + ",";
-                }
-                // Copy to temp file directly to avoid large metrics string in memory.
-                formatWriter.write(currentLine, 0, currentLine.length());
-            }
-            jsonReader.close();
-        } else {
-            formatWriter.write("{", 0 , 1);
-        }
-        // Start new JSON object for new metrics.
-        formatWriter.write("\"" + mStreamName + "\":", 0, mStreamName.length() + 3);
-        formatWriter.flush();
-        formatWriter.close();
-        mJsonWriter = new JsonWriter(new FileWriter(tempJsonFile, true));
-        mJsonWriter.beginObject();
-    }
-
-    /**
-     * Closes the writer.
-     */
-    @Override
-    public void close() throws IOException {
-        // Close JSON Writer.
-        mJsonWriter.endObject();
-        mJsonWriter.close();
-        // Close overall JSON Object.
-        try (BufferedWriter formatWriter = new BufferedWriter(new FileWriter(tempJsonFile, true))) {
-            formatWriter.write("}", 0, 1);
-        }
-        // Copy metrics from temp file and delete temp file.
-        mJsonFile.createNewFile();
-        try (
-                BufferedReader jsonReader = new BufferedReader(new FileReader(tempJsonFile));
-                BufferedWriter metricsWriter = new BufferedWriter(new FileWriter(mJsonFile))
-        ) {
-            String line;
-            while ((line = jsonReader.readLine()) != null) {
-                // Copy from temp file directly to avoid large metrics string in memory.
-                metricsWriter.write(line, 0, line.length());
-            }
-        }
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/RequiredFeatureRule.java b/common/device-side/util/src/com/android/compatibility/common/util/RequiredFeatureRule.java
deleted file mode 100644
index 0968ddc..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/RequiredFeatureRule.java
+++ /dev/null
@@ -1,60 +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.compatibility.common.util;
-
-import android.support.test.InstrumentationRegistry;
-import android.util.Log;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-/**
- * Custom JUnit4 rule that does not run a test case if the device does not have a given feature.
- */
-public class RequiredFeatureRule implements TestRule {
-    private static final String TAG = "RequiredFeatureRule";
-
-    private final String mFeature;
-    private final boolean mHasFeature;
-
-    public RequiredFeatureRule(String feature) {
-        mFeature = feature;
-        mHasFeature = hasFeature(feature);
-    }
-
-    @Override
-    public Statement apply(Statement base, Description description) {
-        return new Statement() {
-
-            @Override
-            public void evaluate() throws Throwable {
-                if (!mHasFeature) {
-                    Log.d(TAG, "skipping "
-                            + description.getClassName() + "#" + description.getMethodName()
-                            + " because device does not have feature '" + mFeature + "'");
-                    return;
-                }
-                base.evaluate();
-            }
-        };
-    }
-
-    public static boolean hasFeature(String feature) {
-        return InstrumentationRegistry.getContext().getPackageManager().hasSystemFeature(feature);
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/RequiredServiceRule.java b/common/device-side/util/src/com/android/compatibility/common/util/RequiredServiceRule.java
deleted file mode 100644
index a4359fd..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/RequiredServiceRule.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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 com.android.compatibility.common.util;
-
-import android.support.test.InstrumentationRegistry;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-/**
- * Custom JUnit4 rule that does not run a test case if the device does not have a given service.
- */
-public class RequiredServiceRule implements TestRule {
-    private static final String TAG = "RequiredServiceRule";
-
-    private final String mService;
-    private final boolean mHasService;
-
-    /**
-     * Creates a rule for the given service.
-     */
-    public RequiredServiceRule(@NonNull String service) {
-        mService = service;
-        mHasService = hasService(service);
-    }
-
-    @Override
-    public Statement apply(@NonNull Statement base, @NonNull Description description) {
-        return new Statement() {
-
-            @Override
-            public void evaluate() throws Throwable {
-                if (!mHasService) {
-                    Log.d(TAG, "skipping "
-                            + description.getClassName() + "#" + description.getMethodName()
-                            + " because device does not have service '" + mService + "'");
-                    return;
-                }
-                base.evaluate();
-            }
-        };
-    }
-
-    /**
-     * Checks if the device has the given service.
-     */
-    public static boolean hasService(@NonNull String service) {
-        // TODO: ideally should call SystemServiceManager directly, but we would need to open
-        // some @Testing APIs for that.
-        String command = "service check " + service;
-        try {
-            String commandOutput = SystemUtil.runShellCommand(
-                    InstrumentationRegistry.getInstrumentation(), command);
-            return !commandOutput.contains("not found");
-        } catch (Exception e) {
-            Log.w(TAG, "Exception running '" + command + "': " + e);
-            return false;
-        }
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/RequiredSystemResourceRule.java b/common/device-side/util/src/com/android/compatibility/common/util/RequiredSystemResourceRule.java
deleted file mode 100644
index ead59943..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/RequiredSystemResourceRule.java
+++ /dev/null
@@ -1,87 +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.
- */
-
-package com.android.compatibility.common.util;
-
-import android.content.res.Resources;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-/**
- * Custom JUnit4 rule that does not run a test case if the device does not define the given system
- * resource.
- */
-public class RequiredSystemResourceRule implements TestRule {
-
-    private static final String TAG = "RequiredSystemResourceRule";
-
-    @NonNull private final String mName;
-    private final boolean mHasResource;
-
-    /**
-     * Creates a rule for the given system resource.
-     *
-     * @param resourceId resource per se
-     * @param name resource name used for debugging purposes
-     */
-    public RequiredSystemResourceRule(@NonNull String name) {
-        mName = name;
-        mHasResource = !TextUtils.isEmpty(getSystemResource(name));
-    }
-
-    @Override
-    public Statement apply(Statement base, Description description) {
-        return new Statement() {
-
-            @Override
-            public void evaluate() throws Throwable {
-                if (!mHasResource) {
-                    Log.d(TAG, "skipping "
-                            + description.getClassName() + "#" + description.getMethodName()
-                            + " because device does not have system resource '" + mName + "'");
-                    return;
-                }
-                base.evaluate();
-            }
-        };
-    }
-
-    /**
-     * Gets the given system resource.
-     */
-    @Nullable
-    public static String getSystemResource(@NonNull String name) {
-        try {
-            final int resourceId = Resources.getSystem().getIdentifier(name, "string", "android");
-            return Resources.getSystem().getString(resourceId);
-        } catch (Exception e) {
-            Log.e(TAG, "could not get value of resource '" + name + "': ", e);
-        }
-        return null;
-    }
-
-    @Override
-    public String toString() {
-        return "RequiredSystemResourceRule[" + mName + "]";
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/Result.java b/common/device-side/util/src/com/android/compatibility/common/util/Result.java
deleted file mode 100644
index 0d8a29a..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/Result.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.compatibility.common.util;
-
-/**
- * Represents the result of a test.
- */
-public interface Result {
-    public static final int RESULT_OK = 1;
-    public static final int RESULT_FAIL = 2;
-    /**
-     * Sets the test result of this object.
-     *
-     * @param resultCode The test result, either {@code RESULT_OK} or {@code RESULT_FAIL}.
-     */
-    void setResult(int resultCode);
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/RetryRule.java b/common/device-side/util/src/com/android/compatibility/common/util/RetryRule.java
deleted file mode 100644
index 32dedea..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/RetryRule.java
+++ /dev/null
@@ -1,90 +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.compatibility.common.util;
-
-import android.util.Log;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-/**
- * Custom JUnit4 rule that retry tests when they fail due to a {@link RetryableException}.
- */
-public class RetryRule implements TestRule {
-
-    private static final String TAG = "RetryRule";
-    private final int mMaxAttempts;
-
-    /**
-     * Retries the underlying test when it catches a {@link RetryableException}.
-     *
-     * @param retries number of retries. Use {@code 0} to disable rule.
-     *
-     * @throws IllegalArgumentException if {@code retries} is less than {@code 0}.
-     */
-    public RetryRule(int retries) {
-        if (retries < 0) {
-            throw new IllegalArgumentException("retries must be more than 0");
-        }
-        mMaxAttempts = retries + 1;
-    }
-
-    @Override
-    public Statement apply(Statement base, Description description) {
-        return new Statement() {
-
-            @Override
-            public void evaluate() throws Throwable {
-                if (mMaxAttempts <= 1) {
-                    Log.v(TAG, "Executing " + description.getDisplayName()
-                            + " right away because mMaxAttempts is " + mMaxAttempts);
-                    base.evaluate();
-                    return;
-                }
-
-                final String name = description.getDisplayName();
-                Throwable caught = null;
-                for (int i = 1; i <= mMaxAttempts; i++) {
-                    try {
-                        base.evaluate();
-                        if (i == 1) {
-                            Log.v(TAG, "Good News, Everyone! " + name + " passed right away");
-                        } else {
-                            Log.d(TAG,
-                                    "Better late than never: " + name + " passed at attempt #" + i);
-                        }
-                        return;
-                    } catch (RetryableException e) {
-                        final Timeout timeout = e.getTimeout();
-                        if (timeout != null) {
-                            long before = timeout.ms();
-                            timeout.increase();
-                            Log.d(TAG, "Increased " + timeout.getName() + " from " + before + "ms"
-                                    + " to " + timeout.ms() + "ms");
-                        }
-                        caught = e;
-                    }
-                    Log.w(TAG, "Arrrr! " + name + " failed at attempt " + i + "/" + mMaxAttempts
-                            + ": " + caught);
-                }
-                Log.e(TAG, "D'OH! " + name + ": giving up after " + mMaxAttempts + " attempts");
-                throw caught;
-            }
-        };
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/RetryableException.java b/common/device-side/util/src/com/android/compatibility/common/util/RetryableException.java
deleted file mode 100644
index 1c6c782..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/RetryableException.java
+++ /dev/null
@@ -1,67 +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.compatibility.common.util;
-
-import androidx.annotation.Nullable;
-
-/**
- * Exception that cause the {@link RetryRule} to re-try a test.
- */
-public class RetryableException extends RuntimeException {
-
-    @Nullable
-    private final Timeout mTimeout;
-
-    public RetryableException(String msg) {
-        this((Timeout) null, msg);
-    }
-
-    public RetryableException(String format, Object...args) {
-        this((Timeout) null, String.format(format, args));
-    }
-
-    public RetryableException(Throwable cause, String format, Object...args) {
-        this((Timeout) null, cause, String.format(format, args), cause);
-    }
-
-    public RetryableException(@Nullable Timeout timeout, String msg) {
-        super(msg);
-        this.mTimeout = timeout;
-    }
-
-    public RetryableException(@Nullable Timeout timeout, String format, Object...args) {
-        super(String.format(format, args));
-        this.mTimeout = timeout;
-    }
-
-    public RetryableException(@Nullable Timeout timeout, Throwable cause, String format,
-            Object...args) {
-        super(String.format(format, args), cause);
-        this.mTimeout = timeout;
-    }
-
-    @Nullable
-    public Timeout getTimeout() {
-        return mTimeout;
-    }
-
-    @Override
-    public String getMessage() {
-        final String superMessage = super.getMessage();
-        return mTimeout == null ? superMessage : superMessage + " (timeout=" + mTimeout + ")";
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/SafeCleanerRule.java b/common/device-side/util/src/com/android/compatibility/common/util/SafeCleanerRule.java
deleted file mode 100644
index 806884c..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/SafeCleanerRule.java
+++ /dev/null
@@ -1,185 +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.compatibility.common.util;
-
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import org.junit.AssumptionViolatedException;
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Callable;
-
-/**
- * Rule used to safely run clean up code after a test is finished, so that exceptions thrown by
- * the cleanup code don't hide exception thrown by the test body
- */
-public final class SafeCleanerRule implements TestRule {
-
-    private static final String TAG = "SafeCleanerRule";
-
-    private final List<ThrowingRunnable> mCleaners = new ArrayList<>();
-    private final List<Callable<List<Throwable>>> mExtraThrowables = new ArrayList<>();
-    private final List<Throwable> mThrowables = new ArrayList<>();
-    private Dumper mDumper;
-
-    /**
-     * Runs {@code cleaner} after the test is finished, catching any {@link Throwable} thrown by it.
-     */
-    public SafeCleanerRule run(@NonNull ThrowingRunnable cleaner) {
-        mCleaners.add(cleaner);
-        return this;
-    }
-
-    /**
-     * Adds exceptions directly.
-     *
-     * <p>Typically used for exceptions caught asychronously during the test execution.
-     */
-    public SafeCleanerRule add(@NonNull Callable<List<Throwable>> exceptions) {
-        mExtraThrowables.add(exceptions);
-        return this;
-    }
-
-    /**
-     * Adds exceptions directly.
-     *
-     * <p>Typically used for exceptions caught during {@code finally} blocks.
-     */
-    public SafeCleanerRule add(Throwable exception) {
-        Log.w(TAG, "Adding exception directly: " + exception);
-        mThrowables.add(exception);
-        return this;
-    }
-
-    /**
-     * Sets a {@link Dumper} used to log errors.
-     */
-    public SafeCleanerRule setDumper(@NonNull Dumper dumper) {
-        mDumper = dumper;
-        return this;
-    }
-
-    @Override
-    public Statement apply(Statement base, Description description) {
-        return new Statement() {
-            @Override
-            public void evaluate() throws Throwable {
-                // First run the test
-                try {
-                    base.evaluate();
-                } catch (Throwable t) {
-                    Log.w(TAG, "Adding exception from main test at index 0: " + t);
-                    mThrowables.add(0, t);
-                }
-
-                // Then the cleanup runners
-                for (ThrowingRunnable runner : mCleaners) {
-                    try {
-                        runner.run();
-                    } catch (Throwable t) {
-                        Log.w(TAG, "Adding exception from cleaner");
-                        mThrowables.add(t);
-                    }
-                }
-
-                // And finally add the extra exceptions
-                for (Callable<List<Throwable>> extraThrowablesCallable : mExtraThrowables) {
-                    final List<Throwable> extraThrowables = extraThrowablesCallable.call();
-                    if (extraThrowables != null && !extraThrowables.isEmpty()) {
-                        Log.w(TAG, "Adding " + extraThrowables.size() + " extra exceptions");
-                        mThrowables.addAll(extraThrowables);
-                    }
-                }
-
-                // Ignore all instances of AssumptionViolatedExceptions
-                mThrowables.removeIf(t -> t instanceof AssumptionViolatedException);
-
-                // Finally, throw up!
-                if (mThrowables.isEmpty()) return;
-
-                final int numberExceptions = mThrowables.size();
-                if (numberExceptions == 1) {
-                    fail(description, mThrowables.get(0));
-                }
-                fail(description, new MultipleExceptions(mThrowables));
-            }
-
-        };
-    }
-
-    private void fail(Description description, Throwable t) throws Throwable {
-        if (mDumper != null) {
-            mDumper.dump(description.getDisplayName(), t);
-        }
-        throw t;
-    }
-
-    private static String toMesssage(List<Throwable> throwables) {
-        String msg = "D'OH!";
-        try {
-            try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) {
-                sw.write("Caught " + throwables.size() + " exceptions\n");
-                for (int i = 0; i < throwables.size(); i++) {
-                    sw.write("\n---- Begin of exception #" + (i + 1) + " ----\n");
-                    final Throwable exception = throwables.get(i);
-                    exception.printStackTrace(pw);
-                    sw.write("---- End of exception #" + (i + 1) + " ----\n\n");
-                }
-                msg = sw.toString();
-            }
-        } catch (IOException e) {
-            // ignore close() errors - should not happen...
-            Log.e(TAG, "Exception closing StringWriter: " + e);
-        }
-        return msg;
-    }
-
-    // VisibleForTesting
-    static class MultipleExceptions extends AssertionError {
-        private final List<Throwable> mThrowables;
-
-        private MultipleExceptions(List<Throwable> throwables) {
-            super(toMesssage(throwables));
-
-            this.mThrowables = throwables;
-        }
-
-        List<Throwable> getThrowables() {
-            return mThrowables;
-        }
-    }
-
-    /**
-     * Optional interface used to dump an error.
-     */
-    public interface Dumper {
-
-        /**
-         * Dumps an error.
-         */
-        void dump(@NonNull String testName, @NonNull Throwable t);
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/SettingsStateChangerRule.java b/common/device-side/util/src/com/android/compatibility/common/util/SettingsStateChangerRule.java
deleted file mode 100644
index 3e0662a..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/SettingsStateChangerRule.java
+++ /dev/null
@@ -1,56 +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.compatibility.common.util;
-
-import android.content.Context;
-import android.provider.Settings;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * JUnit rule used to set a {@link Settings} preference before the test is run.
- *
- * <p>It stores the current value before the test, changes it (if necessary), then restores it after
- * the test (if necessary).
- */
-public class SettingsStateChangerRule extends StateChangerRule<String> {
-
-    /**
-     * Default constructor for the 'secure' context.
-     *
-     * @param context context used to retrieve the {@link Settings} provider.
-     * @param key prefence key.
-     * @param value value to be set before the test is run.
-     */
-    public SettingsStateChangerRule(@NonNull Context context, @NonNull String key,
-            @Nullable String value) {
-        this(context, SettingsUtils.NAMESPACE_SECURE, key, value);
-    }
-
-    /**
-     * Default constructor.
-     *
-     * @param context context used to retrieve the {@link Settings} provider.
-     * @param namespace settings namespace.
-     * @param key prefence key.
-     * @param value value to be set before the test is run.
-     */
-    public SettingsStateChangerRule(@NonNull Context context, @NonNull String namespace,
-            @NonNull String key, @Nullable String value) {
-        super(new SettingsStateManager(context, namespace, key), value);
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/SettingsStateKeeperRule.java b/common/device-side/util/src/com/android/compatibility/common/util/SettingsStateKeeperRule.java
deleted file mode 100644
index 18ca88a..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/SettingsStateKeeperRule.java
+++ /dev/null
@@ -1,39 +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.compatibility.common.util;
-
-import android.content.Context;
-import android.provider.Settings;
-
-import androidx.annotation.NonNull;
-
-/**
- * JUnit rule used to restore a {@link Settings} preference after the test is run.
- *
- * <p>It stores the current value before the test, and restores it after the test (if necessary).
- */
-public class SettingsStateKeeperRule extends StateKeeperRule<String> {
-
-    /**
-     * Default constructor.
-     *
-     * @param context context used to retrieve the {@link Settings} provider.
-     * @param key prefence key.
-     */
-    public SettingsStateKeeperRule(@NonNull Context context, @NonNull String key) {
-        super(new SettingsStateManager(context, SettingsUtils.NAMESPACE_SECURE, key));
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/SettingsStateManager.java b/common/device-side/util/src/com/android/compatibility/common/util/SettingsStateManager.java
deleted file mode 100644
index bab06a6..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/SettingsStateManager.java
+++ /dev/null
@@ -1,65 +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.compatibility.common.util;
-
-import android.content.Context;
-import android.provider.Settings;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.google.common.base.Preconditions;
-
-/**
- * Manages the state of a preference backed by {@link Settings}.
- */
-public class SettingsStateManager implements StateManager<String> {
-
-    private final Context mContext;
-    private final String mNamespace;
-    private final String mKey;
-
-    /**
-     * Default constructor.
-     *
-     * @param context context used to retrieve the {@link Settings} provider.
-     * @param namespace settings namespace.
-     * @param key prefence key.
-     */
-    public SettingsStateManager(@NonNull Context context, @NonNull String namespace,
-            @NonNull String key) {
-        mContext = Preconditions.checkNotNull(context);
-        mNamespace = Preconditions.checkNotNull(namespace);
-        mKey = Preconditions.checkNotNull(key);
-    }
-
-    @Override
-    public void set(@Nullable String value) {
-        SettingsUtils.syncSet(mContext, mNamespace, mKey, value);
-    }
-
-    @Override
-    @Nullable
-    public String get() {
-        return SettingsUtils.get(mNamespace, mKey);
-    }
-
-    @Override
-    public String toString() {
-        return "SettingsStateManager[namespace=" + mNamespace + ", key=" + mKey + "]";
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/SettingsUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/SettingsUtils.java
deleted file mode 100644
index c13de45..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/SettingsUtils.java
+++ /dev/null
@@ -1,201 +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.compatibility.common.util;
-
-import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
-
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import android.content.Context;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * Provides utilities to interact with the device's {@link Settings}.
- */
-public final class SettingsUtils {
-
-    private static final String TAG = SettingsUtils.class.getSimpleName();
-
-    public static final String NAMESPACE_SECURE = "secure";
-    public static final String NAMESPACE_GLOBAL = "global";
-
-    // TODO(b/123885378): we cannot pass an empty value when using 'cmd settings', so we need
-    // to remove the property instead. Once we use the Settings API directly, we can remove this
-    // constant and all if() statements that ues it
-    static final boolean TMP_HACK_REMOVE_EMPTY_PROPERTIES = true;
-
-    /**
-     * Uses a Shell command to set the given preference.
-     */
-    public static void set(@NonNull String namespace, @NonNull String key, @Nullable String value) {
-        if (value == null) {
-            delete(namespace, key);
-            return;
-        }
-        if (TMP_HACK_REMOVE_EMPTY_PROPERTIES && TextUtils.isEmpty(value)) {
-            Log.w(TAG, "Value of " + namespace + ":" + key + " is empty; deleting it instead");
-            delete(namespace, key);
-            return;
-        }
-        runShellCommand("settings put %s %s %s default", namespace, key, value);
-    }
-
-    /**
-     * Sets a preference in the {@link #NAMESPACE_SECURE} namespace.
-     */
-    public static void set(@NonNull String key, @Nullable String value) {
-        set(NAMESPACE_SECURE, key, value);
-    }
-
-    /**
-     * Uses a Shell command to set the given preference, and verifies it was correctly set.
-     */
-    public static void syncSet(@NonNull Context context, @NonNull String namespace,
-            @NonNull String key, @Nullable String value) {
-        if (value == null) {
-            syncDelete(context, namespace, key);
-            return;
-        }
-
-        final String currentValue = get(namespace, key);
-        if (value.equals(currentValue)) {
-            // Already set, ignore
-            return;
-        }
-
-        final OneTimeSettingsListener observer =
-                new OneTimeSettingsListener(context, namespace, key);
-        set(namespace, key, value);
-        observer.assertCalled();
-
-        final String newValue = get(namespace, key);
-        if (TMP_HACK_REMOVE_EMPTY_PROPERTIES && TextUtils.isEmpty(value)) {
-            assertWithMessage("invalid value for '%s' settings", key).that(newValue)
-                .isNull();
-        } else {
-            assertWithMessage("invalid value for '%s' settings", key).that(newValue)
-                    .isEqualTo(value);
-        }
-    }
-
-    /**
-     * Sets a preference in the {@link #NAMESPACE_SECURE} namespace, using a Settings listener to
-     * block until it's set.
-     */
-    public static void syncSet(@NonNull Context context, @NonNull String key,
-            @Nullable String value) {
-        syncSet(context, NAMESPACE_SECURE, key, value);
-    }
-
-    /**
-     * Uses a Shell command to delete the given preference.
-     */
-    public static void delete(@NonNull String namespace, @NonNull String key) {
-        runShellCommand("settings delete %s %s", namespace, key);
-    }
-
-    /**
-     * Deletes a preference in the {@link #NAMESPACE_SECURE} namespace.
-     */
-    public static void delete(@NonNull String key) {
-        delete(NAMESPACE_SECURE, key);
-    }
-
-    /**
-     * Uses a Shell command to delete the given preference, and verifies it was correctly deleted.
-     */
-    public static void syncDelete(@NonNull Context context, @NonNull String namespace,
-            @NonNull String key) {
-
-        final String currentValue = get(namespace, key);
-        if (currentValue == null) {
-            // Already set, ignore
-            return;
-        }
-
-        final OneTimeSettingsListener observer = new OneTimeSettingsListener(context, namespace,
-                key);
-        delete(namespace, key);
-        observer.assertCalled();
-
-        final String newValue = get(namespace, key);
-        assertWithMessage("invalid value for '%s' settings", key).that(newValue).isNull();
-    }
-
-    /**
-     * Deletes a preference in the {@link #NAMESPACE_SECURE} namespace, using a Settings listener to
-     * block until it's deleted.
-     */
-    public static void syncDelete(@NonNull Context context, @NonNull String key) {
-        syncDelete(context, NAMESPACE_SECURE, key);
-    }
-
-    /**
-     * Gets the value of a given preference using Shell command.
-     */
-    @Nullable
-    public static String get(@NonNull String namespace, @NonNull String key) {
-        final String value = runShellCommand("settings get %s %s", namespace, key);
-        if (value == null || value.equals("null")) {
-            return null;
-        } else {
-            return value;
-        }
-    }
-
-    /**
-     * Gets the value of a preference in the {@link #NAMESPACE_SECURE} namespace.
-     */
-    @NonNull
-    public static String get(@NonNull String key) {
-        return get(NAMESPACE_SECURE, key);
-    }
-
-    private SettingsUtils() {
-        throw new UnsupportedOperationException("contain static methods only");
-    }
-
-    /**
-     * @deprecated - use {@link #set(String, String, String)} with {@link #NAMESPACE_GLOBAL}
-     */
-    @Deprecated
-    public static void putGlobalSetting(String key, String value) {
-        set(SettingsUtils.NAMESPACE_GLOBAL, key, value);
-
-    }
-
-    /**
-     * @deprecated - use {@link #set(String, String, String)} with {@link #NAMESPACE_GLOBAL}
-     */
-    @Deprecated
-    public static void putSecureSetting(String key, String value) {
-        set(SettingsUtils.NAMESPACE_SECURE, key, value);
-
-    }
-
-    /**
-     * Get a global setting for the current (foreground) user. Trims ending new line.
-     */
-    public static String getSecureSetting(String key) {
-        return SystemUtil.runShellCommand("settings --user current get secure " + key).trim();
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ShellIdentityUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/ShellIdentityUtils.java
deleted file mode 100644
index f3438ee..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ShellIdentityUtils.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * 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 com.android.compatibility.common.util;
-
-import android.app.UiAutomation;
-import android.support.test.InstrumentationRegistry;
-
-/**
- * Provides utility methods to invoke system and privileged APIs as the shell user.
- */
-public class ShellIdentityUtils {
-
-    /**
-     * Utility interface to invoke a method against the target object.
-     *
-     * @param <T> the type returned by the invoked method.
-     * @param <U> the type of the object against which the method is invoked.
-     */
-    public interface ShellPermissionMethodHelper<T, U> {
-        /**
-         * Invokes the method against the target object.
-         *
-         * @param targetObject the object against which the method should be invoked.
-         * @return the result of the invoked method.
-         */
-        T callMethod(U targetObject);
-    }
-
-    /**
-     * Utility interface to invoke a method against the target object.
-     *
-     * @param <U> the type of the object against which the method is invoked.
-     */
-    public interface ShellPermissionMethodHelperNoReturn<U> {
-        /**
-         * Invokes the method against the target object.
-         *
-         * @param targetObject the object against which the method should be invoked.
-         */
-        void callMethod(U targetObject);
-    }
-
-    /**
-     * Invokes the specified method on the targetObject as the shell user. The method can be invoked
-     * as follows:
-     *
-     * {@code ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
-     *        (tm) -> tm.getDeviceId());}
-     */
-    public static <T, U> T invokeMethodWithShellPermissions(U targetObject,
-            ShellPermissionMethodHelper<T, U> methodHelper) {
-        final UiAutomation uiAutomation =
-                InstrumentationRegistry.getInstrumentation().getUiAutomation();
-        try {
-            uiAutomation.adoptShellPermissionIdentity();
-            return methodHelper.callMethod(targetObject);
-        } finally {
-            uiAutomation.dropShellPermissionIdentity();
-        }
-    }
-
-    /**
-     * Invokes the specified method on the targetObject as the shell user. The method can be invoked
-     * as follows:
-     *
-     * {@code ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
-     *        (tm) -> tm.getDeviceId());}
-     */
-    public static <U> void invokeMethodWithShellPermissionsNoReturn(
-            U targetObject, ShellPermissionMethodHelperNoReturn<U> methodHelper) {
-        final UiAutomation uiAutomation =
-                InstrumentationRegistry.getInstrumentation().getUiAutomation();
-        try {
-            uiAutomation.adoptShellPermissionIdentity();
-            methodHelper.callMethod(targetObject);
-        } finally {
-            uiAutomation.dropShellPermissionIdentity();
-        }
-    }
-
-    /**
-     * Utility interface to invoke a static method.
-     *
-     * @param <T> the type returned by the invoked method.
-     */
-    public interface StaticShellPermissionMethodHelper<T> {
-        /**
-         * Invokes the static method.
-         *
-         * @return the result of the invoked method.
-         */
-        T callMethod();
-    }
-
-    /**
-     * Invokes the specified static method as the shell user. This method can be invoked as follows:
-     *
-     * {@code ShellIdentityUtils.invokeStaticMethodWithShellPermissions(Build::getSerial));}
-     */
-    public static <T> T invokeStaticMethodWithShellPermissions(
-            StaticShellPermissionMethodHelper<T> methodHelper) {
-        final UiAutomation uiAutomation =
-                InstrumentationRegistry.getInstrumentation().getUiAutomation();
-        try {
-            uiAutomation.adoptShellPermissionIdentity();
-            return methodHelper.callMethod();
-        } finally {
-            uiAutomation.dropShellPermissionIdentity();
-        }
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ShellUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/ShellUtils.java
deleted file mode 100644
index 8cb3290..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ShellUtils.java
+++ /dev/null
@@ -1,86 +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.compatibility.common.util;
-
-import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
-
-import android.support.test.InstrumentationRegistry;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-
-/**
- * Provides Shell-based utilities such as running a command.
- */
-public final class ShellUtils {
-
-    private static final String TAG = "ShellHelper";
-
-    /**
-     * Runs a Shell command, returning a trimmed response.
-     */
-    @NonNull
-    public static String runShellCommand(@NonNull String template, Object...args) {
-        final String command = String.format(template, args);
-        Log.d(TAG, "runShellCommand(): " + command);
-        try {
-            final String result = SystemUtil
-                    .runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
-            return TextUtils.isEmpty(result) ? "" : result.trim();
-        } catch (Exception e) {
-            throw new RuntimeException("Command '" + command + "' failed: ", e);
-        }
-    }
-
-    /**
-     * Tap on the view center, it may change window focus.
-     */
-    public static void tap(View view) {
-        final int[] xy = new int[2];
-        view.getLocationOnScreen(xy);
-        final int viewWidth = view.getWidth();
-        final int viewHeight = view.getHeight();
-        final int x = (int) (xy[0] + (viewWidth / 2.0f));
-        final int y = (int) (xy[1] + (viewHeight / 2.0f));
-
-        runShellCommand("input touchscreen tap %d %d", x, y);
-    }
-
-
-    private ShellUtils() {
-        throw new UnsupportedOperationException("contain static methods only");
-    }
-
-    /**
-     * Simulates input of key event.
-     *
-     * @param keyCode key event to fire.
-     */
-    public static void sendKeyEvent(String keyCode) {
-        runShellCommand("input keyevent " + keyCode);
-    }
-
-    /**
-     * Allows an app to draw overlaid windows.
-     */
-    public static void setOverlayPermissions(@NonNull String packageName, boolean allowed) {
-        final String action = allowed ? "allow" : "ignore";
-        runShellCommand("appops set %s SYSTEM_ALERT_WINDOW %s", packageName, action);
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/StateChangerRule.java b/common/device-side/util/src/com/android/compatibility/common/util/StateChangerRule.java
deleted file mode 100644
index 4e59f13..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/StateChangerRule.java
+++ /dev/null
@@ -1,77 +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.compatibility.common.util;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.google.common.base.Preconditions;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-import java.util.Objects;
-
-/**
- * JUnit rule used to prepare a state before the test is run.
- *
- * <p>It stores the current state before the test, changes it (if necessary), then restores it after
- * the test (if necessary).
- *
- * @param <T> value type
- */
-public class StateChangerRule<T> implements TestRule {
-
-    private final StateManager<T> mStateManager;
-    private final T mValue;
-
-    /**
-     * Default constructor.
-     *
-     * @param stateManager abstraction used to mange the state.
-     * @param value value to be set before the test is run.
-     */
-    public StateChangerRule(@NonNull StateManager<T> stateManager, @Nullable T value) {
-        mStateManager = Preconditions.checkNotNull(stateManager);
-        mValue = value;
-    }
-
-    @Override
-    public Statement apply(Statement base, Description description) {
-        return new Statement() {
-
-            @Override
-            public void evaluate() throws Throwable {
-                final T previousValue = mStateManager.get();
-                if (!Objects.equals(previousValue, mValue)) {
-                    mStateManager.set(mValue);
-                }
-                try {
-                    base.evaluate();
-                } finally {
-                    final T currentValue = mStateManager.get();
-                    if (!Objects.equals(currentValue, previousValue)) {
-                        mStateManager.set(previousValue);
-                        // TODO: if set() thowns a RuntimeException, JUnit will silently catch it
-                        // and re-run the test case; it should fail instead.
-                    }
-                }
-            }
-        };
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/StateKeeperRule.java b/common/device-side/util/src/com/android/compatibility/common/util/StateKeeperRule.java
deleted file mode 100644
index ecc02a2..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/StateKeeperRule.java
+++ /dev/null
@@ -1,81 +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.compatibility.common.util;
-
-import androidx.annotation.NonNull;
-
-import com.google.common.base.Preconditions;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-import java.util.Objects;
-
-/**
- * JUnit rule used to restore a state after the test is run.
- *
- * <p>It stores the current state before the test, and restores it after the test (if necessary).
- *
- * @param <T> value type
- */
-public class StateKeeperRule<T> implements TestRule {
-
-    private final StateManager<T> mStateManager;
-
-    /**
-     * Default constructor.
-     *
-     * @param stateManager abstraction used to mange the state.
-     */
-    public StateKeeperRule(@NonNull StateManager<T> stateManager) {
-        mStateManager = Preconditions.checkNotNull(stateManager);
-    }
-
-    /**
-     * Hook for subclasses.
-     */
-    protected void preEvaluate(@SuppressWarnings("unused") Description description) {
-    }
-
-    /**
-     * Hook for subclasses.
-     */
-    protected void postEvaluate(@SuppressWarnings("unused") Description description) {
-    }
-
-    @Override
-    public Statement apply(Statement base, Description description) {
-        return new Statement() {
-
-            @Override
-            public void evaluate() throws Throwable {
-                final T previousValue = mStateManager.get();
-                preEvaluate(description);
-                try {
-                    base.evaluate();
-                } finally {
-                    final T currentValue = mStateManager.get();
-                    if (!Objects.equals(previousValue, currentValue)) {
-                        mStateManager.set(previousValue);
-                    }
-                }
-                postEvaluate(description);
-            }
-        };
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/StateManager.java b/common/device-side/util/src/com/android/compatibility/common/util/StateManager.java
deleted file mode 100644
index 2077e08..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/StateManager.java
+++ /dev/null
@@ -1,36 +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.compatibility.common.util;
-
-import androidx.annotation.Nullable;
-
-/**
- * Abstraction for a state that is managed somewhere, like Android Settings.
- *
- * @param <T> value type
- */
-public interface StateManager<T> {
-
-    /**
-     * Sets a new state.
-     */
-    void set(@Nullable T value);
-
-    /**
-     * Gets the current state.
-     */
-    @Nullable T get();
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/SystemUtil.java b/common/device-side/util/src/com/android/compatibility/common/util/SystemUtil.java
deleted file mode 100644
index fa7f046..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/SystemUtil.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import android.app.ActivityManager;
-import android.app.ActivityManager.MemoryInfo;
-import android.app.Instrumentation;
-import android.app.UiAutomation;
-import android.content.Context;
-import android.os.ParcelFileDescriptor;
-import android.os.StatFs;
-import android.support.test.InstrumentationRegistry;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.concurrent.Callable;
-import java.util.function.Predicate;
-
-public class SystemUtil {
-    private static final String TAG = "CtsSystemUtil";
-
-    public static long getFreeDiskSize(Context context) {
-        final StatFs statFs = new StatFs(context.getFilesDir().getAbsolutePath());
-        return (long)statFs.getAvailableBlocks() * statFs.getBlockSize();
-    }
-
-    public static long getFreeMemory(Context context) {
-        final MemoryInfo info = new MemoryInfo();
-        ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryInfo(info);
-        return info.availMem;
-    }
-
-    public static long getTotalMemory(Context context) {
-        final MemoryInfo info = new MemoryInfo();
-        ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryInfo(info);
-        return info.totalMem;
-    }
-
-    /**
-     * Executes a shell command using shell user identity, and return the standard output in string
-     * <p>Note: calling this function requires API level 21 or above
-     * @param instrumentation {@link Instrumentation} instance, obtained from a test running in
-     * instrumentation framework
-     * @param cmd the command to run
-     * @return the standard output of the command
-     * @throws Exception
-     */
-    public static String runShellCommand(Instrumentation instrumentation, String cmd)
-            throws IOException {
-        Log.v(TAG, "Running command: " + cmd);
-        if (cmd.startsWith("pm grant ") || cmd.startsWith("pm revoke ")) {
-            throw new UnsupportedOperationException("Use UiAutomation.grantRuntimePermission() "
-                    + "or revokeRuntimePermission() directly, which are more robust.");
-        }
-        ParcelFileDescriptor pfd = instrumentation.getUiAutomation().executeShellCommand(cmd);
-        byte[] buf = new byte[512];
-        int bytesRead;
-        FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
-        StringBuffer stdout = new StringBuffer();
-        while ((bytesRead = fis.read(buf)) != -1) {
-            stdout.append(new String(buf, 0, bytesRead));
-        }
-        fis.close();
-        return stdout.toString();
-    }
-
-    /**
-     * Simpler version of {@link #runShellCommand(Instrumentation, String)}.
-     */
-    public static String runShellCommand(String cmd) {
-        try {
-            return runShellCommand(InstrumentationRegistry.getInstrumentation(), cmd);
-        } catch (IOException e) {
-            fail("Failed reading command output: " + e);
-            return "";
-        }
-    }
-
-    /**
-     * Same as {@link #runShellCommand(String)}, with optionally
-     * check the result using {@code resultChecker}.
-     */
-    public static String runShellCommand(String cmd, Predicate<String> resultChecker) {
-        final String result = runShellCommand(cmd);
-        if (resultChecker != null) {
-            assertTrue("Assertion failed. Command was: " + cmd + "\n"
-                    + "Output was:\n" + result,
-                    resultChecker.test(result));
-        }
-        return result;
-    }
-
-    /**
-     * Same as {@link #runShellCommand(String)}, but fails if the output is not empty.
-     */
-    public static String runShellCommandForNoOutput(String cmd) {
-        final String result = runShellCommand(cmd);
-        assertTrue("Command failed. Command was: " + cmd + "\n"
-                + "Didn't expect any output, but the output was:\n" + result,
-                result.length() == 0);
-        return result;
-    }
-
-    /**
-     * Runs a command and print the result on logcat.
-     */
-    public static void runCommandAndPrintOnLogcat(String logtag, String cmd) {
-        Log.i(logtag, "Executing: " + cmd);
-        final String output = runShellCommand(cmd);
-        for (String line : output.split("\\n", -1)) {
-            Log.i(logtag, line);
-        }
-    }
-
-    /**
-     * Runs a command and return the section matching the patterns.
-     *
-     * @see TextUtils#extractSection
-     */
-    public static String runCommandAndExtractSection(String cmd,
-            String extractionStartRegex, boolean startInclusive,
-            String extractionEndRegex, boolean endInclusive) {
-        return TextUtils.extractSection(runShellCommand(cmd), extractionStartRegex, startInclusive,
-                extractionEndRegex, endInclusive);
-    }
-
-    /**
-     * Runs a {@link ThrowingRunnable} adopting Shell's permissions.
-     */
-    public static void runWithShellPermissionIdentity(@NonNull ThrowingRunnable runnable) {
-        final UiAutomation automan = InstrumentationRegistry.getInstrumentation().getUiAutomation();
-        runWithShellPermissionIdentity(automan, runnable);
-    }
-
-    /**
-     * Runs a {@link ThrowingRunnable} adopting a subset of Shell's permissions.
-     */
-    public static void runWithShellPermissionIdentity(@NonNull ThrowingRunnable runnable,
-            String... permissions) {
-        final UiAutomation automan = InstrumentationRegistry.getInstrumentation().getUiAutomation();
-        runWithShellPermissionIdentity(automan, runnable, permissions);
-    }
-
-    /**
-     * Runs a {@link ThrowingRunnable} adopting Shell's permissions, where you can specify the
-     * uiAutomation used.
-     */
-    public static void runWithShellPermissionIdentity(
-            @NonNull UiAutomation automan, @NonNull ThrowingRunnable runnable) {
-        runWithShellPermissionIdentity(automan, runnable, null /* permissions */);
-    }
-
-    /**
-     * Runs a {@link ThrowingRunnable} adopting Shell's permissions, where you can specify the
-     * uiAutomation used.
-     * @param automan UIAutomation to use.
-     * @param runnable The code to run with Shell's identity.
-     * @param permissions A subset of Shell's permissions. Passing {@code null} will use all
-     *                    available permissions.
-     */
-    public static void runWithShellPermissionIdentity(@NonNull UiAutomation automan,
-            @NonNull ThrowingRunnable runnable, String... permissions) {
-        automan.adoptShellPermissionIdentity(permissions);
-        try {
-            runnable.run();
-        } catch (Exception e) {
-            throw new RuntimeException("Caught exception", e);
-        } finally {
-            automan.dropShellPermissionIdentity();
-        }
-    }
-
-    /**
-     * Calls a {@link Callable} adopting Shell's permissions.
-     */
-    public static <T> T callWithShellPermissionIdentity(@NonNull Callable<T> callable)
-            throws Exception {
-        final UiAutomation automan = InstrumentationRegistry.getInstrumentation().getUiAutomation();
-        automan.adoptShellPermissionIdentity();
-        try {
-            return callable.call();
-        } finally {
-            automan.dropShellPermissionIdentity();
-        }
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/TestNameUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/TestNameUtils.java
deleted file mode 100644
index 2dbeaab..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/TestNameUtils.java
+++ /dev/null
@@ -1,67 +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.compatibility.common.util;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * Generic helper used to set / get the the name of the test being run.
- *
- * <p>Typically used on {@code @Rule} classes.
- */
-public final class TestNameUtils {
-
-    private static String sCurrentTestName;
-    private static String sCurrentTestClass;
-
-    /**
-     * Gets the name of the test current running.
-     */
-    @NonNull
-    public static String getCurrentTestName() {
-        if (sCurrentTestName != null) return sCurrentTestName;
-        if (sCurrentTestClass != null) return sCurrentTestClass;
-        return "(Unknown test)";
-    }
-
-    /**
-     * Sets the name of the test current running
-     */
-    public static void setCurrentTestName(@Nullable String name) {
-        sCurrentTestName = name;
-    }
-
-    /**
-     * Sets the name of the test class current running
-     */
-    public static void setCurrentTestClass(@Nullable String testClass) {
-        sCurrentTestClass = testClass;
-    }
-
-    /**
-     * Checks whether a test is running, based on whether {@link #setCurrentTestName(String)} was
-     * called.
-     */
-    public static boolean isRunningTest() {
-        return sCurrentTestName != null;
-    }
-
-    private TestNameUtils() {
-        throw new UnsupportedOperationException("contain static methods only");
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/TestThread.java b/common/device-side/util/src/com/android/compatibility/common/util/TestThread.java
deleted file mode 100644
index 894b9c8..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/TestThread.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.compatibility.common.util;
-
-/**
- * Thread class for executing a Runnable containing assertions in a separate thread.
- * Uncaught exceptions in the Runnable are rethrown in the context of the the thread
- * calling the <code>runTest()</code> method.
- */
-public final class TestThread extends Thread {
-    private Throwable mThrowable;
-    private Runnable mTarget;
-
-    public TestThread(Runnable target) {
-        mTarget = target;
-    }
-
-    @Override
-    public final void run() {
-        try {
-            mTarget.run();
-        } catch (Throwable t) {
-            mThrowable = t;
-        }
-    }
-
-    /**
-     * Run the target Runnable object and wait until the test finish or throw
-     * out Exception if test fail.
-     *
-     * @param runTime
-     * @throws Throwable
-     */
-    public void runTest(long runTime) throws Throwable {
-        start();
-        joinAndCheck(runTime);
-    }
-
-    /**
-     * Get the Throwable object which is thrown when test running
-     * @return  The Throwable object
-     */
-    public Throwable getThrowable() {
-        return mThrowable;
-    }
-
-    /**
-     * Set the Throwable object which is thrown when test running
-     * @param t The Throwable object
-     */
-    public void setThrowable(Throwable t) {
-        mThrowable = t;
-    }
-
-    /**
-     * Wait for the test thread to complete and throw the stored exception if there is one.
-     *
-     * @param runTime The time to wait for the test thread to complete.
-     * @throws Throwable
-     */
-    public void joinAndCheck(long runTime) throws Throwable {
-        this.join(runTime);
-        if (this.isAlive()) {
-            this.interrupt();
-            this.join(runTime);
-            throw new Exception("Thread did not finish within allotted time.");
-        }
-        checkException();
-    }
-
-    /**
-     * Check whether there is an exception when running Runnable object.
-     * @throws Throwable
-     */
-    public void checkException() throws Throwable {
-        if (mThrowable != null) {
-            throw mThrowable;
-        }
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/TestUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/TestUtils.java
deleted file mode 100644
index 3b82cb7..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/TestUtils.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * 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 com.android.compatibility.common.util;
-
-import static junit.framework.Assert.fail;
-
-import android.os.SystemClock;
-import android.util.Log;
-
-import java.util.function.BooleanSupplier;
-
-public class TestUtils {
-    private static final String TAG = "CtsTestUtils";
-
-    private TestUtils() {
-    }
-
-    public static final int DEFAULT_TIMEOUT_SECONDS = 30;
-
-    /** Print an error log and fail. */
-    public static void failWithLog(String message) {
-        Log.e(TAG, message);
-        fail(message);
-    }
-
-    @FunctionalInterface
-    public interface BooleanSupplierWithThrow {
-        boolean getAsBoolean() throws Exception;
-    }
-
-    @FunctionalInterface
-    public interface RunnableWithThrow {
-        void run() throws Exception;
-    }
-
-    /**
-     * Wait until {@code predicate} is satisfied, or fail, with {@link #DEFAULT_TIMEOUT_SECONDS}.
-     */
-    public static void waitUntil(String message, BooleanSupplierWithThrow predicate)
-            throws Exception {
-        waitUntil(message, 0, predicate);
-    }
-
-    /**
-     * Wait until {@code predicate} is satisfied, or fail, with a given timeout.
-     */
-    public static void waitUntil(
-            String message, int timeoutSecond, BooleanSupplierWithThrow predicate)
-            throws Exception {
-        if (timeoutSecond <= 0) {
-            timeoutSecond = DEFAULT_TIMEOUT_SECONDS;
-        }
-        int sleep = 125;
-        final long timeout = SystemClock.uptimeMillis() + timeoutSecond * 1000;
-        while (SystemClock.uptimeMillis() < timeout) {
-            if (predicate.getAsBoolean()) {
-                return; // okay
-            }
-            Thread.sleep(sleep);
-            sleep *= 5;
-            sleep = Math.min(2000, sleep);
-        }
-        failWithLog("Timeout: " + message);
-    }
-
-    /**
-     * Run a Runnable {@code r}, and if it throws, also run {@code onFailure}.
-     */
-    public static void runWithFailureHook(RunnableWithThrow r, RunnableWithThrow onFailure)
-            throws Exception {
-        if (r == null) {
-            throw new NullPointerException("r");
-        }
-        if (onFailure == null) {
-            throw new NullPointerException("onFailure");
-        }
-        try {
-            r.run();
-        } catch (Throwable th) {
-            Log.e(TAG, "Caught exception: " + th, th);
-            onFailure.run();
-            throw th;
-        }
-    }
-
-    /**
-     * Synchronized wait for a specified condition.
-     *
-     * @param notifyLock Lock that will be notified when the condition might have changed
-     * @param condition The condition to check
-     * @param timeoutMs The timeout in millis
-     * @param conditionName The name to include in the assertion. If null, will be given a default.
-     */
-    public static void waitOn(Object notifyLock, BooleanSupplier condition,
-            long timeoutMs, String conditionName) {
-        if (conditionName == null) conditionName = "condition";
-        if (condition.getAsBoolean()) return;
-
-        synchronized (notifyLock) {
-            try {
-                long timeSlept = 0;
-                while (!condition.getAsBoolean() && timeSlept < timeoutMs) {
-                    long sleepStart = SystemClock.uptimeMillis();
-                    notifyLock.wait(timeoutMs - timeSlept);
-                    timeSlept += SystemClock.uptimeMillis() - sleepStart;
-                }
-                if (!condition.getAsBoolean()) {
-                    throw new AssertionError("Timed out after " + timeSlept
-                            + "ms waiting for: " + conditionName);
-                }
-            } catch (InterruptedException e) {
-                throw new RuntimeException(e);
-            }
-        }
-    }
-}
-
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/TextUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/TextUtils.java
deleted file mode 100644
index cca2652..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/TextUtils.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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 com.android.compatibility.common.util;
-
-import java.util.regex.Pattern;
-
-public class TextUtils {
-    private TextUtils() {
-    }
-
-    /**
-     * Return the first section in {@code source} between the line matches
-     * {@code extractionStartRegex} and the line matches {@code extractionEndRegex}.
-     */
-    public static String extractSection(String source,
-            String extractionStartRegex, boolean startInclusive,
-            String extractionEndRegex, boolean endInclusive) {
-
-        final Pattern start = Pattern.compile(extractionStartRegex);
-        final Pattern end = Pattern.compile(extractionEndRegex);
-
-        final StringBuilder sb = new StringBuilder();
-        final String[] lines = source.split("\\n", -1);
-
-        int i = 0;
-        for (; i < lines.length; i++) {
-            final String line = lines[i];
-            if (start.matcher(line).matches()) {
-                if (startInclusive) {
-                    sb.append(line);
-                    sb.append('\n');
-                }
-                i++;
-                break;
-            }
-        }
-
-        for (; i < lines.length; i++) {
-            final String line = lines[i];
-            if (end.matcher(line).matches()) {
-                if (endInclusive) {
-                    sb.append(line);
-                    sb.append('\n');
-                }
-                break;
-            }
-            sb.append(line);
-            sb.append('\n');
-        }
-        return sb.toString();
-    }
-
-    /**
-     * Creates a string consisted of {@code size} chars {@code c}.
-     */
-    public static String repeat(char c, int size) {
-        StringBuilder builder = new StringBuilder(size);
-        for (int i = 1; i <= size; i++) {
-            builder.append(c);
-        }
-        return builder.toString();
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ThermalUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/ThermalUtils.java
deleted file mode 100644
index a61ffc1..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ThermalUtils.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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 com.android.compatibility.common.util;
-
-import android.util.Log;
-
-/**
- * Device-side utility class for override/reset thermal status.
- */
-public final class ThermalUtils {
-    private static final String TAG = "CtsThermalUtils";
-
-    private ThermalUtils() {}
-
-    /** Make the target device think it's not throttling. */
-    public static void overrideThermalNotThrottling() throws Exception {
-        overrideThermalStatus(0);
-    }
-
-    /**
-     * Make the target device think it's in given throttling status.
-     * @param status thermal status defined in android.os.Temperature
-     */
-    public static void overrideThermalStatus(int status) throws Exception {
-        SystemUtil.runShellCommandForNoOutput("cmd thermalservice override-status " + status);
-
-        Log.d(TAG, "override-status " + status);
-    }
-
-    /** Cancel the thermal override status on target device. */
-    public static void resetThermalStatus() throws Exception {
-        SystemUtil.runShellCommandForNoOutput("cmd thermalservice reset");
-
-        Log.d(TAG, "reset");
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ThreadUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/ThreadUtils.java
deleted file mode 100644
index 3948628..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ThreadUtils.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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 com.android.compatibility.common.util;
-
-import android.os.SystemClock;
-
-public final class ThreadUtils {
-    private ThreadUtils() {
-    }
-
-    public static void sleepUntilRealtime(long realtime) throws Exception {
-        Thread.sleep(Math.max(0, realtime - SystemClock.elapsedRealtime()));
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/Timeout.java b/common/device-side/util/src/com/android/compatibility/common/util/Timeout.java
deleted file mode 100644
index 9ac6323..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/Timeout.java
+++ /dev/null
@@ -1,202 +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.compatibility.common.util;
-
-import android.os.SystemClock;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-
-import java.util.concurrent.Callable;
-
-/**
- * A "smart" timeout that supports exponential backoff.
- */
-//TODO: move to common CTS Code
-public final class Timeout {
-
-    private static final String TAG = "Timeout";
-    private static final boolean VERBOSE = true;
-
-    private final String mName;
-    private long mCurrentValue;
-    private final float mMultiplier;
-    private final long mMaxValue;
-
-    private final Sleeper mSleeper;
-
-    private static final Sleeper DEFAULT_SLEEPER = (t) -> SystemClock.sleep(t);
-
-    /**
-     * Default constructor.
-     *
-     * @param name name to be used for logging purposes.
-     * @param initialValue initial timeout value, in ms.
-     * @param multiplier multiplier for {@link #increase()}.
-     * @param maxValue max timeout value (in ms) set by {@link #increase()}.
-     *
-     * @throws IllegalArgumentException if {@code name} is {@code null} or empty,
-     * {@code initialValue}, {@code multiplir} or {@code maxValue} are less than {@code 1},
-     * or if {@code initialValue} is higher than {@code maxValue}
-     */
-    public Timeout(String name, long initialValue, float multiplier, long maxValue) {
-        this(DEFAULT_SLEEPER, name, initialValue, multiplier, maxValue);
-    }
-
-    @VisibleForTesting
-    Timeout(@NonNull Sleeper sleeper, String name, long initialValue, float multiplier,
-            long maxValue) {
-        if (initialValue < 1 || maxValue < 1 || initialValue > maxValue) {
-            throw new IllegalArgumentException(
-                    "invalid initial and/or max values: " + initialValue + " and " + maxValue);
-        }
-        if (multiplier <= 1) {
-            throw new IllegalArgumentException("multiplier must be higher than 1: " + multiplier);
-        }
-        if (TextUtils.isEmpty(name)) {
-            throw new IllegalArgumentException("no name");
-        }
-        mSleeper = sleeper;
-        mName = name;
-        mCurrentValue = initialValue;
-        mMultiplier = multiplier;
-        mMaxValue = maxValue;
-        Log.d(TAG, "Constructor: " + this + " at " + TestNameUtils.getCurrentTestName());
-    }
-
-    /**
-     * Gets the current timeout, in ms.
-     */
-    public long ms() {
-        return mCurrentValue;
-    }
-
-    /**
-     * Gets the max timeout, in ms.
-     */
-    public long getMaxValue() {
-        return mMaxValue;
-    }
-
-    /**
-     * @return the mMultiplier
-     */
-    public float getMultiplier() {
-        return mMultiplier;
-    }
-
-    /**
-     * Gets the user-friendly name of this timeout.
-     */
-    @NonNull
-    public String getName() {
-        return mName;
-    }
-
-    /**
-     * Increases the current value by the {@link #getMultiplier()}, up to {@link #getMaxValue()}.
-     *
-     * @return previous current value.
-     */
-    public long increase() {
-        final long oldValue = mCurrentValue;
-        mCurrentValue = Math.min(mMaxValue, (long) (mCurrentValue * mMultiplier));
-        if (oldValue != mCurrentValue) {
-            Log.w(TAG, mName + " increased from " + oldValue + "ms to " + mCurrentValue + "ms at "
-                    + TestNameUtils.getCurrentTestName());
-        }
-        return oldValue;
-    }
-
-    /**
-     * Runs a {@code job} many times before giving up, sleeping between failed attempts up to
-     * {@link #ms()}.
-     *
-     * @param description description of the job for logging purposes.
-     * @param job job to be run, must return {@code null} if it failed and should be retried.
-     * @throws RetryableException if all attempts failed.
-     * @throws IllegalArgumentException if {@code description} is {@code null} or empty, if
-     * {@code job} is {@code  null}, or if {@code maxAttempts} is less than 1.
-     * @throws Exception any other exception thrown by helper methods.
-     *
-     * @return job's result.
-     */
-    public <T> T run(String description, Callable<T> job) throws Exception {
-        return run(description, 100, job);
-    }
-
-    /**
-     * Runs a {@code job} many times before giving up, sleeping between failed attempts up to
-     * {@link #ms()}.
-     *
-     * @param description description of the job for logging purposes.
-     * @param job job to be run, must return {@code null} if it failed and should be retried.
-     * @param retryMs how long to sleep between failures.
-     * @throws RetryableException if all attempts failed.
-     * @throws IllegalArgumentException if {@code description} is {@code null} or empty, if
-     * {@code job} is {@code  null}, or if {@code maxAttempts} is less than 1.
-     * @throws Exception any other exception thrown by helper methods.
-     *
-     * @return job's result.
-     */
-    public <T> T run(String description, long retryMs, Callable<T> job) throws Exception {
-        if (TextUtils.isEmpty(description)) {
-            throw new IllegalArgumentException("no description");
-        }
-        if (job == null) {
-            throw new IllegalArgumentException("no job");
-        }
-        if (retryMs < 1) {
-            throw new IllegalArgumentException("need to sleep at least 1ms, right?");
-        }
-        long startTime = SystemClock.elapsedRealtime();
-        int attempt = 0;
-        long totalSlept = 0;
-        while (SystemClock.elapsedRealtime() - startTime <= mCurrentValue) {
-            final T result = job.call();
-            if (result != null) {
-                // Good news, everyone: job succeeded on first attempt!
-                return result;
-            }
-            attempt++;
-            final long napTime = Math.min(retryMs, mCurrentValue - totalSlept);
-            if (VERBOSE) {
-                Log.v(TAG, description + " failed at attempt #" + attempt + "; sleeping for "
-                        + napTime + "ms before trying again");
-            }
-            mSleeper.sleep(napTime);
-            totalSlept += napTime;
-
-            retryMs *= mMultiplier;
-        }
-        Log.w(TAG, description + " failed after " + attempt + " attempts and " + totalSlept + "ms: "
-                + this);
-        throw new RetryableException(this, description);
-    }
-
-    @Override
-    public String toString() {
-        return mName + ": [current=" + mCurrentValue + "ms; multiplier=" + mMultiplier + "x; max="
-                + mMaxValue + "ms]";
-    }
-
-    @VisibleForTesting
-    interface Sleeper {
-        void sleep(long napTimeMs);
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/Visitor.java b/common/device-side/util/src/com/android/compatibility/common/util/Visitor.java
deleted file mode 100644
index ab4bbfb..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/Visitor.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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 com.android.compatibility.common.util;
-
-import androidx.annotation.NonNull;
-
-/**
- * Implements the Visitor design pattern
- *
- * @param <V> visited object
- */
-public interface Visitor<V> {
-
-    /**
-     * Visit that object.
-     */
-    void visit(@NonNull V visited);
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/WatchDog.java b/common/device-side/util/src/com/android/compatibility/common/util/WatchDog.java
deleted file mode 100644
index efcc693..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/WatchDog.java
+++ /dev/null
@@ -1,103 +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.compatibility.common.util;
-
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-import android.util.Log;
-
-import junit.framework.Assert;
-
-/**
- * class for checking if rendering function is alive or not.
- * panic if watch-dog is not reset over certain amount of time
- */
-public class WatchDog implements Runnable {
-    private static final String TAG = "WatchDog";
-    private Thread mThread;
-    private Semaphore mSemaphore;
-    private volatile boolean mStopRequested;
-    private final long mTimeoutInMilliSecs;
-    private TimeoutCallback mCallback = null;
-
-    public WatchDog(long timeoutInMilliSecs) {
-        mTimeoutInMilliSecs = timeoutInMilliSecs;
-    }
-
-    public WatchDog(long timeoutInMilliSecs, TimeoutCallback callback) {
-        this(timeoutInMilliSecs);
-        mCallback = callback;
-    }
-
-    /** start watch-dog */
-    public void start() {
-        Log.i(TAG, "start");
-        mStopRequested = false;
-        mSemaphore = new Semaphore(0);
-        mThread = new Thread(this);
-        mThread.start();
-    }
-
-    /** stop watch-dog */
-    public void stop() {
-        Log.i(TAG, "stop");
-        if (mThread == null) {
-            return; // already finished
-        }
-        mStopRequested = true;
-        mSemaphore.release();
-        try {
-            mThread.join();
-        } catch (InterruptedException e) {
-            // ignore
-        }
-        mThread = null;
-        mSemaphore = null;
-    }
-
-    /** resets watch-dog, thus prevent it from panic */
-    public void reset() {
-        if (!mStopRequested) { // stop requested, but rendering still on-going
-            mSemaphore.release();
-        }
-    }
-
-    @Override
-    public void run() {
-        while (!mStopRequested) {
-            try {
-                boolean success = mSemaphore.tryAcquire(mTimeoutInMilliSecs, TimeUnit.MILLISECONDS);
-                if (mCallback == null) {
-                    Assert.assertTrue("Watchdog timed-out", success);
-                } else if (!success) {
-                    mCallback.onTimeout();
-                }
-            } catch (InterruptedException e) {
-                // this thread will not be interrupted,
-                // but if it happens, just check the exit condition.
-            }
-        }
-    }
-
-    /**
-     * Called by the Watchdog when it has timed out.
-     */
-    public interface TimeoutCallback {
-
-        public void onTimeout();
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/WidgetTestUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/WidgetTestUtils.java
deleted file mode 100644
index 2f2b8f7..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/WidgetTestUtils.java
+++ /dev/null
@@ -1,312 +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.
- */
-
-package com.android.compatibility.common.util;
-
-import static android.view.ViewTreeObserver.OnDrawListener;
-import static android.view.ViewTreeObserver.OnGlobalLayoutListener;
-
-import static org.mockito.hamcrest.MockitoHamcrest.argThat;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.support.test.rule.ActivityTestRule;
-import android.text.Editable;
-import android.text.TextUtils;
-import android.view.View;
-import android.view.ViewTreeObserver;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import junit.framework.Assert;
-
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * The useful methods for widget test.
- */
-public class WidgetTestUtils {
-    /**
-     * Assert that two bitmaps have identical content (same dimensions, same configuration,
-     * same pixel content).
-     *
-     * @param b1 the first bitmap which needs to compare.
-     * @param b2 the second bitmap which needs to compare.
-     */
-    public static void assertEquals(Bitmap b1, Bitmap b2) {
-        if (b1 == b2) {
-            return;
-        }
-
-        if (b1 == null || b2 == null) {
-            Assert.fail("the bitmaps are not equal");
-        }
-
-        // b1 and b2 are all not null.
-        if (b1.getWidth() != b2.getWidth() || b1.getHeight() != b2.getHeight()
-            || b1.getConfig() != b2.getConfig()) {
-            Assert.fail("the bitmaps are not equal");
-        }
-
-        int w = b1.getWidth();
-        int h = b1.getHeight();
-        int s = w * h;
-        int[] pixels1 = new int[s];
-        int[] pixels2 = new int[s];
-
-        b1.getPixels(pixels1, 0, w, 0, 0, w, h);
-        b2.getPixels(pixels2, 0, w, 0, 0, w, h);
-
-        for (int i = 0; i < s; i++) {
-            if (pixels1[i] != pixels2[i]) {
-                Assert.fail("the bitmaps are not equal");
-            }
-        }
-    }
-
-    /**
-     * Find beginning of the special element.
-     * @param parser XmlPullParser will be parsed.
-     * @param firstElementName the target element name.
-     *
-     * @throws XmlPullParserException if XML Pull Parser related faults occur.
-     * @throws IOException if I/O-related error occur when parsing.
-     */
-    public static final void beginDocument(XmlPullParser parser, String firstElementName)
-            throws XmlPullParserException, IOException {
-        Assert.assertNotNull(parser);
-        Assert.assertNotNull(firstElementName);
-
-        int type;
-        while ((type = parser.next()) != XmlPullParser.START_TAG
-                && type != XmlPullParser.END_DOCUMENT) {
-            ;
-        }
-
-        if (!parser.getName().equals(firstElementName)) {
-            throw new XmlPullParserException("Unexpected start tag: found " + parser.getName()
-                    + ", expected " + firstElementName);
-        }
-    }
-
-    /**
-     * Compare the expected pixels with actual, scaling for the target context density
-     *
-     * @throws AssertionFailedError
-     */
-    public static void assertScaledPixels(int expected, int actual, Context context) {
-        Assert.assertEquals(expected * context.getResources().getDisplayMetrics().density,
-                actual, 3);
-    }
-
-    /** Converts dips into pixels using the {@link Context}'s density. */
-    public static int convertDipToPixels(Context context, int dip) {
-      float density = context.getResources().getDisplayMetrics().density;
-      return Math.round(density * dip);
-    }
-
-    /**
-     * Retrieve a bitmap that can be used for comparison on any density
-     * @param resources
-     * @return the {@link Bitmap} or <code>null</code>
-     */
-    public static Bitmap getUnscaledBitmap(Resources resources, int resId) {
-        BitmapFactory.Options options = new BitmapFactory.Options();
-        options.inScaled = false;
-        return BitmapFactory.decodeResource(resources, resId, options);
-    }
-
-    /**
-     * Retrieve a dithered bitmap that can be used for comparison on any density
-     * @param resources
-     * @param config the preferred config for the returning bitmap
-     * @return the {@link Bitmap} or <code>null</code>
-     */
-    public static Bitmap getUnscaledAndDitheredBitmap(Resources resources,
-            int resId, Bitmap.Config config) {
-        BitmapFactory.Options options = new BitmapFactory.Options();
-        options.inDither = true;
-        options.inScaled = false;
-        options.inPreferredConfig = config;
-        return BitmapFactory.decodeResource(resources, resId, options);
-    }
-
-    /**
-     * Argument matcher for equality check of a CharSequence.
-     *
-     * @param expected expected CharSequence
-     *
-     * @return
-     */
-    public static CharSequence sameCharSequence(final CharSequence expected) {
-        return argThat(new BaseMatcher<CharSequence>() {
-            @Override
-            public boolean matches(Object o) {
-                if (o instanceof CharSequence) {
-                    return TextUtils.equals(expected, (CharSequence) o);
-                }
-                return false;
-            }
-
-            @Override
-            public void describeTo(Description description) {
-                description.appendText("doesn't match " + expected);
-            }
-        });
-    }
-
-    /**
-     * Argument matcher for equality check of an Editable.
-     *
-     * @param expected expected Editable
-     *
-     * @return
-     */
-    public static Editable sameEditable(final Editable expected) {
-        return argThat(new BaseMatcher<Editable>() {
-            @Override
-            public boolean matches(Object o) {
-                if (o instanceof Editable) {
-                    return TextUtils.equals(expected, (Editable) o);
-                }
-                return false;
-            }
-
-            @Override
-            public void describeTo(Description description) {
-                description.appendText("doesn't match " + expected);
-            }
-        });
-    }
-
-    /**
-     * Runs the specified {@link Runnable} on the main thread and ensures that the specified
-     * {@link View}'s tree is drawn before returning.
-     *
-     * @param activityTestRule the activity test rule used to run the test
-     * @param view the view whose tree should be drawn before returning
-     * @param runner the runnable to run on the main thread, or {@code null} to
-     *               simply force invalidation and a draw pass
-     */
-    public static void runOnMainAndDrawSync(@NonNull final ActivityTestRule activityTestRule,
-            @NonNull final View view, @Nullable final Runnable runner) {
-        final CountDownLatch latch = new CountDownLatch(1);
-
-        try {
-            activityTestRule.runOnUiThread(() -> {
-                final OnDrawListener listener = new OnDrawListener() {
-                    @Override
-                    public void onDraw() {
-                        // posting so that the sync happens after the draw that's about to happen
-                        view.post(() -> {
-                            activityTestRule.getActivity().getWindow().getDecorView()
-                                    .getViewTreeObserver().removeOnDrawListener(this);
-                            latch.countDown();
-                        });
-                    }
-                };
-
-                activityTestRule.getActivity().getWindow().getDecorView()
-                        .getViewTreeObserver().addOnDrawListener(listener);
-
-                if (runner != null) {
-                    runner.run();
-                }
-                view.invalidate();
-            });
-
-            Assert.assertTrue("Expected draw pass occurred within 5 seconds",
-                    latch.await(5, TimeUnit.SECONDS));
-        } catch (Throwable t) {
-            throw new RuntimeException(t);
-        }
-    }
-
-    /**
-     * Runs the specified Runnable on the main thread and ensures that the activity's view tree is
-     * laid out before returning.
-     *
-     * @param activityTestRule the activity test rule used to run the test
-     * @param runner the runnable to run on the main thread. {@code null} is
-     * allowed, and simply means that there no runnable is required.
-     * @param forceLayout true if there should be an explicit call to requestLayout(),
-     * false otherwise
-     */
-    public static void runOnMainAndLayoutSync(@NonNull final ActivityTestRule activityTestRule,
-            @Nullable final Runnable runner, boolean forceLayout)
-            throws Throwable {
-        runOnMainAndLayoutSync(activityTestRule,
-                activityTestRule.getActivity().getWindow().getDecorView(), runner, forceLayout);
-    }
-
-    /**
-     * Runs the specified Runnable on the main thread and ensures that the specified view is
-     * laid out before returning.
-     *
-     * @param activityTestRule the activity test rule used to run the test
-     * @param view The view
-     * @param runner the runnable to run on the main thread. {@code null} is
-     * allowed, and simply means that there no runnable is required.
-     * @param forceLayout true if there should be an explicit call to requestLayout(),
-     * false otherwise
-     */
-    public static void runOnMainAndLayoutSync(@NonNull final ActivityTestRule activityTestRule,
-            @NonNull final View view, @Nullable final Runnable runner, boolean forceLayout)
-            throws Throwable {
-        final View rootView = view.getRootView();
-
-        final CountDownLatch latch = new CountDownLatch(1);
-
-        activityTestRule.runOnUiThread(() -> {
-            final OnGlobalLayoutListener listener = new ViewTreeObserver.OnGlobalLayoutListener() {
-                @Override
-                public void onGlobalLayout() {
-                    rootView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
-                    // countdown immediately since the layout we were waiting on has happened
-                    latch.countDown();
-                }
-            };
-
-            rootView.getViewTreeObserver().addOnGlobalLayoutListener(listener);
-
-            if (runner != null) {
-                runner.run();
-            }
-
-            if (forceLayout) {
-                rootView.requestLayout();
-            }
-        });
-
-        try {
-            Assert.assertTrue("Expected layout pass within 5 seconds",
-                    latch.await(5, TimeUnit.SECONDS));
-        } catch (InterruptedException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/WifiConfigCreator.java b/common/device-side/util/src/com/android/compatibility/common/util/WifiConfigCreator.java
deleted file mode 100755
index f2d1f65..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/WifiConfigCreator.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.compatibility.common.util;
-
-import static android.net.wifi.WifiManager.EXTRA_WIFI_STATE;
-import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.ProxyInfo;
-import android.net.Uri;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiManager;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * A simple activity to create and manage wifi configurations.
- */
-public class WifiConfigCreator {
-    public static final String ACTION_CREATE_WIFI_CONFIG =
-            "com.android.compatibility.common.util.CREATE_WIFI_CONFIG";
-    public static final String ACTION_UPDATE_WIFI_CONFIG =
-            "com.android.compatibility.common.util.UPDATE_WIFI_CONFIG";
-    public static final String ACTION_REMOVE_WIFI_CONFIG =
-            "com.android.compatibility.common.util.REMOVE_WIFI_CONFIG";
-    public static final String EXTRA_NETID = "extra-netid";
-    public static final String EXTRA_SSID = "extra-ssid";
-    public static final String EXTRA_SECURITY_TYPE = "extra-security-type";
-    public static final String EXTRA_PASSWORD = "extra-password";
-
-    public static final int SECURITY_TYPE_NONE = 1;
-    public static final int SECURITY_TYPE_WPA = 2;
-    public static final int SECURITY_TYPE_WEP = 3;
-
-    private static final String TAG = "WifiConfigCreator";
-
-    private static final long ENABLE_WIFI_WAIT_SEC = 10L;
-
-    private final Context mContext;
-    private final WifiManager mWifiManager;
-
-    public WifiConfigCreator(Context context) {
-        mContext = context;
-        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
-    }
-
-    /**
-     * Adds a new WiFi network.
-     * @return network id or -1 in case of error
-     */
-    public int addNetwork(String ssid, boolean hidden, int securityType,
-            String password) throws InterruptedException, SecurityException {
-        checkAndEnableWifi();
-
-        WifiConfiguration wifiConf = createConfig(ssid, hidden, securityType, password);
-
-        int netId = mWifiManager.addNetwork(wifiConf);
-
-        if (netId != -1) {
-            mWifiManager.enableNetwork(netId, true);
-        } else {
-            Log.w(TAG, "Unable to add SSID '" + ssid + "': netId = " + netId);
-        }
-        return netId;
-    }
-
-    /**
-     * Adds a new wifiConfiguration with OPEN security type, and the given pacProxy
-     * verifies that the proxy is added by getting the configuration back, and checking it.
-     * @return returns the PAC proxy URL after adding the network and getting it from WifiManager
-     * @throws IllegalStateException if any of the WifiManager operations fail
-     */
-    public String addHttpProxyNetworkVerifyAndRemove(String ssid, String pacProxyUrl)
-            throws IllegalStateException {
-        String retrievedPacProxyUrl = null;
-        int netId = -1;
-        try {
-            WifiConfiguration conf = createConfig(ssid, false, SECURITY_TYPE_NONE, null);
-            if (pacProxyUrl != null) {
-                conf.setHttpProxy(ProxyInfo.buildPacProxy(Uri.parse(pacProxyUrl)));
-            }
-            netId = mWifiManager.addNetwork(conf);
-            if (netId == -1) {
-                throw new IllegalStateException("Failed to addNetwork: " + ssid);
-            }
-            for (final WifiConfiguration w : mWifiManager.getConfiguredNetworks()) {
-                if (w.SSID.equals(ssid)) {
-                    conf = w;
-                    break;
-                }
-            }
-            if (conf == null) {
-                throw new IllegalStateException("Failed to get WifiConfiguration for: " + ssid);
-            }
-            Uri pacProxyFileUri = null;
-            ProxyInfo httpProxy = conf.getHttpProxy();
-            if (httpProxy != null) pacProxyFileUri = httpProxy.getPacFileUrl();
-            if (pacProxyFileUri != null) {
-                retrievedPacProxyUrl = conf.getHttpProxy().getPacFileUrl().toString();
-            }
-            if (!mWifiManager.removeNetwork(netId)) {
-                throw new IllegalStateException("Failed to remove WifiConfiguration: " + ssid);
-            }
-        } finally {
-            mWifiManager.removeNetwork(netId);
-        }
-        return retrievedPacProxyUrl;
-    }
-
-    /**
-     * Updates a new WiFi network.
-     * @return network id (may differ from original) or -1 in case of error
-     */
-    public int updateNetwork(WifiConfiguration wifiConf, String ssid, boolean hidden,
-            int securityType, String password) throws InterruptedException, SecurityException {
-        checkAndEnableWifi();
-        if (wifiConf == null) {
-            return -1;
-        }
-
-        WifiConfiguration conf = createConfig(ssid, hidden, securityType, password);
-        conf.networkId = wifiConf.networkId;
-
-        int newNetId = mWifiManager.updateNetwork(conf);
-
-        if (newNetId != -1) {
-            mWifiManager.saveConfiguration();
-            mWifiManager.enableNetwork(newNetId, true);
-        } else {
-            Log.w(TAG, "Unable to update SSID '" + ssid + "': netId = " + newNetId);
-        }
-        return newNetId;
-    }
-
-    /**
-     * Updates a new WiFi network.
-     * @return network id (may differ from original) or -1 in case of error
-     */
-    public int updateNetwork(int netId, String ssid, boolean hidden,
-            int securityType, String password) throws InterruptedException, SecurityException {
-        checkAndEnableWifi();
-
-        WifiConfiguration wifiConf = null;
-        List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
-        for (WifiConfiguration config : configs) {
-            if (config.networkId == netId) {
-                wifiConf = config;
-                break;
-            }
-        }
-        return updateNetwork(wifiConf, ssid, hidden, securityType, password);
-    }
-
-    public boolean removeNetwork(int netId) {
-        return mWifiManager.removeNetwork(netId);
-    }
-
-    /**
-     * Creates a WifiConfiguration set up according to given parameters
-     * @param ssid SSID of the network
-     * @param hidden Is SSID not broadcast?
-     * @param securityType One of {@link #SECURITY_TYPE_NONE}, {@link #SECURITY_TYPE_WPA} or
-     *                     {@link #SECURITY_TYPE_WEP}
-     * @param password Password for WPA or WEP
-     * @return Created configuration object
-     */
-    private WifiConfiguration createConfig(String ssid, boolean hidden, int securityType,
-            String password) {
-        WifiConfiguration wifiConf = new WifiConfiguration();
-        if (!TextUtils.isEmpty(ssid)) {
-            wifiConf.SSID = '"' + ssid + '"';
-        }
-        wifiConf.status = WifiConfiguration.Status.ENABLED;
-        wifiConf.hiddenSSID = hidden;
-        switch (securityType) {
-            case SECURITY_TYPE_NONE:
-                wifiConf.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
-                break;
-            case SECURITY_TYPE_WPA:
-                updateForWPAConfiguration(wifiConf, password);
-                break;
-            case SECURITY_TYPE_WEP:
-                updateForWEPConfiguration(wifiConf, password);
-                break;
-        }
-        return wifiConf;
-    }
-
-    private void updateForWPAConfiguration(WifiConfiguration wifiConf, String wifiPassword) {
-        wifiConf.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
-        if (!TextUtils.isEmpty(wifiPassword)) {
-            wifiConf.preSharedKey = '"' + wifiPassword + '"';
-        }
-    }
-
-    private void updateForWEPConfiguration(WifiConfiguration wifiConf, String password) {
-        wifiConf.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
-        wifiConf.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
-        wifiConf.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
-        if (!TextUtils.isEmpty(password)) {
-            int length = password.length();
-            if ((length == 10 || length == 26
-                    || length == 58) && password.matches("[0-9A-Fa-f]*")) {
-                wifiConf.wepKeys[0] = password;
-            } else {
-                wifiConf.wepKeys[0] = '"' + password + '"';
-            }
-            wifiConf.wepTxKeyIndex = 0;
-        }
-    }
-
-    private void checkAndEnableWifi() throws InterruptedException {
-        final CountDownLatch enabledLatch = new CountDownLatch(1);
-
-        // Register a change receiver first to pick up events between isEnabled and setEnabled
-        final BroadcastReceiver watcher = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                if (intent.getIntExtra(EXTRA_WIFI_STATE, -1) == WIFI_STATE_ENABLED) {
-                    enabledLatch.countDown();
-                }
-            }
-        };
-
-        mContext.registerReceiver(watcher, new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION));
-        try {
-            // In case wifi is not already enabled, wait for it to come up
-            if (!mWifiManager.isWifiEnabled()) {
-                SystemUtil.runShellCommand("svc wifi enable");
-                enabledLatch.await(ENABLE_WIFI_WAIT_SEC, TimeUnit.SECONDS);
-            }
-        } finally {
-            mContext.unregisterReceiver(watcher);
-        }
-    }
-}
-
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/Within.java b/common/device-side/util/src/com/android/compatibility/common/util/Within.java
deleted file mode 100644
index 4d9ff80..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/Within.java
+++ /dev/null
@@ -1,91 +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.compatibility.common.util;
-
-import android.os.SystemClock;
-
-import org.mockito.Mockito;
-import org.mockito.exceptions.base.MockitoAssertionError;
-import org.mockito.internal.verification.api.VerificationData;
-import org.mockito.invocation.Invocation;
-import org.mockito.verification.VerificationMode;
-
-import java.util.List;
-
-/**
- * Custom verification mode that allows waiting for the specific invocation to happen within
- * a certain time interval. Not that unlike {@link Mockito#timeout(int)}, this mode will not
- * return early and throw exception if the expected method was called with a different set of
- * parameters before the call that we're waiting for.
- */
-public class Within implements VerificationMode {
-    private static final long TIME_SLICE = 50;
-    private final long mTimeout;
-
-    public Within(long timeout) {
-        mTimeout = timeout;
-    }
-
-    @Override
-    public void verify(VerificationData data) {
-        long timeout = mTimeout;
-        MockitoAssertionError errorToRethrow = null;
-        // Loop in the same way we do in PollingCheck, sleeping and then testing for the target
-        // invocation
-        while (timeout > 0) {
-            SystemClock.sleep(TIME_SLICE);
-
-            try {
-                final List<Invocation> actualInvocations = data.getAllInvocations();
-                // Iterate over all invocations so far to see if we have a match
-                for (Invocation invocation : actualInvocations) {
-                    if (data.getWanted().matches(invocation)) {
-                        // Found our match within our timeout. Mark all invocations as verified
-                        markAllInvocationsAsVerified(data);
-                        // and return
-                        return;
-                    }
-                }
-            } catch (MockitoAssertionError assertionError) {
-                errorToRethrow = assertionError;
-            }
-
-            timeout -= TIME_SLICE;
-        }
-
-        if (errorToRethrow != null) {
-            throw errorToRethrow;
-        }
-
-        throw new MockitoAssertionError(
-                "Timed out while waiting " + mTimeout + "ms for " + data.getWanted().toString());
-    }
-
-    // TODO: Uncomment once upgraded to 2.7.13
-    // @Override
-    public VerificationMode description(String description) {
-        // Return this for now.
-        // TODO: Return wrapper once upgraded to 2.7.13
-        return this;
-    }
-
-    private void markAllInvocationsAsVerified(VerificationData data) {
-        for (Invocation invocation : data.getAllInvocations()) {
-            invocation.markVerified();
-            data.getWanted().captureArgumentsFrom(invocation);
-        }
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/IBooleanCallback.aidl b/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/IBooleanCallback.aidl
deleted file mode 100644
index 2fdb26b..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/IBooleanCallback.aidl
+++ /dev/null
@@ -1,20 +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.compatibility.common.util.devicepolicy.provisioning;
-
-interface IBooleanCallback {
-    oneway void onResult(boolean result);
-}
\ No newline at end of file
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/SilentProvisioningTestManager.java b/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/SilentProvisioningTestManager.java
deleted file mode 100644
index 05edf1a..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/SilentProvisioningTestManager.java
+++ /dev/null
@@ -1,179 +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.compatibility.common.util.devicepolicy.provisioning;
-
-import static android.app.admin.DevicePolicyManager.ACTION_MANAGED_PROFILE_PROVISIONED;
-import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
-import static android.content.Intent.ACTION_MANAGED_PROFILE_ADDED;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.uiautomator.UiDevice;
-import android.util.Log;
-
-import com.android.compatibility.common.util.BlockingBroadcastReceiver;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-
-public class SilentProvisioningTestManager {
-    private static final long TIMEOUT_SECONDS = 120L;
-    private static final String TAG = "SilentProvisioningTest";
-
-    private final LinkedBlockingQueue<Boolean> mProvisioningResults = new LinkedBlockingQueue(1);
-
-    private final IBooleanCallback mProvisioningResultCallback = new IBooleanCallback.Stub() {
-        @Override
-        public void onResult(boolean result) {
-            try {
-                mProvisioningResults.put(result);
-            } catch (InterruptedException e) {
-                Log.e(TAG, "IBooleanCallback.callback", e);
-            }
-        }
-    };
-
-    private final Context mContext;
-    private Intent mReceivedProfileProvisionedIntent;
-
-    public SilentProvisioningTestManager(Context context) {
-        mContext = context.getApplicationContext();
-    }
-
-    public Intent getReceviedProfileProvisionedIntent() {
-        return mReceivedProfileProvisionedIntent;
-    }
-
-    public boolean startProvisioningAndWait(Intent provisioningIntent) throws InterruptedException {
-        wakeUpAndDismissInsecureKeyguard();
-        mContext.startActivity(getStartIntent(provisioningIntent));
-        Log.i(TAG, "startActivity with intent: " + provisioningIntent);
-
-        if (ACTION_PROVISION_MANAGED_PROFILE.equals(provisioningIntent.getAction())) {
-            return waitManagedProfileProvisioning();
-        } else {
-            return waitDeviceOwnerProvisioning();
-        }
-    }
-
-    private boolean waitDeviceOwnerProvisioning() throws InterruptedException {
-        return pollProvisioningResult();
-    }
-
-    private boolean waitManagedProfileProvisioning() throws InterruptedException {
-        BlockingBroadcastReceiver managedProfileProvisionedReceiver =
-                new BlockingBroadcastReceiver(mContext, ACTION_MANAGED_PROFILE_PROVISIONED);
-        BlockingBroadcastReceiver managedProfileAddedReceiver =
-                new BlockingBroadcastReceiver(mContext, ACTION_MANAGED_PROFILE_ADDED);
-        try {
-            managedProfileProvisionedReceiver.register();
-            managedProfileAddedReceiver.register();
-
-            if (!pollProvisioningResult()) {
-                return false;
-            }
-
-            mReceivedProfileProvisionedIntent =
-                    managedProfileProvisionedReceiver.awaitForBroadcast();
-            if (mReceivedProfileProvisionedIntent == null) {
-                Log.i(TAG, "managedProfileProvisionedReceiver.awaitForBroadcast(): failed");
-                return false;
-            }
-
-            if (managedProfileAddedReceiver.awaitForBroadcast() == null) {
-                Log.i(TAG, "managedProfileAddedReceiver.awaitForBroadcast(): failed");
-                return false;
-            }
-        } finally {
-            managedProfileProvisionedReceiver.unregisterQuietly();
-            managedProfileAddedReceiver.unregisterQuietly();
-        }
-        return true;
-    }
-
-    private boolean pollProvisioningResult() throws InterruptedException {
-        Boolean result = mProvisioningResults.poll(TIMEOUT_SECONDS, TimeUnit.SECONDS);
-        if (result == null) {
-            Log.i(TAG, "ManagedProvisioning doesn't return result within "
-                    + TIMEOUT_SECONDS + " seconds ");
-            return false;
-        }
-
-        if (!result) {
-            Log.i(TAG, "Failed to provision");
-            return false;
-        }
-        return true;
-    }
-
-    private Intent getStartIntent(Intent intent) {
-        final Bundle bundle = new Bundle();
-        bundle.putParcelable(Intent.EXTRA_INTENT, intent);
-        bundle.putBinder(StartProvisioningActivity.EXTRA_BOOLEAN_CALLBACK,
-                mProvisioningResultCallback.asBinder());
-        return new Intent(mContext, StartProvisioningActivity.class)
-                .putExtras(bundle)
-                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-    }
-
-    private static void wakeUpAndDismissInsecureKeyguard() {
-        try {
-            UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-            uiDevice.wakeUp();
-            uiDevice.pressMenu();
-        } catch (RemoteException e) {
-            Log.e(TAG, "wakeUpScreen", e);
-        }
-    }
-
-    private static class BlockingReceiver extends BroadcastReceiver {
-
-        private final CountDownLatch mLatch = new CountDownLatch(1);
-        private final Context mContext;
-        private final String mAction;
-        private Intent mReceivedIntent;
-
-        private BlockingReceiver(Context context, String action) {
-            mContext = context;
-            mAction = action;
-            mReceivedIntent = null;
-        }
-
-        public void register() {
-            mContext.registerReceiver(this, new IntentFilter(mAction));
-        }
-
-        public boolean await() throws InterruptedException {
-            return mLatch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS);
-        }
-
-        public Intent getReceivedIntent() {
-            return mReceivedIntent;
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            mReceivedIntent = intent;
-            mLatch.countDown();
-        }
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/StartProvisioningActivity.java b/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/StartProvisioningActivity.java
deleted file mode 100644
index 4a98794..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/StartProvisioningActivity.java
+++ /dev/null
@@ -1,75 +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.compatibility.common.util.devicepolicy.provisioning;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.WindowManager;
-
-/**
- * Must register it in AndroidManifest.xml
- * <activity android:name="com.android.compatibility.common.util.devicepolicy.provisioning.StartProvisioningActivity"></activity>
- */
-public class StartProvisioningActivity extends Activity {
-    private static final int REQUEST_CODE = 1;
-    private static final String TAG = "StartProvisionActivity";
-
-    public static final String EXTRA_BOOLEAN_CALLBACK = "EXTRA_BOOLEAN_CALLBACK";
-
-    IBooleanCallback mResultCallback;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        // Reduce flakiness of the test
-        // Show activity on top of keyguard
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
-        // Turn on screen to prevent activity being paused by system
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
-
-        mResultCallback = IBooleanCallback.Stub.asInterface(
-                getIntent().getExtras().getBinder(EXTRA_BOOLEAN_CALLBACK));
-        Log.i(TAG, "result callback class name " + mResultCallback);
-
-        // Only provision it if the activity is not re-created
-        if (savedInstanceState == null) {
-            Intent provisioningIntent = getIntent().getParcelableExtra(Intent.EXTRA_INTENT);
-
-            startActivityForResult(provisioningIntent, REQUEST_CODE);
-            Log.i(TAG, "Start provisioning intent");
-        }
-    }
-
-    @Override
-    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (requestCode == REQUEST_CODE) {
-            try {
-                boolean result = resultCode == RESULT_OK;
-                mResultCallback.onResult(result);
-                Log.i(TAG, "onActivityResult result: " + result);
-            } catch (RemoteException e) {
-                Log.e(TAG, "onActivityResult", e);
-            }
-        } else {
-            super.onActivityResult(requestCode, resultCode, data);
-        }
-    }
-}
\ No newline at end of file
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/transition/TargetTracking.java b/common/device-side/util/src/com/android/compatibility/common/util/transition/TargetTracking.java
deleted file mode 100644
index 7c53921..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/transition/TargetTracking.java
+++ /dev/null
@@ -1,27 +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.compatibility.common.util.transition;
-
-import android.graphics.Rect;
-import android.view.View;
-
-import java.util.ArrayList;
-
-public interface TargetTracking {
-    ArrayList<View> getTrackedTargets();
-    void clearTargets();
-    Rect getCapturedEpicenter();
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/transition/TrackingTransition.java b/common/device-side/util/src/com/android/compatibility/common/util/transition/TrackingTransition.java
deleted file mode 100644
index 55b235d..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/transition/TrackingTransition.java
+++ /dev/null
@@ -1,91 +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.compatibility.common.util.transition;
-
-import android.animation.Animator;
-import android.graphics.Rect;
-import android.transition.Transition;
-import android.transition.TransitionValues;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-
-/**
- * A transition that tracks which targets are applied to it.
- * It will assume any target that it applies to will have differences
- * between the start and end state, regardless of the differences
- * that actually exist. In other words, it doesn't actually check
- * any size or position differences or any other property of the view.
- * It just records the difference.
- * <p>
- * Both start and end value Views are recorded, but no actual animation
- * is created.
- */
-public class TrackingTransition extends Transition implements TargetTracking {
-    public final ArrayList<View> targets = new ArrayList<>();
-    private final Rect[] mEpicenter = new Rect[1];
-    private static String PROP = "tracking:prop";
-    private static String[] PROPS = { PROP };
-
-    @Override
-    public String[] getTransitionProperties() {
-        return PROPS;
-    }
-
-    @Override
-    public void captureStartValues(TransitionValues transitionValues) {
-        transitionValues.values.put(PROP, 0);
-    }
-
-    @Override
-    public void captureEndValues(TransitionValues transitionValues) {
-        transitionValues.values.put(PROP, 1);
-    }
-
-    @Override
-    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
-            TransitionValues endValues) {
-        if (startValues != null) {
-            targets.add(startValues.view);
-        }
-        if (endValues != null) {
-            targets.add(endValues.view);
-        }
-        Rect epicenter = getEpicenter();
-        if (epicenter != null) {
-            mEpicenter[0] = new Rect(epicenter);
-        } else {
-            mEpicenter[0] = null;
-        }
-        return null;
-    }
-
-    @Override
-    public ArrayList<View> getTrackedTargets() {
-        return targets;
-    }
-
-    @Override
-    public void clearTargets() {
-        targets.clear();
-    }
-
-    @Override
-    public Rect getCapturedEpicenter() {
-        return mEpicenter[0];
-    }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/transition/TrackingVisibility.java b/common/device-side/util/src/com/android/compatibility/common/util/transition/TrackingVisibility.java
deleted file mode 100644
index 8a5a19e..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/transition/TrackingVisibility.java
+++ /dev/null
@@ -1,75 +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.compatibility.common.util.transition;
-
-import android.animation.Animator;
-import android.graphics.Rect;
-import android.transition.TransitionValues;
-import android.transition.Visibility;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-
-/**
- * Visibility transition that tracks which targets are applied to it.
- * This transition does no animation.
- */
-public class TrackingVisibility extends Visibility implements TargetTracking {
-    public final ArrayList<View> targets = new ArrayList<>();
-    private final Rect[] mEpicenter = new Rect[1];
-
-    @Override
-    public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
-            TransitionValues endValues) {
-        targets.add(endValues.view);
-        Rect epicenter = getEpicenter();
-        if (epicenter != null) {
-            mEpicenter[0] = new Rect(epicenter);
-        } else {
-            mEpicenter[0] = null;
-        }
-        return null;
-    }
-
-    @Override
-    public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues,
-            TransitionValues endValues) {
-        targets.add(startValues.view);
-        Rect epicenter = getEpicenter();
-        if (epicenter != null) {
-            mEpicenter[0] = new Rect(epicenter);
-        } else {
-            mEpicenter[0] = null;
-        }
-        return null;
-    }
-
-    @Override
-    public ArrayList<View> getTrackedTargets() {
-        return targets;
-    }
-
-    @Override
-    public void clearTargets() {
-        targets.clear();
-    }
-
-    @Override
-    public Rect getCapturedEpicenter() {
-        return mEpicenter[0];
-    }
-}
diff --git a/common/device-side/util/tests/src/com/android/compatibility/common/util/ApiLevelUtilTest.java b/common/device-side/util/tests/src/com/android/compatibility/common/util/ApiLevelUtilTest.java
deleted file mode 100644
index 13305c3..0000000
--- a/common/device-side/util/tests/src/com/android/compatibility/common/util/ApiLevelUtilTest.java
+++ /dev/null
@@ -1,77 +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.compatibility.common.util;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import android.os.Build;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for {@line ApiLevelUtil}.
- */
-@RunWith(AndroidJUnit4.class)
-public class ApiLevelUtilTest {
-
-    @Test
-    public void testComparisonByInt() throws Exception {
-        int version = Build.VERSION.SDK_INT;
-
-        assertFalse(ApiLevelUtil.isBefore(version - 1));
-        assertFalse(ApiLevelUtil.isBefore(version));
-        assertTrue(ApiLevelUtil.isBefore(version + 1));
-
-        assertTrue(ApiLevelUtil.isAfter(version - 1));
-        assertFalse(ApiLevelUtil.isAfter(version));
-        assertFalse(ApiLevelUtil.isAfter(version + 1));
-
-        assertTrue(ApiLevelUtil.isAtLeast(version - 1));
-        assertTrue(ApiLevelUtil.isAtLeast(version));
-        assertFalse(ApiLevelUtil.isAtLeast(version + 1));
-
-        assertFalse(ApiLevelUtil.isAtMost(version - 1));
-        assertTrue(ApiLevelUtil.isAtMost(version));
-        assertTrue(ApiLevelUtil.isAtMost(version + 1));
-    }
-
-    @Test
-    public void testComparisonByString() throws Exception {
-        // test should pass as long as device SDK version is at least 12
-        assertTrue(ApiLevelUtil.isAtLeast("HONEYCOMB_MR1"));
-        assertTrue(ApiLevelUtil.isAtLeast("12"));
-    }
-
-    @Test
-    public void testResolveVersionString() throws Exception {
-        // can only test versions known to the device build
-        assertEquals(ApiLevelUtil.resolveVersionString("GINGERBREAD_MR1"), 10);
-        assertEquals(ApiLevelUtil.resolveVersionString("10"), 10);
-        assertEquals(ApiLevelUtil.resolveVersionString("HONEYCOMB"), 11);
-        assertEquals(ApiLevelUtil.resolveVersionString("11"), 11);
-        assertEquals(ApiLevelUtil.resolveVersionString("honeycomb_mr1"), 12);
-        assertEquals(ApiLevelUtil.resolveVersionString("12"), 12);
-    }
-
-    @Test(expected = RuntimeException.class)
-    public void testResolveMisspelledVersionString() throws Exception {
-        ApiLevelUtil.resolveVersionString("GINGERBEARD");
-    }
-}
diff --git a/common/device-side/util/tests/src/com/android/compatibility/common/util/BusinessLogicDeviceExecutorTest.java b/common/device-side/util/tests/src/com/android/compatibility/common/util/BusinessLogicDeviceExecutorTest.java
deleted file mode 100644
index 9f3ca47..0000000
--- a/common/device-side/util/tests/src/com/android/compatibility/common/util/BusinessLogicDeviceExecutorTest.java
+++ /dev/null
@@ -1,314 +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.compatibility.common.util;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.fail;
-import static org.junit.Assume.assumeTrue;
-
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.AssumptionViolatedException;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import junit.framework.AssertionFailedError;
-
-/**
- * Tests for {@line BusinessLogicDeviceExecutor}.
- */
-@RunWith(AndroidJUnit4.class)
-public class BusinessLogicDeviceExecutorTest {
-
-    private static final String THIS_CLASS =
-            "com.android.compatibility.common.util.BusinessLogicDeviceExecutorTest";
-    private static final String METHOD_1 = THIS_CLASS + ".method1";
-    private static final String METHOD_2 = THIS_CLASS + ".method2";
-    private static final String METHOD_3 = THIS_CLASS + ".method3";
-    private static final String METHOD_4 = THIS_CLASS + ".method4";
-    private static final String METHOD_5 = THIS_CLASS + ".method5";
-    private static final String METHOD_6 = THIS_CLASS + ".method6";
-    private static final String METHOD_7 = THIS_CLASS + ".method7";
-    private static final String METHOD_8 = THIS_CLASS + ".method8";
-    private static final String METHOD_9 = THIS_CLASS + ".method9";
-    private static final String METHOD_10 = THIS_CLASS + ".method10";
-    private static final String FAKE_METHOD = THIS_CLASS + ".methodDoesntExist";
-    private static final String ARG_STRING_1 = "arg1";
-    private static final String ARG_STRING_2 = "arg2";
-
-    private static final String OTHER_METHOD_1 = THIS_CLASS + "$OtherClass.method1";
-
-    private String mInvoked = null;
-    private Object[] mArgsUsed = null;
-    private Context mContext;
-    private BusinessLogicExecutor mExecutor;
-
-    @Before
-    public void setUp() {
-        mContext = InstrumentationRegistry.getTargetContext();
-        mExecutor = new BusinessLogicDeviceExecutor(mContext, this);
-        // reset the instance variables tracking the method invoked and the args used
-        mInvoked = null;
-        mArgsUsed = null;
-        // reset the OtherClass class variable tracking the method invoked
-        OtherClass.otherInvoked = null;
-    }
-
-    @Test
-    public void testInvokeMethodInThisClass() throws Exception {
-        mExecutor.invokeMethod(METHOD_1);
-        // assert that mInvoked was set for this BusinessLogicDeviceExecutorTest instance
-        assertEquals("Failed to invoke method in this class", mInvoked, METHOD_1);
-    }
-
-    @Test
-    public void testInvokeMethodInOtherClass() throws Exception {
-        mExecutor.invokeMethod(OTHER_METHOD_1);
-        // assert that OtherClass.method1 was invoked, and static field of OtherClass was changed
-        assertEquals("Failed to invoke method in other class", OtherClass.otherInvoked,
-                OTHER_METHOD_1);
-    }
-
-    @Test
-    public void testInvokeMethodWithStringArgs() throws Exception {
-        mExecutor.invokeMethod(METHOD_2, ARG_STRING_1, ARG_STRING_2);
-        assertEquals("Failed to invoke method in this class", mInvoked, METHOD_2);
-        // assert both String arguments were correctly set for method2
-        assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
-        assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_2);
-    }
-
-    @Test
-    public void testInvokeMethodWithStringAndContextArgs() throws Exception {
-        mExecutor.invokeMethod(METHOD_3, ARG_STRING_1);
-        assertEquals("Failed to invoke method in this class", mInvoked, METHOD_3);
-        // assert that String arg and Context arg were correctly set for method3
-        assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
-        assertEquals("Failed to set second argument", mArgsUsed[1], mContext);
-    }
-
-    @Test
-    public void testInvokeMethodWithContextAndStringArgs() throws Exception {
-        mExecutor.invokeMethod(METHOD_4, ARG_STRING_1);
-        assertEquals("Failed to invoke method in this class", mInvoked, METHOD_4);
-        // Like testInvokeMethodWithStringAndContextArgs, but flip the args for method4
-        assertEquals("Failed to set first argument", mArgsUsed[0], mContext);
-        assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_1);
-    }
-
-    @Test
-    public void testInvokeMethodWithStringArrayArg() throws Exception {
-        mExecutor.invokeMethod(METHOD_5, ARG_STRING_1, ARG_STRING_2);
-        assertEquals("Failed to invoke method in this class", mInvoked, METHOD_5);
-        // assert both String arguments were correctly set for method5
-        assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
-        assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_2);
-    }
-
-    @Test
-    public void testInvokeMethodWithEmptyStringArrayArg() throws Exception {
-        mExecutor.invokeMethod(METHOD_5);
-        assertEquals("Failed to invoke method in this class", mInvoked, METHOD_5);
-        // assert no String arguments were set for method5
-        assertEquals("Incorrectly set args", mArgsUsed.length, 0);
-    }
-
-    @Test
-    public void testInvokeMethodWithStringAndStringArrayArgs() throws Exception {
-        mExecutor.invokeMethod(METHOD_6, ARG_STRING_1, ARG_STRING_2);
-        assertEquals("Failed to invoke method in this class", mInvoked, METHOD_6);
-        // assert both String arguments were correctly set for method6
-        assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
-        assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_2);
-    }
-
-    @Test
-    public void testInvokeMethodWithAllArgTypes() throws Exception {
-        mExecutor.invokeMethod(METHOD_7, ARG_STRING_1, ARG_STRING_2);
-        assertEquals("Failed to invoke method in this class", mInvoked, METHOD_7);
-        // assert all arguments were correctly set for method7
-        assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
-        assertEquals("Failed to set second argument", mArgsUsed[1], mContext);
-        assertEquals("Failed to set third argument", mArgsUsed[2], ARG_STRING_2);
-    }
-
-    @Test
-    public void testInvokeOverloadedMethodOneArg() throws Exception {
-        mExecutor.invokeMethod(METHOD_1, ARG_STRING_1);
-        assertEquals("Failed to invoke method in this class", mInvoked, METHOD_1);
-        assertEquals("Set wrong number of arguments", mArgsUsed.length, 1);
-        assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
-    }
-
-    @Test
-    public void testInvokeOverloadedMethodTwoArgs() throws Exception {
-        mExecutor.invokeMethod(METHOD_1, ARG_STRING_1, ARG_STRING_2);
-        assertEquals("Failed to invoke method in this class", mInvoked, METHOD_1);
-        assertEquals("Set wrong number of arguments", mArgsUsed.length, 2);
-        assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
-        assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_2);
-    }
-
-    @Test(expected = RuntimeException.class)
-    public void testInvokeNonExistentMethod() throws Exception {
-        mExecutor.invokeMethod(FAKE_METHOD, ARG_STRING_1, ARG_STRING_2);
-    }
-
-    @Test(expected = RuntimeException.class)
-    public void testInvokeMethodTooManyArgs() throws Exception {
-        mExecutor.invokeMethod(METHOD_3, ARG_STRING_1, ARG_STRING_2);
-    }
-
-    @Test(expected = RuntimeException.class)
-    public void testInvokeMethodTooFewArgs() throws Exception {
-        mExecutor.invokeMethod(METHOD_2, ARG_STRING_1);
-    }
-
-    @Test(expected = RuntimeException.class)
-    public void testInvokeMethodIncompatibleArgs() throws Exception {
-        mExecutor.invokeMethod(METHOD_8, ARG_STRING_1);
-    }
-
-    @Test
-    public void testExecuteConditionCheckReturnValue() throws Exception {
-        assertTrue("Wrong return value",
-                mExecutor.executeCondition(METHOD_2, ARG_STRING_1, ARG_STRING_1));
-        assertFalse("Wrong return value",
-                mExecutor.executeCondition(METHOD_2, ARG_STRING_1, ARG_STRING_2));
-    }
-
-    @Test(expected = RuntimeException.class)
-    public void testExecuteInvalidCondition() throws Exception {
-        mExecutor.executeCondition(METHOD_1); // method1 does not return type boolean
-    }
-
-    @Test
-    public void testExecuteAction() throws Exception {
-        mExecutor.executeAction(METHOD_2, ARG_STRING_1, ARG_STRING_2);
-        assertEquals("Failed to invoke method in this class", mInvoked, METHOD_2);
-        // assert both String arguments were correctly set for method2
-        assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
-        assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_2);
-    }
-
-    @Test(expected = RuntimeException.class)
-    public void testExecuteActionThrowException() throws Exception {
-        mExecutor.executeAction(METHOD_9);
-    }
-
-    @Test
-    public void testExecuteActionViolateAssumption() throws Exception {
-        try {
-            mExecutor.executeAction(METHOD_10);
-            // JUnit4 doesn't support expecting AssumptionViolatedException with "expected"
-            // attribute on @Test annotation, so test using Assert.fail()
-            fail("Expected assumption failure");
-        } catch (AssumptionViolatedException e) {
-            // expected
-        }
-    }
-
-    public void method1() {
-        mInvoked = METHOD_1;
-    }
-
-    // overloaded method with one arg
-    public void method1(String arg1) {
-        mInvoked = METHOD_1;
-        mArgsUsed = new Object[]{arg1};
-    }
-
-    // overloaded method with two args
-    public void method1(String arg1, String arg2) {
-        mInvoked = METHOD_1;
-        mArgsUsed = new Object[]{arg1, arg2};
-    }
-
-    public boolean method2(String arg1, String arg2) {
-        mInvoked = METHOD_2;
-        mArgsUsed = new Object[]{arg1, arg2};
-        return arg1.equals(arg2);
-    }
-
-    public void method3(String arg1, Context arg2) {
-        mInvoked = METHOD_3;
-        mArgsUsed = new Object[]{arg1, arg2};
-    }
-
-    // Same as method3, but flipped args
-    public void method4(Context arg1, String arg2) {
-        mInvoked = METHOD_4;
-        mArgsUsed = new Object[]{arg1, arg2};
-    }
-
-    public void method5(String... args) {
-        mInvoked = METHOD_5;
-        mArgsUsed = args;
-    }
-
-    public void method6(String arg1, String... moreArgs) {
-        mInvoked = METHOD_6;
-        List<String> allArgs = new ArrayList<>();
-        allArgs.add(arg1);
-        allArgs.addAll(Arrays.asList(moreArgs));
-        mArgsUsed = allArgs.toArray(new String[0]);
-    }
-
-    public void method7(String arg1, Context arg2, String... moreArgs) {
-        mInvoked = METHOD_7;
-        List<Object> allArgs = new ArrayList<>();
-        allArgs.add(arg1);
-        allArgs.add(arg2);
-        allArgs.addAll(Arrays.asList(moreArgs));
-        mArgsUsed = allArgs.toArray(new Object[0]);
-    }
-
-    public void method8(String arg1, Integer arg2) {
-        // This method should never be successfully invoked, since Integer parameter types are
-        // unsupported for the BusinessLogic service
-    }
-
-    // throw AssertionFailedError
-    @Ignore
-    public void method9() throws AssertionFailedError {
-        assertTrue(false);
-    }
-
-    // throw AssumptionViolatedException
-    public void method10() throws AssumptionViolatedException {
-        assumeTrue(false);
-    }
-
-    public static class OtherClass {
-
-        public static String otherInvoked = null;
-
-        public void method1() {
-            otherInvoked = OTHER_METHOD_1;
-        }
-    }
-}
diff --git a/common/device-side/util/tests/src/com/android/compatibility/common/util/BusinessLogicTestCaseTest.java b/common/device-side/util/tests/src/com/android/compatibility/common/util/BusinessLogicTestCaseTest.java
deleted file mode 100644
index ad4acbb..0000000
--- a/common/device-side/util/tests/src/com/android/compatibility/common/util/BusinessLogicTestCaseTest.java
+++ /dev/null
@@ -1,71 +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.compatibility.common.util;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertTrue;
-
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Tests for {@line BusinessLogicTestCase}.
- */
-@RunWith(AndroidJUnit4.class)
-public class BusinessLogicTestCaseTest {
-
-    private static final String KEY_1 = "key1";
-    private static final String KEY_2 = "key2";
-    private static final String VALUE_1 = "value1";
-    private static final String VALUE_2 = "value2";
-
-    DummyTest mDummyTest;
-    DummyTest mOtherDummyTest;
-
-    @Before
-    public void setUp() {
-        mDummyTest = new DummyTest();
-        mOtherDummyTest = new DummyTest();
-    }
-
-    @Test
-    public void testMapPut() throws Exception {
-        mDummyTest.mapPut("instanceMap", KEY_1, VALUE_1);
-        assertTrue("mapPut failed for instanceMap", mDummyTest.instanceMap.containsKey(KEY_1));
-        assertEquals("mapPut failed for instanceMap", mDummyTest.instanceMap.get(KEY_1), VALUE_1);
-        assertTrue("mapPut affected wrong instance", mOtherDummyTest.instanceMap.isEmpty());
-    }
-
-    @Test
-    public void testStaticMapPut() throws Exception {
-        mDummyTest.mapPut("staticMap", KEY_2, VALUE_2);
-        assertTrue("mapPut failed for staticMap", mDummyTest.staticMap.containsKey(KEY_2));
-        assertEquals("mapPut failed for staticMap", mDummyTest.staticMap.get(KEY_2), VALUE_2);
-        assertTrue("mapPut on static map should affect all instances",
-                mOtherDummyTest.staticMap.containsKey(KEY_2));
-    }
-
-    public static class DummyTest extends BusinessLogicTestCase {
-        public Map<String, String> instanceMap = new HashMap<>();
-        public static Map<String, String> staticMap = new HashMap<>();
-    }
-}
diff --git a/common/device-side/util/tests/src/com/android/compatibility/common/util/DeviceReportTest.java b/common/device-side/util/tests/src/com/android/compatibility/common/util/DeviceReportTest.java
deleted file mode 100644
index ab42b32..0000000
--- a/common/device-side/util/tests/src/com/android/compatibility/common/util/DeviceReportTest.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * 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
- */
-package com.android.compatibility.common.util;
-
-import android.app.Instrumentation;
-import android.os.Bundle;
-import android.os.Environment;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.lang.StringBuilder;
-
-import junit.framework.TestCase;
-
-import org.json.JSONObject;
-
-/**
- * Tests for {@line DeviceReportLog}.
- */
-public class DeviceReportTest extends TestCase {
-
-    /**
-     * A stub of {@link Instrumentation}
-     */
-    public class TestInstrumentation extends Instrumentation {
-
-        private int mResultCode = -1;
-        private Bundle mResults = null;
-
-        @Override
-        public void sendStatus(int resultCode, Bundle results) {
-            mResultCode = resultCode;
-            mResults = results;
-        }
-    }
-
-    private static final int RESULT_CODE = 2;
-    private static final String RESULT_KEY = "COMPATIBILITY_TEST_RESULT";
-    private static final String TEST_MESSAGE_1 = "Foo";
-    private static final double TEST_VALUE_1 = 3;
-    private static final ResultType TEST_TYPE_1 = ResultType.HIGHER_BETTER;
-    private static final ResultUnit TEST_UNIT_1 = ResultUnit.SCORE;
-    private static final String TEST_MESSAGE_2 = "Bar";
-    private static final double TEST_VALUE_2 = 5;
-    private static final ResultType TEST_TYPE_2 = ResultType.LOWER_BETTER;
-    private static final ResultUnit TEST_UNIT_2 = ResultUnit.COUNT;
-    private static final String TEST_MESSAGE_3 = "Sample";
-    private static final double TEST_VALUE_3 = 7;
-    private static final ResultType TEST_TYPE_3 = ResultType.LOWER_BETTER;
-    private static final ResultUnit TEST_UNIT_3 = ResultUnit.COUNT;
-    private static final String TEST_MESSAGE_4 = "Message";
-    private static final double TEST_VALUE_4 = 9;
-    private static final ResultType TEST_TYPE_4 = ResultType.LOWER_BETTER;
-    private static final ResultUnit TEST_UNIT_4 = ResultUnit.COUNT;
-    private static final String REPORT_NAME_1 = "TestReport1";
-    private static final String REPORT_NAME_2 = "TestReport2";
-    private static final String STREAM_NAME_1 = "SampleStream1";
-    private static final String STREAM_NAME_2 = "SampleStream2";
-    private static final String STREAM_NAME_3 = "SampleStream3";
-    private static final String STREAM_NAME_4 = "SampleStream4";
-
-    public void testSubmit() throws Exception {
-        DeviceReportLog log = new DeviceReportLog(REPORT_NAME_1, STREAM_NAME_1);
-        log.addValue(TEST_MESSAGE_1, TEST_VALUE_1, TEST_TYPE_1, TEST_UNIT_1);
-        log.setSummary(TEST_MESSAGE_2, TEST_VALUE_2, TEST_TYPE_2, TEST_UNIT_2);
-        TestInstrumentation inst = new TestInstrumentation();
-        log.submit(inst);
-        assertEquals("Incorrect result code", RESULT_CODE, inst.mResultCode);
-        assertNotNull("Bundle missing", inst.mResults);
-        String metrics = inst.mResults.getString(RESULT_KEY);
-        assertNotNull("Metrics missing", metrics);
-        ReportLog result = ReportLog.parse(metrics);
-        assertNotNull("Metrics could not be decoded", result);
-    }
-
-    public void testFile() throws Exception {
-        assertTrue("External storage is not mounted",
-                Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED));
-        final File dir = new File(Environment.getExternalStorageDirectory(), "report-log-files");
-        assertTrue("Report Log directory missing", dir.isDirectory() || dir.mkdirs());
-
-        // Remove files from earlier possible runs.
-        File[] files = dir.listFiles();
-        for (File file : files) {
-            file.delete();
-        }
-
-        TestInstrumentation inst = new TestInstrumentation();
-
-        DeviceReportLog log1 = new DeviceReportLog(REPORT_NAME_1, STREAM_NAME_1);
-        log1.addValue(TEST_MESSAGE_1, TEST_VALUE_1, TEST_TYPE_1, TEST_UNIT_1);
-        log1.setSummary(TEST_MESSAGE_1, TEST_VALUE_1, TEST_TYPE_1, TEST_UNIT_1);
-        log1.submit(inst);
-
-        DeviceReportLog log2 = new DeviceReportLog(REPORT_NAME_1, STREAM_NAME_2);
-        log2.addValue(TEST_MESSAGE_2, TEST_VALUE_2, TEST_TYPE_2, TEST_UNIT_2);
-        log2.setSummary(TEST_MESSAGE_2, TEST_VALUE_2, TEST_TYPE_2, TEST_UNIT_2);
-        log2.submit(inst);
-
-        DeviceReportLog log3 = new DeviceReportLog(REPORT_NAME_2, STREAM_NAME_3);
-        log3.addValue(TEST_MESSAGE_3, TEST_VALUE_3, TEST_TYPE_3, TEST_UNIT_3);
-        log3.setSummary(TEST_MESSAGE_3, TEST_VALUE_3, TEST_TYPE_3, TEST_UNIT_3);
-        log3.submit(inst);
-
-        DeviceReportLog log4 = new DeviceReportLog(REPORT_NAME_2, STREAM_NAME_4);
-        log4.addValue(TEST_MESSAGE_4, TEST_VALUE_4, TEST_TYPE_4, TEST_UNIT_4);
-        log4.setSummary(TEST_MESSAGE_4, TEST_VALUE_4, TEST_TYPE_4, TEST_UNIT_4);
-        log4.submit(inst);
-
-        File jsonFile1 = new File(dir, REPORT_NAME_1 + ".reportlog.json");
-        File jsonFile2 = new File(dir, REPORT_NAME_2 + ".reportlog.json");
-        assertTrue("Report Log missing", jsonFile1.exists());
-        assertTrue("Report Log missing", jsonFile2.exists());
-
-        BufferedReader jsonReader = new BufferedReader(new FileReader(jsonFile1));
-        StringBuilder metricsBuilder = new StringBuilder();
-        String line;
-        while ((line = jsonReader.readLine()) != null) {
-            metricsBuilder.append(line);
-        }
-        String metrics = metricsBuilder.toString().trim();
-        JSONObject jsonObject = new JSONObject(metrics);
-        assertTrue("Incorrect metrics",
-                jsonObject.getJSONObject(STREAM_NAME_1).getDouble(TEST_MESSAGE_1) == TEST_VALUE_1);
-        assertTrue("Incorrect metrics",
-                jsonObject.getJSONObject(STREAM_NAME_2).getDouble(TEST_MESSAGE_2) == TEST_VALUE_2);
-
-        jsonReader = new BufferedReader(new FileReader(jsonFile2));
-        metricsBuilder = new StringBuilder();
-        while ((line = jsonReader.readLine()) != null) {
-            metricsBuilder.append(line);
-        }
-        metrics = metricsBuilder.toString().trim();
-        jsonObject = new JSONObject(metrics);
-        assertTrue("Incorrect metrics",
-                jsonObject.getJSONObject(STREAM_NAME_3).getDouble(TEST_MESSAGE_3) == TEST_VALUE_3);
-        assertTrue("Incorrect metrics",
-                jsonObject.getJSONObject(STREAM_NAME_4).getDouble(TEST_MESSAGE_4) == TEST_VALUE_4);
-    }
-}
diff --git a/common/device-side/util/tests/src/com/android/compatibility/common/util/RetryRuleTest.java b/common/device-side/util/tests/src/com/android/compatibility/common/util/RetryRuleTest.java
deleted file mode 100644
index 644d95f..0000000
--- a/common/device-side/util/tests/src/com/android/compatibility/common/util/RetryRuleTest.java
+++ /dev/null
@@ -1,138 +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.compatibility.common.util;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.testng.Assert.assertThrows;
-import static org.testng.Assert.expectThrows;
-
-import org.junit.Test;
-import org.junit.runner.Description;
-import org.junit.runner.RunWith;
-import org.junit.runners.model.Statement;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-
-@RunWith(MockitoJUnitRunner.class)
-public class RetryRuleTest {
-
-    private final Description mDescription = Description.createSuiteDescription("Whatever");
-
-    private static final RetryableException sRetryableException =
-            new RetryableException("Y U NO RETRY?");
-
-    private static class RetryableStatement<T extends Exception> extends Statement {
-        private final int mNumberFailures;
-        private int mNumberCalls;
-        private final T mException;
-
-        RetryableStatement(int numberFailures, T exception) {
-            mNumberFailures = numberFailures;
-            mException = exception;
-        }
-
-        @Override
-        public void evaluate() throws Throwable {
-            mNumberCalls++;
-            if (mNumberCalls <= mNumberFailures) {
-                throw mException;
-            }
-        }
-
-        @Override
-        public String toString() {
-            return "RetryableStatement: failures=" + mNumberFailures + ", calls=" + mNumberCalls;
-        }
-    }
-
-    private @Mock Statement mMockStatement;
-
-    @Test
-    public void testInvalidConstructor() throws Throwable {
-        assertThrows(IllegalArgumentException.class, () -> new RetryRule(-1));
-    }
-
-    @Test
-    public void testPassOnRetryableException() throws Throwable {
-        final RetryRule rule = new RetryRule(1);
-        rule.apply(new RetryableStatement<RetryableException>(1, sRetryableException), mDescription)
-                .evaluate();
-    }
-
-    @Test
-    public void testPassOnRetryableExceptionWithTimeout() throws Throwable {
-        final Timeout timeout = new Timeout("YOUR TIME IS GONE", 1, 2, 10);
-        final RetryableException exception = new RetryableException(timeout, "Y U NO?");
-
-        final RetryRule rule = new RetryRule(1);
-        rule.apply(new RetryableStatement<RetryableException>(1, exception), mDescription)
-                .evaluate();
-
-        // Assert timeout was increased
-        assertThat(timeout.ms()).isEqualTo(2);
-    }
-
-    @Test
-    public void testFailOnRetryableException() throws Throwable {
-        final RetryRule rule = new RetryRule(1);
-
-        final RetryableException actualException = expectThrows(RetryableException.class,
-                () -> rule.apply(new RetryableStatement<RetryableException>(2, sRetryableException),
-                        mDescription).evaluate());
-
-        assertThat(actualException).isSameAs(sRetryableException);
-    }
-
-    @Test
-    public void testPassWhenDisabledAndStatementPass() throws Throwable {
-        final RetryRule rule = new RetryRule(0);
-
-        rule.apply(mMockStatement, mDescription).evaluate();
-
-        verify(mMockStatement, times(1)).evaluate();
-    }
-
-    @Test
-    public void testFailWhenDisabledAndStatementThrowsRetryableException() throws Throwable {
-        final RetryableException exception = new RetryableException("Y U NO?");
-        final RetryRule rule = new RetryRule(0);
-        doThrow(exception).when(mMockStatement).evaluate();
-
-        final RetryableException actualException = expectThrows(RetryableException.class,
-                () -> rule.apply(mMockStatement, mDescription).evaluate());
-
-        assertThat(actualException).isSameAs(exception);
-        verify(mMockStatement, times(1)).evaluate();
-    }
-
-    @Test
-    public void testFailWhenDisabledAndStatementThrowsNonRetryableException() throws Throwable {
-        final RuntimeException exception = new RuntimeException("Y U NO?");
-        final RetryRule rule = new RetryRule(0);
-        doThrow(exception).when(mMockStatement).evaluate();
-
-        final RuntimeException actualException = expectThrows(RuntimeException.class,
-                () -> rule.apply(mMockStatement, mDescription).evaluate());
-
-        assertThat(actualException).isSameAs(exception);
-        verify(mMockStatement, times(1)).evaluate();
-    }
-}
diff --git a/common/device-side/util/tests/src/com/android/compatibility/common/util/SafeCleanerRuleTest.java b/common/device-side/util/tests/src/com/android/compatibility/common/util/SafeCleanerRuleTest.java
deleted file mode 100644
index a56d7b2..0000000
--- a/common/device-side/util/tests/src/com/android/compatibility/common/util/SafeCleanerRuleTest.java
+++ /dev/null
@@ -1,299 +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.compatibility.common.util;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.verify;
-import static org.testng.Assert.expectThrows;
-
-import com.android.compatibility.common.util.SafeCleanerRule.Dumper;
-
-import com.google.common.collect.ImmutableList;
-
-import org.junit.AssumptionViolatedException;
-import org.junit.Test;
-import org.junit.runner.Description;
-import org.junit.runner.RunWith;
-import org.junit.runners.model.Statement;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-
-import java.util.List;
-import java.util.concurrent.Callable;
-
-@RunWith(MockitoJUnitRunner.class)
-public class SafeCleanerRuleTest {
-
-    private static class FailureStatement extends Statement {
-        private final Throwable mThrowable;
-
-        FailureStatement(Throwable t) {
-            mThrowable = t;
-        }
-
-        @Override
-        public void evaluate() throws Throwable {
-            throw mThrowable;
-        }
-    }
-
-    private final Description mDescription = Description.createSuiteDescription("Whatever");
-    private final RuntimeException mRuntimeException = new RuntimeException("D'OH!");
-
-    @Mock private Dumper mDumper;
-
-    // Use mocks for objects that don't throw any exception.
-    @Mock private ThrowingRunnable mGoodGuyRunner1;
-    @Mock private ThrowingRunnable mGoodGuyRunner2;
-    @Mock private Callable<List<Throwable>> mGoodGuyExtraExceptions1;
-    @Mock private Callable<List<Throwable>> mGoodGuyExtraExceptions2;
-    @Mock private Statement mGoodGuyStatement;
-
-    @Test
-    public void testEmptyRule_testPass() throws Throwable {
-        final SafeCleanerRule rule = new SafeCleanerRule();
-        rule.apply(mGoodGuyStatement, mDescription).evaluate();
-    }
-
-    @Test
-    public void testEmptyRule_testFails() throws Throwable {
-        final SafeCleanerRule rule = new SafeCleanerRule();
-        final Throwable actualException = expectThrows(RuntimeException.class,
-                () -> rule.apply(new FailureStatement(mRuntimeException), mDescription).evaluate());
-        assertThat(actualException).isSameAs(mRuntimeException);
-    }
-
-    @Test
-    public void testEmptyRule_testFails_withDumper() throws Throwable {
-        final SafeCleanerRule rule = new SafeCleanerRule().setDumper(mDumper);
-        final Throwable actualException = expectThrows(RuntimeException.class,
-                () -> rule.apply(new FailureStatement(mRuntimeException), mDescription).evaluate());
-        assertThat(actualException).isSameAs(mRuntimeException);
-        verify(mDumper).dump("Whatever", actualException);
-    }
-
-    @Test
-    public void testOnlyTestFails() throws Throwable {
-        final SafeCleanerRule rule = new SafeCleanerRule()
-                .run(mGoodGuyRunner1)
-                .add(mGoodGuyExtraExceptions1);
-        final Throwable actualException = expectThrows(RuntimeException.class,
-                () -> rule.apply(new FailureStatement(mRuntimeException), mDescription).evaluate());
-        assertThat(actualException).isSameAs(mRuntimeException);
-        verify(mGoodGuyRunner1).run();
-        verify(mGoodGuyExtraExceptions1).call();
-    }
-
-    @Test
-    public void testOnlyTestFails_withDumper() throws Throwable {
-        final SafeCleanerRule rule = new SafeCleanerRule()
-                .setDumper(mDumper)
-                .run(mGoodGuyRunner1)
-                .add(mGoodGuyExtraExceptions1);
-        final Throwable actualException = expectThrows(RuntimeException.class,
-                () -> rule.apply(new FailureStatement(mRuntimeException), mDescription).evaluate());
-        assertThat(actualException).isSameAs(mRuntimeException);
-        verify(mGoodGuyRunner1).run();
-        verify(mGoodGuyExtraExceptions1).call();
-        verify(mDumper).dump("Whatever", actualException);
-    }
-
-    @Test
-    public void testTestPass_oneRunnerFails() throws Throwable {
-        final SafeCleanerRule rule = new SafeCleanerRule()
-                .run(mGoodGuyRunner1)
-                .run(() -> { throw mRuntimeException; })
-                .run(mGoodGuyRunner2)
-                .add(mGoodGuyExtraExceptions1);
-        final Throwable actualException = expectThrows(RuntimeException.class,
-                () -> rule.apply(mGoodGuyStatement, mDescription).evaluate());
-        assertThat(actualException).isSameAs(mRuntimeException);
-        verify(mGoodGuyRunner1).run();
-        verify(mGoodGuyRunner2).run();
-        verify(mGoodGuyExtraExceptions1).call();
-    }
-
-    @Test
-    public void testTestPass_oneRunnerFails_withDumper() throws Throwable {
-        final SafeCleanerRule rule = new SafeCleanerRule()
-                .setDumper(mDumper)
-                .run(mGoodGuyRunner1)
-                .run(() -> {
-                    throw mRuntimeException;
-                })
-                .run(mGoodGuyRunner2)
-                .add(mGoodGuyExtraExceptions1);
-        final Throwable actualException = expectThrows(RuntimeException.class,
-                () -> rule.apply(mGoodGuyStatement, mDescription).evaluate());
-        assertThat(actualException).isSameAs(mRuntimeException);
-        verify(mGoodGuyRunner1).run();
-        verify(mGoodGuyRunner2).run();
-        verify(mGoodGuyExtraExceptions1).call();
-        verify(mDumper).dump("Whatever", actualException);
-    }
-
-    @Test
-    public void testTestPass_oneExtraExceptionThrownAsCallable() throws Throwable {
-        final SafeCleanerRule rule = new SafeCleanerRule()
-                .run(mGoodGuyRunner1)
-                .add(mRuntimeException)
-                .add(mGoodGuyExtraExceptions1)
-                .run(mGoodGuyRunner2);
-        final Throwable actualException = expectThrows(RuntimeException.class,
-                () -> rule.apply(mGoodGuyStatement, mDescription).evaluate());
-        assertThat(actualException).isSameAs(mRuntimeException);
-        verify(mGoodGuyRunner1).run();
-        verify(mGoodGuyRunner2).run();
-        verify(mGoodGuyExtraExceptions1).call();
-    }
-
-    @Test
-    public void testTestPass_oneExtraExceptionThrown() throws Throwable {
-        final SafeCleanerRule rule = new SafeCleanerRule()
-                .run(mGoodGuyRunner1)
-                .add(() -> {
-                    return ImmutableList.of(mRuntimeException);
-                })
-                .add(mGoodGuyExtraExceptions1)
-                .run(mGoodGuyRunner2);
-        final Throwable actualException = expectThrows(RuntimeException.class,
-                () -> rule.apply(mGoodGuyStatement, mDescription).evaluate());
-        assertThat(actualException).isSameAs(mRuntimeException);
-        verify(mGoodGuyRunner1).run();
-        verify(mGoodGuyRunner2).run();
-        verify(mGoodGuyExtraExceptions1).call();
-    }
-
-    @Test
-    public void testTestPass_oneExtraExceptionThrown_withDumper() throws Throwable {
-        final SafeCleanerRule rule = new SafeCleanerRule()
-                .setDumper(mDumper)
-                .run(mGoodGuyRunner1)
-                .add(() -> { return ImmutableList.of(mRuntimeException); })
-                .add(mGoodGuyExtraExceptions1)
-                .run(mGoodGuyRunner2);
-        final Throwable actualException = expectThrows(RuntimeException.class,
-                () -> rule.apply(mGoodGuyStatement, mDescription).evaluate());
-        assertThat(actualException).isSameAs(mRuntimeException);
-        verify(mGoodGuyRunner1).run();
-        verify(mGoodGuyRunner2).run();
-        verify(mGoodGuyExtraExceptions1).call();
-        verify(mDumper).dump("Whatever", actualException);
-    }
-
-    @Test
-    public void testThrowTheKitchenSinkAKAEverybodyThrows() throws Throwable {
-        final Exception extra1 = new Exception("1");
-        final Exception extra2 = new Exception("2");
-        final Exception extra3 = new Exception("3");
-        final Error error1 = new Error("one");
-        final Error error2 = new Error("two");
-        final RuntimeException testException  = new RuntimeException("TEST, Y U NO PASS?");
-        final SafeCleanerRule rule = new SafeCleanerRule()
-                .run(mGoodGuyRunner1)
-                .add(mGoodGuyExtraExceptions1)
-                .add(mRuntimeException)
-                .add(() -> {
-                    return ImmutableList.of(extra1, extra2);
-                })
-                .run(() -> {
-                    throw error1;
-                })
-                .run(mGoodGuyRunner2)
-                .add(() -> {
-                    return ImmutableList.of(extra3);
-                })
-                .add(mGoodGuyExtraExceptions2)
-                .run(() -> {
-                    throw error2;
-                });
-
-        final SafeCleanerRule.MultipleExceptions actualException = expectThrows(
-                SafeCleanerRule.MultipleExceptions.class,
-                () -> rule.apply(new FailureStatement(testException), mDescription).evaluate());
-        assertThat(actualException.getThrowables())
-                .containsExactly(testException, mRuntimeException, error1, error2, extra1, extra2,
-                        extra3)
-                .inOrder();
-        verify(mGoodGuyRunner1).run();
-        verify(mGoodGuyRunner2).run();
-        verify(mGoodGuyExtraExceptions1).call();
-    }
-
-    @Test
-    public void testIgnoreAssumptionViolatedException() throws Throwable {
-        final AssumptionViolatedException ave = new AssumptionViolatedException(
-                "tis an assumption violation");
-        final RuntimeException testException  = new RuntimeException("TEST, Y U NO PASS?");
-        final SafeCleanerRule rule = new SafeCleanerRule()
-                .run(mGoodGuyRunner1)
-                .add(mRuntimeException)
-                .run(() -> {
-                    throw ave;
-                });
-
-        final SafeCleanerRule.MultipleExceptions actualException = expectThrows(
-                SafeCleanerRule.MultipleExceptions.class,
-                () -> rule.apply(new FailureStatement(testException), mDescription).evaluate());
-        assertThat(actualException.getThrowables())
-                .containsExactly(testException, mRuntimeException)
-                .inOrder();
-        verify(mGoodGuyRunner1).run();
-    }
-
-    @Test
-    public void testThrowTheKitchenSinkAKAEverybodyThrows_withDumper() throws Throwable {
-        final Exception extra1 = new Exception("1");
-        final Exception extra2 = new Exception("2");
-        final Exception extra3 = new Exception("3");
-        final Exception extra4 = new Exception("4");
-        final Error error1 = new Error("one");
-        final Error error2 = new Error("two");
-        final RuntimeException testException  = new RuntimeException("TEST, Y U NO PASS?");
-        final SafeCleanerRule rule = new SafeCleanerRule()
-                .setDumper(mDumper)
-                .run(mGoodGuyRunner1)
-                .add(mGoodGuyExtraExceptions1)
-                .add(() -> {
-                    return ImmutableList.of(extra1, extra2);
-                })
-                .run(() -> {
-                    throw error1;
-                })
-                .run(mGoodGuyRunner2)
-                .add(() -> { return ImmutableList.of(extra3); })
-                .add(mGoodGuyExtraExceptions2)
-                .run(() -> {
-                    throw error2;
-                })
-                .run(() -> {
-                    throw extra4;
-                });
-
-        final SafeCleanerRule.MultipleExceptions actualException = expectThrows(
-                SafeCleanerRule.MultipleExceptions.class,
-                () -> rule.apply(new FailureStatement(testException), mDescription).evaluate());
-        assertThat(actualException.getThrowables())
-                .containsExactly(testException, error1, error2, extra4, extra1, extra2, extra3)
-                .inOrder();
-        verify(mGoodGuyRunner1).run();
-        verify(mGoodGuyRunner2).run();
-        verify(mGoodGuyExtraExceptions1).call();
-        verify(mDumper).dump("Whatever", actualException);
-    }
-}
diff --git a/common/device-side/util/tests/src/com/android/compatibility/common/util/StateChangerRuleTest.java b/common/device-side/util/tests/src/com/android/compatibility/common/util/StateChangerRuleTest.java
deleted file mode 100644
index 9b1851e..0000000
--- a/common/device-side/util/tests/src/com/android/compatibility/common/util/StateChangerRuleTest.java
+++ /dev/null
@@ -1,173 +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.compatibility.common.util;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertThrows;
-import static org.testng.Assert.expectThrows;
-
-import org.junit.Test;
-import org.junit.runner.Description;
-import org.junit.runner.RunWith;
-import org.junit.runners.model.Statement;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-
-@RunWith(MockitoJUnitRunner.class)
-public class StateChangerRuleTest {
-
-    private final RuntimeException mRuntimeException = new RuntimeException("D'OH");
-    private final Description mDescription = Description.createSuiteDescription("Whatever");
-
-    @Mock
-    private StateManager<String> mStateManager;
-
-    @Mock
-    private Statement mStatement;
-
-    @Test
-    public void testInvalidConstructor() {
-        assertThrows(NullPointerException.class,
-                () -> new StateChangerRule<Object>(null, "value"));
-    }
-
-    @Test
-    public void testSetAndRestoreOnSuccess() throws Throwable {
-        final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
-                "newValue");
-        when(mStateManager.get()).thenReturn("before", "changed");
-
-        rule.apply(mStatement, mDescription).evaluate();
-
-        verify(mStatement, times(1)).evaluate();
-        verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
-        verify(mStateManager, times(1)).set("newValue");
-        verify(mStateManager, times(1)).set("before");
-        verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
-    }
-
-    @Test
-    public void testDontSetIfSameValueOnSuccess() throws Throwable {
-        final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
-                "sameValue");
-        when(mStateManager.get()).thenReturn("sameValue");
-
-        rule.apply(mStatement, mDescription).evaluate();
-
-        verify(mStatement, times(1)).evaluate();
-        verify(mStateManager, never()).set(anyString());
-    }
-
-    @Test
-    public void testSetButDontRestoreIfSameValueOnSuccess() throws Throwable {
-        final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
-                "newValue");
-        when(mStateManager.get()).thenReturn("before", "before");
-
-        rule.apply(mStatement, mDescription).evaluate();
-
-        verify(mStatement, times(1)).evaluate();
-        verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
-        verify(mStateManager, times(1)).set("newValue");
-        verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
-    }
-
-    @Test
-    public void testDontSetButRestoreIfValueChangedOnSuccess() throws Throwable {
-        final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
-                "sameValue");
-        when(mStateManager.get()).thenReturn("sameValue", "changed");
-
-        rule.apply(mStatement, mDescription).evaluate();
-
-        verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
-        verify(mStatement, times(1)).evaluate();
-        verify(mStateManager, times(1)).set("sameValue");
-        verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
-    }
-
-    @Test
-    public void testSetAndRestoreOnFailure() throws Throwable {
-        final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
-                "newValue");
-        when(mStateManager.get()).thenReturn("before", "changed");
-        doThrow(mRuntimeException).when(mStatement).evaluate();
-
-        final RuntimeException actualException = expectThrows(RuntimeException.class,
-                () -> rule.apply(mStatement, mDescription).evaluate());
-        assertThat(actualException).isSameAs(mRuntimeException);
-
-        verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
-        verify(mStateManager, times(1)).set("newValue");
-        verify(mStateManager, times(1)).set("before");
-        verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
-    }
-
-    @Test
-    public void testDontSetIfSameValueOnFailure() throws Throwable {
-        final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
-                "sameValue");
-        when(mStateManager.get()).thenReturn("sameValue");
-        doThrow(mRuntimeException).when(mStatement).evaluate();
-
-        final RuntimeException actualException = expectThrows(RuntimeException.class,
-                () -> rule.apply(mStatement, mDescription).evaluate());
-        assertThat(actualException).isSameAs(mRuntimeException);
-
-        verify(mStateManager, never()).set(anyString());
-    }
-
-    @Test
-    public void testSetButDontRestoreIfSameValueOnFailure() throws Throwable {
-        final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
-                "newValue");
-        when(mStateManager.get()).thenReturn("before", "before");
-        doThrow(mRuntimeException).when(mStatement).evaluate();
-
-        final RuntimeException actualException = expectThrows(RuntimeException.class,
-                () -> rule.apply(mStatement, mDescription).evaluate());
-        assertThat(actualException).isSameAs(mRuntimeException);
-
-        verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
-        verify(mStateManager, times(1)).set("newValue");
-        verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
-    }
-
-    @Test
-    public void testDontSetButRestoreIfValueChangedOnFailure() throws Throwable {
-        final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
-                "sameValue");
-        when(mStateManager.get()).thenReturn("sameValue", "changed");
-        doThrow(mRuntimeException).when(mStatement).evaluate();
-
-        final RuntimeException actualException = expectThrows(RuntimeException.class,
-                () -> rule.apply(mStatement, mDescription).evaluate());
-        assertThat(actualException).isSameAs(mRuntimeException);
-
-        verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
-        verify(mStateManager, times(1)).set("sameValue");
-        verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
-    }
-}
diff --git a/common/device-side/util/tests/src/com/android/compatibility/common/util/StateKeeperRuleTest.java b/common/device-side/util/tests/src/com/android/compatibility/common/util/StateKeeperRuleTest.java
deleted file mode 100644
index 4599aca..0000000
--- a/common/device-side/util/tests/src/com/android/compatibility/common/util/StateKeeperRuleTest.java
+++ /dev/null
@@ -1,107 +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.compatibility.common.util;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertThrows;
-import static org.testng.Assert.expectThrows;
-
-import org.junit.Test;
-import org.junit.runner.Description;
-import org.junit.runner.RunWith;
-import org.junit.runners.model.Statement;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-
-@RunWith(MockitoJUnitRunner.class)
-public class StateKeeperRuleTest {
-
-    private final RuntimeException mRuntimeException = new RuntimeException("D'OH");
-    private final Description mDescription = Description.createSuiteDescription("Whatever");
-
-    @Mock
-    private StateManager<String> mStateManager;
-
-    @Mock
-    private Statement mStatement;
-
-    @Test
-    public void testInvalidConstructor() {
-        assertThrows(NullPointerException.class, () -> new StateKeeperRule<Object>(null));
-    }
-
-    @Test
-    public void testRestoreOnSuccess() throws Throwable {
-        final StateKeeperRule<String> rule = new StateKeeperRule<>(mStateManager);
-        when(mStateManager.get()).thenReturn("before", "changed");
-
-        rule.apply(mStatement, mDescription).evaluate();
-
-        verify(mStatement, times(1)).evaluate();
-        verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
-        verify(mStateManager, times(1)).set("before");
-        verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
-    }
-
-    @Test
-    public void testRestoreOnFailure() throws Throwable {
-        final StateKeeperRule<String> rule = new StateKeeperRule<>(mStateManager);
-        when(mStateManager.get()).thenReturn("before", "changed");
-        doThrow(mRuntimeException).when(mStatement).evaluate();
-
-        final RuntimeException actualException = expectThrows(RuntimeException.class,
-                () -> rule.apply(mStatement, mDescription).evaluate());
-
-        assertThat(actualException).isSameAs(mRuntimeException);
-        verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
-        verify(mStateManager, times(1)).set("before");
-        verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
-    }
-
-    @Test
-    public void testDoNotRestoreWhenNotChanged() throws Throwable {
-        final StateKeeperRule<String> rule = new StateKeeperRule<>(mStateManager);
-        when(mStateManager.get()).thenReturn("not_changed");
-
-        rule.apply(mStatement, mDescription).evaluate();
-
-        verify(mStatement, times(1)).evaluate();
-        verify(mStateManager, never()).set(anyString());
-    }
-
-    @Test
-    public void testDoNotRestoreOnFailure() throws Throwable {
-        final StateKeeperRule<String> rule = new StateKeeperRule<>(mStateManager);
-        when(mStateManager.get()).thenReturn("not_changed");
-        doThrow(mRuntimeException).when(mStatement).evaluate();
-
-        final RuntimeException actualException = expectThrows(RuntimeException.class,
-                () -> rule.apply(mStatement, mDescription).evaluate());
-
-        assertThat(actualException).isSameAs(mRuntimeException);
-
-        verify(mStateManager, never()).set(anyString());
-    }
-}
diff --git a/common/device-side/util/tests/src/com/android/compatibility/common/util/TimeoutTest.java b/common/device-side/util/tests/src/com/android/compatibility/common/util/TimeoutTest.java
deleted file mode 100644
index 8992d18..0000000
--- a/common/device-side/util/tests/src/com/android/compatibility/common/util/TimeoutTest.java
+++ /dev/null
@@ -1,146 +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.compatibility.common.util;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertThrows;
-import static org.testng.Assert.expectThrows;
-
-import android.os.SystemClock;
-
-import com.android.compatibility.common.util.Timeout.Sleeper;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-
-import java.util.concurrent.Callable;
-
-@RunWith(MockitoJUnitRunner.class)
-public class TimeoutTest {
-
-    private static final String NAME = "TIME, Y U NO OUT?";
-    private static final String DESC = "something";
-
-    @Mock
-    private Callable<Object> mJob;
-
-    private final MySleeper mSleeper = new MySleeper();
-
-    @Test
-    public void testInvalidConstructor() {
-        // Invalid name
-        assertThrows(IllegalArgumentException.class, ()-> new Timeout(null, 1, 2, 2));
-        assertThrows(IllegalArgumentException.class, ()-> new Timeout("", 1, 2, 2));
-        // Invalid initial value
-        assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, -1, 2, 2));
-        assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, 0, 2, 2));
-        // Invalid multiplier
-        assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, 1, -1, 2));
-        assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, 1, 0, 2));
-        assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, 1, 1, 2));
-        // Invalid max value
-        assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, 1, 2, -1));
-        assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, 1, 2, 0));
-        // Max value cannot be less than initial
-        assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, 2, 2, 1));
-    }
-
-    @Test
-    public void testGetters() {
-        final Timeout timeout = new Timeout(NAME, 1, 2, 5);
-        assertThat(timeout.ms()).isEqualTo(1);
-        assertFloat(timeout.getMultiplier(), 2);
-        assertThat(timeout.getMaxValue()).isEqualTo(5);
-        assertThat(timeout.getName()).isEqualTo(NAME);
-    }
-
-    @Test
-    public void testIncrease() {
-        final Timeout timeout = new Timeout(NAME, 1, 2, 5);
-        // Pre-maximum
-        assertThat(timeout.increase()).isEqualTo(1);
-        assertThat(timeout.ms()).isEqualTo(2);
-        assertThat(timeout.increase()).isEqualTo(2);
-        assertThat(timeout.ms()).isEqualTo(4);
-        // Post-maximum
-        assertThat(timeout.increase()).isEqualTo(4);
-        assertThat(timeout.ms()).isEqualTo(5);
-        assertThat(timeout.increase()).isEqualTo(5);
-        assertThat(timeout.ms()).isEqualTo(5);
-    }
-
-    @Test
-    public void testRun_invalidArgs() {
-        final Timeout timeout = new Timeout(NAME, 1, 2, 5);
-        // Invalid description
-        assertThrows(IllegalArgumentException.class, ()-> timeout.run(null, mJob));
-        assertThrows(IllegalArgumentException.class, ()-> timeout.run("", mJob));
-        // Invalid max attempts
-        assertThrows(IllegalArgumentException.class, ()-> timeout.run(DESC, -1, mJob));
-        assertThrows(IllegalArgumentException.class, ()-> timeout.run(DESC, 0, mJob));
-        // Invalid job
-        assertThrows(IllegalArgumentException.class, ()-> timeout.run(DESC, null));
-    }
-
-    @Test
-    public void testRun_successOnFirstAttempt() throws Exception {
-        final Timeout timeout = new Timeout(mSleeper, NAME, 100, 2, 500);
-        final Object result = new Object();
-        when(mJob.call()).thenReturn(result);
-        assertThat(timeout.run(DESC, 1, mJob)).isSameAs(result);
-        assertThat(mSleeper.totalSleepingTime).isEqualTo(0);
-    }
-
-    @Test
-    public void testRun_successOnSecondAttempt() throws Exception {
-        final Timeout timeout = new Timeout(mSleeper, NAME, 100, 2, 500);
-        final Object result = new Object();
-        when(mJob.call()).thenReturn((Object) null, result);
-        assertThat(timeout.run(DESC, 10, mJob)).isSameAs(result);
-        assertThat(mSleeper.totalSleepingTime).isEqualTo(10);
-    }
-
-    @Test
-    public void testRun_allAttemptsFailed() throws Exception {
-        final Timeout timeout = new Timeout(mSleeper, NAME, 100, 2, 500);
-        final RetryableException e = expectThrows(RetryableException.class,
-                () -> timeout.run(DESC, 10, mJob));
-        assertThat(e.getMessage()).contains(DESC);
-        assertThat(e.getTimeout()).isSameAs(timeout);
-        assertThat(mSleeper.totalSleepingTime).isEqualTo(100);
-    }
-
-    private static final class MySleeper implements Sleeper {
-        public long totalSleepingTime;
-
-        @Override
-        public void sleep(long napTimeMs) {
-            // We still need to sleep, as the retry is based on ellapsed time. We could use a
-            // Mockito spy, but let's keep it simple
-            SystemClock.sleep(napTimeMs);
-            totalSleepingTime += napTimeMs;
-        }
-    }
-
-    public static void assertFloat(float actualValue, float expectedValue) {
-        assertThat(actualValue).isWithin(1.0e-10f).of(expectedValue);
-    }
-}
diff --git a/hostsidetests/appbinding/hostside/OWNERS b/hostsidetests/appbinding/hostside/OWNERS
new file mode 100644
index 0000000..77d350b
--- /dev/null
+++ b/hostsidetests/appbinding/hostside/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 533114
+omakoto@google.com
+yamasani@google.com
diff --git a/hostsidetests/appsecurity/OWNERS b/hostsidetests/appsecurity/OWNERS
index f86fd96..ea12403 100644
--- a/hostsidetests/appsecurity/OWNERS
+++ b/hostsidetests/appsecurity/OWNERS
@@ -25,8 +25,10 @@
 per-file PermissionsHostTest.java = moltmann@google.com
 per-file PkgInstallSignatureVerificationTest.java = cbrubaker@google.com
 per-file PrivilegedUpdateTests.java = toddke@google.com
+per-file ReviewPermissionHelper = moltmann@google.com
+per-file RequestsOnlyCalendarApp22.java = moltmann@google.com
 per-file ScopedDirectoryAccessTest.java = jsharkey@google.com
 per-file SplitTests.java = toddke@google.com
 per-file StorageHostTest.java = jsharkey@google.com
 per-file UseEmbeddedDexTest.java = victorhsieh@google.com
-per-file UsePermission*.java = moltmann@google.com
+per-file UsePermission*.java = moltmann@google.com
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExceptionUtils.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExceptionUtils.java
new file mode 100644
index 0000000..437f2aa
--- /dev/null
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExceptionUtils.java
@@ -0,0 +1,44 @@
+/*
+ * 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.appsecurity.cts;
+
+/**
+ * Utilities to deal with exceptions
+ */
+public class ExceptionUtils {
+    private ExceptionUtils() {}
+
+    /**
+     * Gets the root {@link Throwable#getCause() cause} of {@code t}
+     */
+    public static Throwable getRootCause(Throwable t) {
+        while (t.getCause() != null) t = t.getCause();
+        return t;
+    }
+
+    /**
+     * Appends {@code cause} at the end of the causal chain of {@code t}
+     *
+     * @return {@code t} for convenience
+     */
+    public static <E extends Throwable> E appendCause(E t, Throwable cause) {
+        if (cause != null) {
+            getRootCause(t).initCause(cause);
+        }
+        return t;
+    }
+}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/MatcherUtils.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/MatcherUtils.java
new file mode 100644
index 0000000..3acfd90
--- /dev/null
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/MatcherUtils.java
@@ -0,0 +1,122 @@
+/*
+ * 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.appsecurity.cts;
+
+import static org.hamcrest.CoreMatchers.both;
+import static org.junit.Assert.assertThat;
+
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Assert;
+
+import java.util.function.Function;
+
+/**
+ * {@link Matcher} factories and {@link Assert#assertThat assertion} utilities.
+ */
+public class MatcherUtils {
+    private MatcherUtils() {}
+
+    /**
+     * Creates a matcher based on whether object's given property property satisfies
+     * the given matcher.
+     *
+     * This is often preferable over just retrieving the property directly and asserting on it, as
+     * it ensures the enclosing object is included in the error message, providing better context.
+     *
+     * E.g.:
+     * {@code
+     *   Matcher<Intent> isExplicit =
+     *           propertyMatches("component", Intent::getComponent, notNullValue());
+     *   assertThat(myIntent, isExplicit);
+     *   // ^ properly includes myIntent in error message, should match fail
+     * }
+     *
+     * @param propName property name to be used in error message
+     * @param propGetter retriever of the property value used for evaluation
+     * @param propCondition condition a property is asserted to satisfy
+     * @param <RECEIVER> type that has the given property
+     * @param <PROP> type of the given property
+     * @return a mather on {@code RECEIVER} type
+     */
+    public static <RECEIVER, PROP> Matcher<RECEIVER> propertyMatches(
+            String propName,
+            Function<? super RECEIVER, ? extends PROP> propGetter,
+            Matcher<? extends PROP> propCondition) {
+        return new TypeSafeMatcher<RECEIVER>() {
+            @Override
+            protected boolean matchesSafely(RECEIVER item) {
+                return propCondition.matches(propGetter.apply(item));
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("has ")
+                        .appendText(propName)
+                        .appendText(" that ")
+                        .appendDescriptionOf(propCondition);
+            }
+        };
+    }
+
+    /**
+     * A more type-safe version of: {@link CoreMatchers#instanceOf} && {@code cond},
+     * with {@code cond} being parametrized on the subtype being tested for as the first step.
+     */
+    public static <SUPER, SUB extends SUPER> Matcher<SUPER> instanceOf(
+            Class<SUB> type, Matcher<? super SUB> cond) {
+        return (Matcher<SUPER>) both(CoreMatchers.instanceOf(type)).and((Matcher) cond);
+    }
+
+    /**
+     * {@link Throwable} matcher based on whether its {@link Throwable::getMessage message} is
+     * matching {@code condition}.
+     */
+    public static Matcher<Throwable> hasMessageThat(Matcher<? super String> condition) {
+        return propertyMatches("message", Throwable::getMessage, condition);
+    }
+
+    /**
+     * Runs {@code action}, and asserts that it throws an exception matching {@code exceptionCond}.
+     */
+    public static void assertThrows(
+            Matcher<? super Throwable> exceptionCond, ThrowingRunnable action) {
+        Throwable thrown;
+        try {
+            action.run();
+            thrown = null;
+        } catch (Throwable t) {
+            thrown = t;
+        }
+        assertOrRethrow(thrown, exceptionCond);
+    }
+
+    /**
+     * Asserts that {@code exception} matches the given {@code condition}, or else
+     * throws the resulting assertion error, with the original exception attached as a cause.
+     */
+    public static <E extends Throwable> void assertOrRethrow(
+            E exception, Matcher<? super E> condition) throws AssertionError {
+        try {
+            assertThat(exception, condition);
+        } catch (AssertionError err) {
+            throw ExceptionUtils.appendCause(err, exception);
+        }
+    }
+}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
index d0cc258..b14239b 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
@@ -16,6 +16,12 @@
 
 package android.appsecurity.cts;
 
+import static android.appsecurity.cts.MatcherUtils.assertThrows;
+import static android.appsecurity.cts.MatcherUtils.hasMessageThat;
+import static android.appsecurity.cts.MatcherUtils.instanceOf;
+
+import static org.hamcrest.CoreMatchers.containsString;
+
 import android.platform.test.annotations.AppModeFull;
 import android.platform.test.annotations.Presubmit;
 
@@ -121,31 +127,13 @@
     public void testFail() throws Exception {
         // Sanity check that remote failure is host failure
         assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_23), false, false));
-        boolean didThrow = false;
-        try {
-            runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest23",
-                    "testFail");
-        } catch (AssertionError expected) {
-            didThrow = true;
-        }
-        if (!didThrow) {
-            fail("Expected remote failure");
-        }
+        runThrowingTest("com.android.cts.usepermission.UsePermissionTest23", "testFail");
     }
 
     public void testKill() throws Exception {
         // Sanity check that remote kill is host failure
         assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_23), false, false));
-        boolean didThrow = false;
-        try {
-            runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest23",
-                    "testKill");
-        } catch (AssertionError expected) {
-            didThrow = true;
-        }
-        if (!didThrow) {
-            fail("Expected remote failure");
-        }
+        runThrowingTest("com.android.cts.usepermission.UsePermissionTest23", "testKill");
     }
 
     @AppModeFull(reason = "Instant applications must be at least SDK 26")
@@ -164,16 +152,8 @@
 
         approveReviewPermissionDialog();
 
-        boolean didThrow = false;
-        try {
-            runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest22",
-                    "testCompatRevoked_part1");
-        } catch (AssertionError expected) {
-            didThrow = true;
-        }
-        if (!didThrow) {
-            fail("App must be killed on a permission revoke");
-        }
+        runThrowingTest("com.android.cts.usepermission.UsePermissionTest22",
+                "testCompatRevoked_part1");
         runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest22",
                 "testCompatRevoked_part2");
     }
@@ -256,32 +236,16 @@
 
     public void testRevokeAffectsWholeGroup23() throws Exception {
         assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_23), false, false));
-        boolean didThrow = false;
-        try {
-            runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest23",
-                    "testRevokeAffectsWholeGroup_part1");
-        } catch (AssertionError expected) {
-            didThrow = true;
-        }
-        if (!didThrow) {
-            fail("Should have thrown an exception.");
-        }
+        runThrowingTest("com.android.cts.usepermission.UsePermissionTest23",
+                "testRevokeAffectsWholeGroup_part1");
         runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest23",
                 "testRevokeAffectsWholeGroup_part2");
     }
 
     public void testGrantPreviouslyRevokedWithPrejudiceShowsPrompt23() throws Exception {
         assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_23), false, false));
-        boolean didThrow = false;
-        try {
-            runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest23",
-                    "testGrantPreviouslyRevokedWithPrejudiceShowsPrompt_part1");
-        } catch (Throwable expected) {
-            didThrow = true;
-        }
-        if (!didThrow) {
-            fail("App must be killed on a permission revoke");
-        }
+        runThrowingTest("com.android.cts.usepermission.UsePermissionTest23",
+                "testGrantPreviouslyRevokedWithPrejudiceShowsPrompt_part1");
         runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest23",
                 "testGrantPreviouslyRevokedWithPrejudiceShowsPrompt_part2");
     }
@@ -319,15 +283,8 @@
 
     public void testNoDowngradePermissionModel() throws Exception {
         assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_23), false, false));
-        boolean didThrow = false;
-        try {
-            assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_22), true, false));
-        } catch (AssertionError expected) {
-            didThrow = true;
-        }
-        if (!didThrow) {
-            fail("Permission mode downgrade not allowed");
-        }
+        assertNotNull("Permission mode downgrade not allowed",
+                getDevice().installPackage(mBuildHelper.getTestFile(APK_22), true, false));
     }
 
     public void testNoResidualPermissionsOnUninstall() throws Exception {
@@ -345,16 +302,8 @@
 
         approveReviewPermissionDialog();
 
-        boolean didThrow = false;
-        try {
-            runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest22",
-                    "testRevokePropagatedOnUpgradeOldToNewModel_part1");
-        } catch (AssertionError expected) {
-            didThrow = true;
-        }
-        if (!didThrow) {
-            fail("App must be killed on a permission revoke");
-        }
+        runThrowingTest("com.android.cts.usepermission.UsePermissionTest22",
+                "testRevokePropagatedOnUpgradeOldToNewModel_part1");
         assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_23), true, false));
         runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest23",
                 "testRevokePropagatedOnUpgradeOldToNewModel_part2");
@@ -530,4 +479,10 @@
             throws DeviceNotAvailableException {
         Utils.runDeviceTestsAsCurrentUser(getDevice(), packageName, testClassName, testMethodName);
     }
+
+    private void runThrowingTest(String clazz, String testMethod) {
+        assertThrows(
+                instanceOf(AssertionError.class, hasMessageThat(containsString("Process crashed"))),
+                () -> runDeviceTests(USES_PERMISSION_PKG, clazz, testMethod));
+    }
 }
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ThrowingRunnable.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ThrowingRunnable.java
similarity index 94%
rename from common/device-side/util/src/com/android/compatibility/common/util/ThrowingRunnable.java
rename to hostsidetests/appsecurity/src/android/appsecurity/cts/ThrowingRunnable.java
index 0588cff..140ee75 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/ThrowingRunnable.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ThrowingRunnable.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.compatibility.common.util;
+package android.appsecurity.cts;
 
 /**
  * Similar to {@link Runnable} but has {@code throws Exception}.
diff --git a/hostsidetests/appsecurity/test-apps/DeclareNotRuntimePermissions/OWNERS b/hostsidetests/appsecurity/test-apps/DeclareNotRuntimePermissions/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/DeclareNotRuntimePermissions/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/DuplicatePermissionDeclareApp/OWNERS b/hostsidetests/appsecurity/test-apps/DuplicatePermissionDeclareApp/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/DuplicatePermissionDeclareApp/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/EscalateToRuntimePermissions/OWNERS b/hostsidetests/appsecurity/test-apps/EscalateToRuntimePermissions/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/EscalateToRuntimePermissions/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
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 78f2fff..30291a0 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
@@ -211,6 +211,18 @@
             fail("Expected write access to be blocked");
         } catch (SecurityException | FileNotFoundException expected) {
         }
+
+        // Verify that we can't grant ourselves access
+        for (int flag : new int[] {
+                Intent.FLAG_GRANT_READ_URI_PERMISSION,
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+        }) {
+            try {
+                mContext.grantUriPermission(mContext.getPackageName(), blue, flag);
+                fail("Expected granting to be blocked for flag 0x" + Integer.toHexString(flag));
+            } catch (SecurityException expected) {
+            }
+        }
     }
 
     @Test
diff --git a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/AndroidManifest.xml
index 429ea8b..9188993 100644
--- a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/AndroidManifest.xml
@@ -92,7 +92,8 @@
                     android:pathPrefix="/yes"
                     android:readPermission="com.android.cts.permissionWithSignature"
                     android:writePermission="com.android.cts.permissionWithSignature" />
-            <grant-uri-permission android:pathPattern=".*" />
+            <grant-uri-permission android:pathPattern="/foo.*" />
+            <grant-uri-permission android:pathPattern="/yes.*" />
         </provider>
 
         <!-- Target for tests that verify path permissions can restrict access
@@ -112,5 +113,6 @@
                     android:writePermission="com.android.cts.permissionNormal" />
         </provider>
 
+        <activity android:name=".SendResultActivity" android:exported="true" />
     </application>
 </manifest>
diff --git a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/OWNERS b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/SendResultActivity.java b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/SendResultActivity.java
new file mode 100644
index 0000000..4fdca81
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/SendResultActivity.java
@@ -0,0 +1,53 @@
+/*
+ * 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.permissiondeclareapp;
+
+import android.app.Activity;
+import android.content.ClipData;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+public class SendResultActivity extends Activity {
+    private static final String TAG = "SendUriActivity";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // For simplicity, we're willing to grant whatever they asked for
+        final ClipData reqClip = getIntent().getParcelableExtra(Intent.EXTRA_TEXT);
+        final int reqMode = getIntent().getIntExtra(Intent.EXTRA_INDEX, 0);
+
+        final Intent result = new Intent();
+        result.setClipData(reqClip);
+        result.addFlags(reqMode);
+
+        try {
+            Log.d(TAG, "Finishing OK with " + result);
+            setResult(RESULT_OK, result);
+            finish();
+        } catch (SecurityException e) {
+            Log.d(TAG, "Finishing CANCELED due to " + e);
+            setResult(RESULT_CANCELED, null);
+            finish();
+
+            // Make sure we hand control back to whoever started us
+            android.os.Process.killProcess(android.os.Process.myPid());
+        }
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/PermissionDeclareAppCompat/OWNERS b/hostsidetests/appsecurity/test-apps/PermissionDeclareAppCompat/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/PermissionDeclareAppCompat/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/PermissionPolicy25/OWNERS b/hostsidetests/appsecurity/test-apps/PermissionPolicy25/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/PermissionPolicy25/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/AndroidManifest.xml
index 7b254dc..30d73f5 100644
--- a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/AndroidManifest.xml
@@ -19,7 +19,7 @@
         package="com.android.cts.reviewpermissionhelper">
 
     <application>
-        <activity android:name=".ActivityStarter" />
+        <activity android:name="com.android.compatibility.common.util.FutureResultActivity" />
         <uses-library android:name="android.test.runner" />
     </application>
 
diff --git a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/OWNERS b/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/src/com/android/cts/reviewpermissionhelper/ActivityStarter.kt b/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/src/com/android/cts/reviewpermissionhelper/ActivityStarter.kt
deleted file mode 100644
index 90dbca9..0000000
--- a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/src/com/android/cts/reviewpermissionhelper/ActivityStarter.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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 com.android.cts.reviewpermissionhelper
-
-import android.app.Activity
-import android.content.Intent
-import android.os.Bundle
-import java.util.concurrent.LinkedBlockingQueue
-
-val installDialogResults = LinkedBlockingQueue<Int>()
-
-class ActivityStarter : Activity() {
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-
-        savedInstanceState ?: installDialogResults.clear()
-    }
-
-    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
-        installDialogResults.offer(resultCode)
-    }
-}
diff --git a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/src/com/android/cts/reviewpermissionhelper/ReviewPermissionsTest.kt b/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/src/com/android/cts/reviewpermissionhelper/ReviewPermissionsTest.kt
index 15fdff3..b07a4bb 100644
--- a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/src/com/android/cts/reviewpermissionhelper/ReviewPermissionsTest.kt
+++ b/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/src/com/android/cts/reviewpermissionhelper/ReviewPermissionsTest.kt
@@ -32,11 +32,13 @@
 import android.support.test.uiautomator.By
 import android.support.test.uiautomator.UiDevice
 import android.support.test.uiautomator.Until
+import com.android.compatibility.common.util.FutureResultActivity
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNull
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import java.util.concurrent.CompletableFuture
 import java.util.concurrent.LinkedBlockingQueue
 import java.util.concurrent.TimeUnit
 
@@ -47,16 +49,22 @@
 @RunWith(AndroidJUnit4::class)
 class ReviewPermissionsTest {
     @get:Rule
-    val activityStarter = ActivityTestRule(ActivityStarter::class.java)
+    val activityStarter = ActivityTestRule(FutureResultActivity::class.java)
 
     val instrumentation = InstrumentationRegistry.getInstrumentation()
     val uiDevice = UiDevice.getInstance(instrumentation)
 
-    fun startActivityInReviewedAp() {
+    fun startActivityInReviewedAp(): CompletableFuture<Int> {
         val startAutoClosingActivity = Intent()
         startAutoClosingActivity.component = ComponentName(USE_PERMISSION_PKG,
                 USE_PERMISSION_PKG + ".AutoClosingActivity")
-        activityStarter.activity.startActivityForResult(startAutoClosingActivity, 42)
+        return activityStarter.activity.startActivityForResult(startAutoClosingActivity)
+    }
+
+    private inline fun startActivityInReviewedAp(expectedResult: Int, runAfterStart: () -> Unit) {
+        val activityResult = startActivityInReviewedAp()
+        runAfterStart()
+        assertEquals(expectedResult, activityResult.get(UI_TIMEOUT, TimeUnit.MILLISECONDS))
     }
 
     fun clickContinue() {
@@ -66,24 +74,23 @@
 
     @Test
     fun approveReviewPermissions() {
-        startActivityInReviewedAp()
-        clickContinue()
-        assertEquals(RESULT_OK, installDialogResults.poll(UI_TIMEOUT, TimeUnit.MILLISECONDS))
+        startActivityInReviewedAp(expectedResult = RESULT_OK) {
+            clickContinue()
+        }
     }
 
     @Test
     fun cancelReviewPermissions() {
-        startActivityInReviewedAp()
-
-        uiDevice.wait(Until.findObject(
-                By.res("com.android.permissioncontroller:id/cancel_button")), UI_TIMEOUT).click()
-        assertEquals(RESULT_CANCELED, installDialogResults.poll(UI_TIMEOUT, TimeUnit.MILLISECONDS))
+        startActivityInReviewedAp(expectedResult = RESULT_CANCELED) {
+            uiDevice.wait(Until.findObject(
+                    By.res("com.android.permissioncontroller:id/cancel_button")), UI_TIMEOUT)
+                    .click()
+        }
     }
 
     @Test
     fun assertNoReviewPermissionsNeeded() {
-        startActivityInReviewedAp()
-        assertEquals(RESULT_OK, installDialogResults.poll(UI_TIMEOUT, TimeUnit.MILLISECONDS))
+        startActivityInReviewedAp(expectedResult = RESULT_OK) {}
     }
 
     @Test
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/OWNERS b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/UsePermissionTest22.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/UsePermissionTest22.java
index 20be2cc..a1bd122 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/UsePermissionTest22.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/UsePermissionTest22.java
@@ -36,7 +36,6 @@
  * Runtime permission behavior tests for apps targeting API 22
  */
 public class UsePermissionTest22 extends BasePermissionsTest {
-    private static final int REQUEST_CODE_PERMISSIONS = 42;
 
     private final Context mContext = getInstrumentation().getContext();
 
@@ -137,11 +136,9 @@
     public void testNoRuntimePrompt() throws Exception {
         // Request the permission and do nothing
         BasePermissionActivity.Result result = requestPermissions(
-                new String[] {Manifest.permission.SEND_SMS}, REQUEST_CODE_PERMISSIONS,
-                BasePermissionActivity.class, null);
+                new String[]{Manifest.permission.SEND_SMS}, null);
 
         // Expect the permission is not granted
-        assertEquals(REQUEST_CODE_PERMISSIONS, result.requestCode);
         assertTrue(Arrays.equals(result.permissions, new String[0]));
         assertTrue(Arrays.equals(result.grantResults, new int[0]));
     }
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/OWNERS b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionActivity.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionActivity.java
index cacfa80..48dafef 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionActivity.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionActivity.java
@@ -20,23 +20,33 @@
 import android.os.Bundle;
 import android.view.WindowManager;
 
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.SynchronousQueue;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 
 public class BasePermissionActivity extends Activity {
     private static final long OPERATION_TIMEOUT_MILLIS = 5000;
 
-    private final SynchronousQueue<Result> mResult = new SynchronousQueue<>();
+    /**
+     * Static to ensure correct behavior if {@link Activity} instance was recreated before
+     * result delivery.
+     *
+     * requestCode -> Future<Result>
+     */
+    private static Map<Integer, CompletableFuture<Result>> sPendingResults =
+            new ConcurrentHashMap<>();
+    private static AtomicInteger sNextRequestCode = new AtomicInteger(0);
+
     private final CountDownLatch mOnCreateSync = new CountDownLatch(1);
 
     public static class Result {
-        public final int requestCode;
         public final String[] permissions;
         public final int[] grantResults;
 
-        public Result(int requestCode, String[] permissions, int[] grantResults) {
-            this.requestCode = requestCode;
+        public Result(String[] permissions, int[] grantResults) {
             this.permissions = permissions;
             this.grantResults = grantResults;
         }
@@ -53,14 +63,18 @@
         mOnCreateSync.countDown();
     }
 
+    public CompletableFuture<Result> requestPermissions(String[] permissions) {
+        int requestCode = sNextRequestCode.getAndIncrement();
+        CompletableFuture<Result> future = new CompletableFuture<>();
+        sPendingResults.put(requestCode, future);
+        requestPermissions(permissions, requestCode);
+        return future;
+    }
+
     @Override
     public void onRequestPermissionsResult(int requestCode, String[] permissions,
             int[] grantResults) {
-        try {
-            mResult.offer(new Result(requestCode, permissions, grantResults), 5, TimeUnit.SECONDS);
-        } catch (InterruptedException e) {
-            throw new RuntimeException(e);
-        }
+        sPendingResults.get(requestCode).complete(new Result(permissions, grantResults));
     }
 
     public void waitForOnCreate() {
@@ -70,12 +84,4 @@
             throw new RuntimeException(e);
         }
     }
-
-    public Result getResult() {
-        try {
-            return mResult.poll(OPERATION_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
-        } catch (InterruptedException e) {
-            throw new RuntimeException(e);
-        }
-    }
 }
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
index b2f9f3f..e08bcf0 100755
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
@@ -16,11 +16,17 @@
 
 package com.android.cts.usepermission;
 
+import static com.android.compatibility.common.util.UiAutomatorUtils.waitFindObject;
+import static com.android.compatibility.common.util.UiAutomatorUtils.getUiDevice;
+
 import static junit.framework.Assert.assertEquals;
 
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
 import android.Manifest;
 import android.app.Activity;
 import android.app.Instrumentation;
@@ -51,6 +57,10 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.compatibility.common.util.ExceptionUtils;
+import com.android.compatibility.common.util.ThrowingRunnable;
+import com.android.compatibility.common.util.UiDumpUtils;
+
 import junit.framework.Assert;
 
 import org.junit.Before;
@@ -59,6 +69,8 @@
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.regex.Pattern;
 
@@ -83,20 +95,16 @@
     }
 
     protected static void assertPermissionRequestResult(BasePermissionActivity.Result result,
-            int requestCode, String[] permissions, boolean[] granted) {
-        assertEquals(requestCode, result.requestCode);
+            String[] permissions, boolean[] granted) {
         for (int i = 0; i < permissions.length; i++) {
             assertEquals(permissions[i], result.permissions[i]);
-            assertEquals(granted[i] ? PackageManager.PERMISSION_GRANTED
+            assertEquals(granted[i]
+                    ? PackageManager.PERMISSION_GRANTED
                     : PackageManager.PERMISSION_DENIED, result.grantResults[i]);
 
         }
     }
 
-    protected static UiDevice getUiDevice() {
-        return UiDevice.getInstance(getInstrumentation());
-    }
-
     protected static Activity launchActivity(String packageName,
             Class<?> clazz, Bundle extras) {
         Intent intent = new Intent(Intent.ACTION_MAIN);
@@ -269,74 +277,67 @@
     }
 
     protected BasePermissionActivity.Result requestPermissions(
-            String[] permissions, int requestCode, Class<?> clazz, Runnable postRequestAction)
+            String[] permissions, ThrowingRunnable postRequestAction)
             throws Exception {
-        // Start an activity
-        BasePermissionActivity activity = (BasePermissionActivity) launchActivity(
-                getInstrumentation().getTargetContext().getPackageName(), clazz, null);
+        return ExceptionUtils.wrappingExceptions(UiDumpUtils::wrapWithUiDump, () -> {
+            // Start an activity
+            BasePermissionActivity activity = (BasePermissionActivity) launchActivity(
+                    getInstrumentation().getTargetContext().getPackageName(),
+                    BasePermissionActivity.class, null);
 
-        activity.waitForOnCreate();
+            activity.waitForOnCreate();
 
-        // Request the permissions
-        activity.requestPermissions(permissions, requestCode);
+            // Request the permissions
+            CompletableFuture<BasePermissionActivity.Result> futureResult =
+                    activity.requestPermissions(permissions);
 
-        // Define a more conservative idle criteria
-        getInstrumentation().getUiAutomation().waitForIdle(
-                IDLE_TIMEOUT_MILLIS, GLOBAL_TIMEOUT_MILLIS);
+            // Define a more conservative idle criteria
+            getInstrumentation().getUiAutomation().waitForIdle(
+                    IDLE_TIMEOUT_MILLIS, GLOBAL_TIMEOUT_MILLIS);
 
-        // Perform the post-request action
-        if (postRequestAction != null) {
-            postRequestAction.run();
-        }
+            // Perform the post-request action
+            if (postRequestAction != null) {
+                postRequestAction.run();
+            }
 
-        BasePermissionActivity.Result result = activity.getResult();
-        activity.finish();
-        return result;
+            BasePermissionActivity.Result result =
+                    futureResult.get(GLOBAL_TIMEOUT_MILLIS, MILLISECONDS);
+            activity.finish();
+            return result;
+        });
     }
 
     protected void clickAllowButton() throws Exception {
-        scrollToBottomIfWatch();
-        waitForIdle();
-        getUiDevice().wait(Until.findObject(By.res(
-                "com.android.permissioncontroller:id/permission_allow_button")),
-                GLOBAL_TIMEOUT_MILLIS).click();
+        scrollToBottom();
+        click("com.android.permissioncontroller:id/permission_allow_button");
     }
 
     protected void clickAllowAlwaysButton() throws Exception {
-        waitForIdle();
-        getUiDevice().wait(Until.findObject(By.res(
-                "com.android.permissioncontroller:id/permission_allow_always_button")),
-                GLOBAL_TIMEOUT_MILLIS).click();
+        click("com.android.permissioncontroller:id/permission_allow_always_button");
     }
 
     protected void clickAllowForegroundButton() throws Exception {
-        waitForIdle();
-        getUiDevice().wait(Until.findObject(By.res(
-                "com.android.permissioncontroller:id/permission_allow_foreground_only_button")),
-                GLOBAL_TIMEOUT_MILLIS).click();
+        click("com.android.permissioncontroller:id/permission_allow_foreground_only_button");
     }
 
     protected void clickDenyButton() throws Exception {
-        scrollToBottomIfWatch();
-        waitForIdle();
-        getUiDevice().wait(Until.findObject(By.res(
-                "com.android.permissioncontroller:id/permission_deny_button")),
-                GLOBAL_TIMEOUT_MILLIS).click();
+        scrollToBottom();
+        click("com.android.permissioncontroller:id/permission_deny_button");
     }
 
     protected void clickDenyAndDontAskAgainButton() throws Exception {
-        waitForIdle();
-        getUiDevice().wait(Until.findObject(By.res(
-                "com.android.permissioncontroller:id/permission_deny_and_dont_ask_again_button")),
-                GLOBAL_TIMEOUT_MILLIS).click();
+        scrollToBottom();
+        click("com.android.permissioncontroller:id/permission_deny_and_dont_ask_again_button");
     }
 
     protected void clickDontAskAgainButton() throws Exception {
-        scrollToBottomIfWatch();
+        scrollToBottom();
+        click("com.android.permissioncontroller:id/permission_deny_dont_ask_again_button");
+    }
+
+    private void click(String resourceName) throws TimeoutException {
         waitForIdle();
-        getUiDevice().wait(Until.findObject(By.res(
-                "com.android.permissioncontroller:id/permission_deny_dont_ask_again_button")),
-                GLOBAL_TIMEOUT_MILLIS).click();
+        waitFindObject(By.res(resourceName)).click();
     }
 
     protected void grantPermission(String permission) throws Exception {
@@ -355,116 +356,103 @@
         setPermissionGrantState(permissions, false, legacyApp);
     }
 
-    private void scrollToBottomIfWatch() throws Exception {
-        if (mWatch) {
-            getUiDevice().wait(Until.findObject(By.clazz(ScrollView.class)), GLOBAL_TIMEOUT_MILLIS);
-            UiScrollable scrollable =
-                    new UiScrollable(new UiSelector().className(ScrollView.class));
-            if (scrollable.exists()) {
-                scrollable.flingToEnd(10);
-            }
+    private void scrollToBottom() throws Exception {
+        waitFindObject(By.clazz(ScrollView.class));
+        UiScrollable scrollable =
+                new UiScrollable(new UiSelector().className(ScrollView.class));
+        if (scrollable.exists()) {
+            scrollable.flingToEnd(10);
         }
     }
 
     private void setPermissionGrantState(String[] permissions, boolean granted,
             boolean legacyApp) throws Exception {
-        getUiDevice().pressBack();
-        waitForIdle();
-        getUiDevice().pressBack();
-        waitForIdle();
-        getUiDevice().pressBack();
-        waitForIdle();
-
-        if (isTv()) {
-            getUiDevice().pressHome();
+        ExceptionUtils.wrappingExceptions(UiDumpUtils::wrapWithUiDump, () -> {
+            getUiDevice().pressBack();
             waitForIdle();
-        }
+            getUiDevice().pressBack();
+            waitForIdle();
+            getUiDevice().pressBack();
+            waitForIdle();
 
-        // Open the app details settings
-        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
-        intent.addCategory(Intent.CATEGORY_DEFAULT);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        intent.setData(Uri.parse("package:" + mContext.getPackageName()));
-        startActivity(intent);
-
-        waitForIdle();
-
-        // Open the permissions UI
-        String label = mContext.getResources().getString(R.string.Permissions);
-        AccessibilityNodeInfo permLabelView = getNodeTimed(() -> findByText(label), true);
-        Assert.assertNotNull("Permissions label should be present", permLabelView);
-
-        AccessibilityNodeInfo permItemView = findCollectionItem(permLabelView);
-
-        click(permItemView);
-
-        waitForIdle();
-
-        for (String permission : permissions) {
-            // Find the permission screen
-            String permissionLabel = getPermissionLabel(permission);
-
-            UiObject2 permissionView = null;
-            long start = System.currentTimeMillis();
-            while (permissionView == null && start + RETRY_TIMEOUT > System.currentTimeMillis()) {
-                permissionView = getUiDevice().wait(Until.findObject(By.text(permissionLabel)),
-                        IDLE_TIMEOUT_MILLIS);
-
-                if (permissionView == null) {
-                    getUiDevice().findObject(By.scrollable(true))
-                            .scroll(Direction.DOWN, 1);
-                }
+            if (isTv()) {
+                getUiDevice().pressHome();
+                waitForIdle();
             }
 
-            permissionView.click();
+            // Open the app details settings
+            Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+            intent.addCategory(Intent.CATEGORY_DEFAULT);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            intent.setData(Uri.parse("package:" + mContext.getPackageName()));
+            startActivity(intent);
+
             waitForIdle();
 
-            String denyLabel = mContext.getResources().getString(R.string.Deny);
+            // Open the permissions UI
+            String label = mContext.getResources().getString(R.string.Permissions);
+            AccessibilityNodeInfo permLabelView = getNodeTimed(() -> findByText(label), true);
+            Assert.assertNotNull("Permissions label should be present", permLabelView);
 
-            final boolean wasGranted = !getUiDevice().wait(Until.findObject(By.text(denyLabel)),
-                    GLOBAL_TIMEOUT_MILLIS).isChecked();
-            if (granted != wasGranted) {
-                // Toggle the permission
+            AccessibilityNodeInfo permItemView = findCollectionItem(permLabelView);
 
-                if (granted) {
-                    String allowLabel = mContext.getResources().getString(R.string.Allow);
-                    getUiDevice().findObject(By.text(allowLabel)).click();
-                } else {
-                    getUiDevice().findObject(By.text(denyLabel)).click();
-                }
+            click(permItemView);
+
+            waitForIdle();
+
+            for (String permission : permissions) {
+                // Find the permission screen
+                String permissionLabel = getPermissionLabel(permission);
+
+                waitFindObject(By.text(permissionLabel)).click();
                 waitForIdle();
 
-                if (wasGranted && legacyApp) {
-                    scrollToBottomIfWatch();
-                    Context context = getInstrumentation().getContext();
-                    String packageName = context.getPackageManager()
-                            .getPermissionControllerPackageName();
-                    String resIdName = "com.android.permissioncontroller"
-                            + ":string/grant_dialog_button_deny_anyway";
-                    Resources resources = context
-                            .createPackageContext(packageName, 0).getResources();
-                    final int confirmResId = resources.getIdentifier(resIdName, null, null);
-                    String confirmTitle = CaseMap.toUpper().apply(
-                            resources.getConfiguration().getLocales().get(0),
-                            resources.getString(confirmResId));
-                    getUiDevice().wait(Until.findObject(
-                            byTextStartsWithCaseInsensitive(confirmTitle)),
-                            GLOBAL_TIMEOUT_MILLIS).click();
+                final boolean wasGranted = !waitFindObject(byText(R.string.Deny)).isChecked();
+                if (granted != wasGranted) {
+                    // Toggle the permission
 
+                    if (granted) {
+                        waitFindObject(byText(R.string.Allow)).click();
+                    } else {
+                        waitFindObject(byText(R.string.Deny)).click();
+                    }
                     waitForIdle();
+
+                    if (wasGranted && legacyApp) {
+                        scrollToBottom();
+                        Context context = getInstrumentation().getContext();
+                        String packageName = context.getPackageManager()
+                                .getPermissionControllerPackageName();
+                        String resIdName = "com.android.permissioncontroller"
+                                + ":string/grant_dialog_button_deny_anyway";
+                        Resources resources = context
+                                .createPackageContext(packageName, 0).getResources();
+                        final int confirmResId = resources.getIdentifier(resIdName, null, null);
+                        String confirmTitle = resources.getString(confirmResId);
+
+                        waitFindObject(byTextStartsWithCaseInsensitive(confirmTitle))
+                                .click();
+                        waitForIdle();
+                    }
                 }
+
+                getUiDevice().pressBack();
+                waitForIdle();
             }
 
             getUiDevice().pressBack();
             waitForIdle();
-        }
-
-        getUiDevice().pressBack();
-        waitForIdle();
-        getUiDevice().pressBack();
-        waitForIdle();
+            getUiDevice().pressBack();
+            waitForIdle();
+        });
     }
 
+    private BySelector byText(int stringId) {
+        return By.text(mContext.getResources().getString(stringId));
+    }
+
+
+
     private BySelector byTextStartsWithCaseInsensitive(String prefix) {
         return By.text(Pattern.compile(String.format("(?i)^%s.*$", Pattern.quote(prefix))));
     }
@@ -500,6 +488,9 @@
     }
 
     private static AccessibilityNodeInfo findByText(AccessibilityNodeInfo root, String text) {
+        if (root == null) {
+            return null;
+        }
         List<AccessibilityNodeInfo> nodes = root.findAccessibilityNodeInfosByText(text);
         for (AccessibilityNodeInfo node : nodes) {
             if (node.getText().toString().equals(text)) {
@@ -587,12 +578,14 @@
     }
 
     private static void click(AccessibilityNodeInfo node) throws Exception {
-        getInstrumentation().getUiAutomation().executeAndWaitForEvent(
-                () -> node.performAction(AccessibilityNodeInfo.ACTION_CLICK),
-                (AccessibilityEvent event) -> event.getEventType()
-                        == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
-                        || event.getEventType() == AccessibilityEvent.TYPE_WINDOWS_CHANGED,
-                GLOBAL_TIMEOUT_MILLIS);
+        ExceptionUtils.wrappingExceptions(UiDumpUtils::wrapWithUiDump, () -> {
+            getInstrumentation().getUiAutomation().executeAndWaitForEvent(
+                    () -> node.performAction(AccessibilityNodeInfo.ACTION_CLICK),
+                    (AccessibilityEvent event) -> event.getEventType()
+                            == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
+                            || event.getEventType() == AccessibilityEvent.TYPE_WINDOWS_CHANGED,
+                    GLOBAL_TIMEOUT_MILLIS);
+        });
     }
 
     private static AccessibilityNodeInfo findCollectionItem(AccessibilityNodeInfo current)
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/UsePermissionTest23.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/UsePermissionTest23.java
index 9482df0..3dde0fa 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/UsePermissionTest23.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/UsePermissionTest23.java
@@ -16,6 +16,7 @@
 
 package com.android.cts.usepermission;
 
+import static com.android.compatibility.common.util.UiAutomatorUtils.getUiDevice;
 import static junit.framework.Assert.assertEquals;
 
 import static org.junit.Assert.fail;
@@ -26,7 +27,6 @@
 import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.net.Uri;
-import android.os.Build;
 import android.provider.CalendarContract;
 
 import androidx.test.InstrumentationRegistry;
@@ -41,7 +41,6 @@
  * Runtime permission behavior tests for apps targeting API 23
  */
 public class UsePermissionTest23 extends BasePermissionsTest {
-    private static final int REQUEST_CODE_PERMISSIONS = 42;
 
     private final Context mContext = getInstrumentation().getContext();
 
@@ -105,21 +104,13 @@
         }
 
         // Go through normal grant flow
-        BasePermissionActivity.Result result = requestPermissions(new String[] {
+        BasePermissionActivity.Result result = requestPermissions(new String[]{
                 Manifest.permission.READ_CALENDAR,
-                Manifest.permission.WRITE_CALENDAR},
-                REQUEST_CODE_PERMISSIONS,
-                BasePermissionActivity.class,
-                () -> {
-                    try {
-                        clickAllowButton();
-                        getUiDevice().waitForIdle();
-                    } catch (Exception e) {
-                        throw new RuntimeException(e);
-                    }
-                });
+                Manifest.permission.WRITE_CALENDAR}, () -> {
+            clickAllowButton();
+            getUiDevice().waitForIdle();
+        });
 
-        assertEquals(REQUEST_CODE_PERMISSIONS, result.requestCode);
         assertEquals(Manifest.permission.READ_CALENDAR, result.permissions[0]);
         assertEquals(Manifest.permission.WRITE_CALENDAR, result.permissions[1]);
         assertEquals(PackageManager.PERMISSION_GRANTED, result.grantResults[0]);
@@ -147,21 +138,13 @@
         String[] permissions = new String[] {Manifest.permission.WRITE_CONTACTS};
 
         // request only one permission from the 'contacts' permission group
-        BasePermissionActivity.Result result = requestPermissions(permissions,
-                REQUEST_CODE_PERMISSIONS,
-                BasePermissionActivity.class,
-                () -> {
-                    try {
-                        clickAllowButton();
-                        getUiDevice().waitForIdle();
-                    } catch (Exception e) {
-                        throw new RuntimeException(e);
-                    }
-                });
+        BasePermissionActivity.Result result = requestPermissions(permissions, () -> {
+            clickAllowButton();
+            getUiDevice().waitForIdle();
+        });
 
         // Expect the permission is granted
-        assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
-                permissions, new boolean[] {true});
+        assertPermissionRequestResult(result, permissions, new boolean[] {true});
 
         // Make sure no undeclared as used permissions are granted
         assertEquals(PackageManager.PERMISSION_DENIED, getInstrumentation().getContext()
@@ -181,21 +164,13 @@
         // request only one permission from the 'SMS' permission group at runtime,
         // but two from this group are <uses-permission> in the manifest
         // request only one permission from the 'contacts' permission group
-        BasePermissionActivity.Result result = requestPermissions(permissions,
-                REQUEST_CODE_PERMISSIONS,
-                BasePermissionActivity.class,
-                () -> {
-                    try {
-                        clickAllowButton();
-                        getUiDevice().waitForIdle();
-                    } catch (Exception e) {
-                        throw new RuntimeException(e);
-                    }
-                });
+        BasePermissionActivity.Result result = requestPermissions(permissions, () -> {
+            clickAllowButton();
+            getUiDevice().waitForIdle();
+        });
 
         // Expect the permission is granted
-        assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
-                permissions, new boolean[] {true});
+        assertPermissionRequestResult(result, permissions, new boolean[] {true});
 
         // We should now have been granted both of the permissions from this group.
         assertEquals(PackageManager.PERMISSION_GRANTED, getInstrumentation().getContext()
@@ -211,21 +186,13 @@
         String[] permissions = new String[] {Manifest.permission.WRITE_CONTACTS};
 
         // Request the permission and cancel the request
-        BasePermissionActivity.Result result = requestPermissions(permissions,
-                REQUEST_CODE_PERMISSIONS,
-                BasePermissionActivity.class,
-                () -> {
-                    try {
-                        clickDenyButton();
-                        getUiDevice().waitForIdle();
-                    } catch (Exception e) {
-                        throw new RuntimeException(e);
-                    }
-                });
+        BasePermissionActivity.Result result = requestPermissions(permissions, () -> {
+            clickDenyButton();
+            getUiDevice().waitForIdle();
+        });
 
         // Expect the permission is not granted
-        assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
-                permissions, new boolean[] {false});
+        assertPermissionRequestResult(result, permissions, new boolean[] {false});
     }
 
     @Test
@@ -237,29 +204,20 @@
         String[] permissions = new String[] {Manifest.permission.WRITE_CONTACTS};
 
         // Request the permission and allow it
-        BasePermissionActivity.Result firstResult = requestPermissions(permissions,
-                REQUEST_CODE_PERMISSIONS,
-                BasePermissionActivity.class, () -> {
-                    try {
-                        clickAllowButton();
-                        getUiDevice().waitForIdle();
-                    } catch (Exception e) {
-                        throw new RuntimeException(e);
-                    }
-                });
+        BasePermissionActivity.Result firstResult = requestPermissions(permissions, () -> {
+            clickAllowButton();
+            getUiDevice().waitForIdle();
+        });
 
         // Expect the permission is granted
-        assertPermissionRequestResult(firstResult, REQUEST_CODE_PERMISSIONS,
-                permissions, new boolean[] {true});
+        assertPermissionRequestResult(firstResult, permissions, new boolean[] {true});
 
         // Request the permission and do nothing
-        BasePermissionActivity.Result secondResult = requestPermissions(new String[] {
-                Manifest.permission.WRITE_CONTACTS}, REQUEST_CODE_PERMISSIONS + 1,
-                BasePermissionActivity.class, null);
+        BasePermissionActivity.Result secondResult = requestPermissions(new String[]{
+                Manifest.permission.WRITE_CONTACTS}, null);
 
         // Expect the permission is granted
-        assertPermissionRequestResult(secondResult, REQUEST_CODE_PERMISSIONS + 1,
-                permissions, new boolean[] {true});
+        assertPermissionRequestResult(secondResult, permissions, new boolean[] {true});
     }
 
     @Test
@@ -271,45 +229,30 @@
         String[] permissions = new String[] {Manifest.permission.WRITE_CONTACTS};
 
         // Request the permission and deny it
-        BasePermissionActivity.Result firstResult = requestPermissions(
-                permissions, REQUEST_CODE_PERMISSIONS,
-                BasePermissionActivity.class, () -> {
-                    try {
-                        clickDenyButton();
-                        getUiDevice().waitForIdle();
-                    } catch (Exception e) {
-                        throw new RuntimeException(e);
-                    }
-                });
+        BasePermissionActivity.Result firstResult = requestPermissions(permissions, () -> {
+            clickDenyButton();
+            getUiDevice().waitForIdle();
+        });
 
         // Expect the permission is not granted
-        assertPermissionRequestResult(firstResult, REQUEST_CODE_PERMISSIONS,
-                permissions, new boolean[] {false});
+        assertPermissionRequestResult(firstResult, permissions, new boolean[] {false});
 
         // Request the permission and choose don't ask again
-        BasePermissionActivity.Result secondResult = requestPermissions(new String[] {
-                        Manifest.permission.WRITE_CONTACTS}, REQUEST_CODE_PERMISSIONS + 1,
-                BasePermissionActivity.class, () -> {
-                    try {
-                        denyWithPrejudice();
-                        getUiDevice().waitForIdle();
-                    } catch (Exception e) {
-                        throw new RuntimeException(e);
-                    }
-                });
+        BasePermissionActivity.Result secondResult = requestPermissions(new String[]{
+                Manifest.permission.WRITE_CONTACTS}, () -> {
+            denyWithPrejudice();
+            getUiDevice().waitForIdle();
+        });
 
         // Expect the permission is not granted
-        assertPermissionRequestResult(secondResult, REQUEST_CODE_PERMISSIONS + 1,
-                permissions, new boolean[] {false});
+        assertPermissionRequestResult(secondResult, permissions, new boolean[] {false});
 
         // Request the permission and do nothing
-        BasePermissionActivity.Result thirdResult = requestPermissions(new String[] {
-                        Manifest.permission.WRITE_CONTACTS}, REQUEST_CODE_PERMISSIONS + 2,
-                BasePermissionActivity.class, null);
+        BasePermissionActivity.Result thirdResult = requestPermissions(new String[]{
+                Manifest.permission.WRITE_CONTACTS}, null);
 
         // Expect the permission is not granted
-        assertPermissionRequestResult(thirdResult, REQUEST_CODE_PERMISSIONS + 2,
-                permissions, new boolean[] {false});
+        assertPermissionRequestResult(thirdResult, permissions, new boolean[] {false});
     }
 
     @Test
@@ -347,36 +290,23 @@
         String[] permissions = new String[] {Manifest.permission.READ_CALENDAR};
 
         // Request the permission and deny it
-        BasePermissionActivity.Result firstResult = requestPermissions(
-                permissions, REQUEST_CODE_PERMISSIONS,
-                BasePermissionActivity.class, () -> {
-                    try {
-                        clickDenyButton();
-                        getUiDevice().waitForIdle();
-                    } catch (Exception e) {
-                        throw new RuntimeException(e);
-                    }
-                });
+        BasePermissionActivity.Result firstResult = requestPermissions(permissions, () -> {
+            clickDenyButton();
+            getUiDevice().waitForIdle();
+        });
 
         // Expect the permission is not granted
-        assertPermissionRequestResult(firstResult, REQUEST_CODE_PERMISSIONS,
-                permissions, new boolean[] {false});
+        assertPermissionRequestResult(firstResult, permissions, new boolean[] {false});
 
         // Request the permission and choose don't ask again
-        BasePermissionActivity.Result secondResult = requestPermissions(new String[] {
-                        Manifest.permission.READ_CALENDAR}, REQUEST_CODE_PERMISSIONS + 1,
-                BasePermissionActivity.class, () -> {
-                    try {
-                        denyWithPrejudice();
-                        getUiDevice().waitForIdle();
-                    } catch (Exception e) {
-                        throw new RuntimeException(e);
-                    }
-                });
+        BasePermissionActivity.Result secondResult = requestPermissions(new String[]{
+                Manifest.permission.READ_CALENDAR}, () -> {
+            denyWithPrejudice();
+            getUiDevice().waitForIdle();
+        });
 
         // Expect the permission is not granted
-        assertPermissionRequestResult(secondResult, REQUEST_CODE_PERMISSIONS + 1,
-                permissions, new boolean[] {false});
+        assertPermissionRequestResult(secondResult, permissions, new boolean[] {false});
 
         // Clear the denial with prejudice
         grantPermission(Manifest.permission.READ_CALENDAR);
@@ -392,20 +322,15 @@
                 .checkSelfPermission(Manifest.permission.READ_CALENDAR));
 
         // Request the permission and allow it
-        BasePermissionActivity.Result thirdResult = requestPermissions(new String[] {
-                        Manifest.permission.READ_CALENDAR}, REQUEST_CODE_PERMISSIONS + 2,
-                BasePermissionActivity.class, () -> {
-                    try {
-                        clickAllowButton();
-                        getUiDevice().waitForIdle();
-                    } catch (Exception e) {
-                        throw new RuntimeException(e);
-                    }
-                });
+        BasePermissionActivity.Result thirdResult = requestPermissions(new String[]{
+                Manifest.permission.READ_CALENDAR}, () -> {
+            clickAllowButton();
+            getUiDevice().waitForIdle();
+        });
 
         // Make sure the permission is granted
-        assertPermissionRequestResult(thirdResult, REQUEST_CODE_PERMISSIONS + 2,
-                new String[] {Manifest.permission.READ_CALENDAR}, new boolean[] {true});
+        assertPermissionRequestResult(thirdResult, new String[] {Manifest.permission.READ_CALENDAR},
+                new boolean[] {true});
     }
 
     @Test
@@ -417,12 +342,10 @@
         String[] permissions = new String[] {Manifest.permission.BIND_PRINT_SERVICE};
 
         // Request the permission and do nothing
-        BasePermissionActivity.Result result = requestPermissions(permissions,
-                REQUEST_CODE_PERMISSIONS, BasePermissionActivity.class, null);
+        BasePermissionActivity.Result result = requestPermissions(permissions, null);
 
         // Expect the permission is not granted
-        assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
-                permissions, new boolean[] {false});
+        assertPermissionRequestResult(result, permissions, new boolean[] {false});
     }
 
     @Test
@@ -434,12 +357,10 @@
         String[] permissions = new String[] {"permission.does.not.exist"};
 
         // Request the permission and do nothing
-        BasePermissionActivity.Result result = requestPermissions(permissions,
-                REQUEST_CODE_PERMISSIONS, BasePermissionActivity.class, null);
+        BasePermissionActivity.Result result = requestPermissions(permissions, null);
 
         // Expect the permission is not granted
-        assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
-                permissions, new boolean[] {false});
+        assertPermissionRequestResult(result, permissions, new boolean[] {false});
     }
 
     @Test
@@ -456,8 +377,7 @@
         };
 
         // Request the permission and allow it
-        BasePermissionActivity.Result result = requestPermissions(permissions,
-                REQUEST_CODE_PERMISSIONS, BasePermissionActivity.class, () -> {
+        BasePermissionActivity.Result result = requestPermissions(permissions, () -> {
             try {
                 clickAllowButton();
                 getUiDevice().waitForIdle();
@@ -469,8 +389,7 @@
         });
 
         // Expect the permission are reported as granted
-        assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
-                permissions, new boolean[] {true, true});
+        assertPermissionRequestResult(result, permissions, new boolean[] {true, true});
 
         // The permissions are granted
         assertEquals(PackageManager.PERMISSION_GRANTED, getInstrumentation().getContext()
@@ -534,20 +453,19 @@
                 .checkSelfPermission(Manifest.permission.READ_CALENDAR));
 
         // Request the permission and allow it
-        BasePermissionActivity.Result thirdResult = requestPermissions(new String[] {
-                        Manifest.permission.READ_CALENDAR}, REQUEST_CODE_PERMISSIONS,
-                BasePermissionActivity.class, () -> {
-                    try {
-                        clickAllowButton();
-                        getUiDevice().waitForIdle();
-                    } catch (Exception e) {
-                        throw new RuntimeException(e);
-                    }
-                });
+        BasePermissionActivity.Result thirdResult = requestPermissions(new String[]{
+                Manifest.permission.READ_CALENDAR}, () -> {
+            try {
+                clickAllowButton();
+                getUiDevice().waitForIdle();
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        });
 
         // Make sure the permission is granted
-        assertPermissionRequestResult(thirdResult, REQUEST_CODE_PERMISSIONS,
-                new String[] {Manifest.permission.READ_CALENDAR}, new boolean[] {true});
+        assertPermissionRequestResult(thirdResult, new String[] {Manifest.permission.READ_CALENDAR},
+                new boolean[] {true});
     }
 
     @Test
@@ -571,12 +489,9 @@
 
         // Go through normal grant flow
         BasePermissionActivity.Result result = requestPermissions(permissions,
-                REQUEST_CODE_PERMISSIONS,
-                BasePermissionActivity.class,
                 () -> { /* empty */ });
 
-        assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
-                permissions, new boolean[] {false});
+        assertPermissionRequestResult(result, permissions, new boolean[] {false});
     }
 
     @Test
@@ -595,21 +510,19 @@
         };
 
         // Request the permission and allow it
-        BasePermissionActivity.Result result = requestPermissions(permissions,
-                REQUEST_CODE_PERMISSIONS, BasePermissionActivity.class, () -> {
-                    try {
-                        clickAllowButton();
-                        getUiDevice().waitForIdle();
-                        clickAllowButton();
-                        getUiDevice().waitForIdle();
-                    } catch (Exception e) {
-                        throw new RuntimeException(e);
-                    }
-                });
+        BasePermissionActivity.Result result = requestPermissions(permissions, () -> {
+            try {
+                clickAllowButton();
+                getUiDevice().waitForIdle();
+                clickAllowButton();
+                getUiDevice().waitForIdle();
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        });
 
         // Expect the permission are reported as granted
-        assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
-                permissions, new boolean[] {false, true, false, true});
+        assertPermissionRequestResult(result, permissions, new boolean[] {false, true, false, true});
 
         // The permissions are granted
         assertEquals(PackageManager.PERMISSION_GRANTED, getInstrumentation().getContext()
@@ -626,13 +539,10 @@
 
         // Request the permission and allow it
         BasePermissionActivity.Result result = requestPermissions(permissions,
-                REQUEST_CODE_PERMISSIONS,
-                BasePermissionActivity.class,
                 () -> { /* empty */ });
 
         // Expect the permissions is not granted
-        assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
-                permissions, new boolean[] {false});
+        assertPermissionRequestResult(result, permissions, new boolean[] {false});
     }
 
     private void assertAllPermissionsRevoked() {
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp25/OWNERS b/hostsidetests/appsecurity/test-apps/UsePermissionApp25/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp25/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp26/OWNERS b/hostsidetests/appsecurity/test-apps/UsePermissionApp26/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp26/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp26/src/com/android/cts/usepermission/UsePermissionTest26.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp26/src/com/android/cts/usepermission/UsePermissionTest26.java
index e64838b..81b334e 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp26/src/com/android/cts/usepermission/UsePermissionTest26.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp26/src/com/android/cts/usepermission/UsePermissionTest26.java
@@ -16,6 +16,7 @@
 
 package com.android.cts.usepermission;
 
+import static com.android.compatibility.common.util.UiAutomatorUtils.getUiDevice;
 import static junit.framework.Assert.assertEquals;
 
 import android.Manifest;
@@ -42,21 +43,17 @@
         // request only one permission from the 'SMS' permission group at runtime,
         // but two from this group are <uses-permission> in the manifest
         // request only one permission from the 'contacts' permission group
-        BasePermissionActivity.Result result = requestPermissions(permissions,
-                REQUEST_CODE_PERMISSIONS,
-                BasePermissionActivity.class,
-                () -> {
-                    try {
-                        clickAllowButton();
-                        getUiDevice().waitForIdle();
-                    } catch (Exception e) {
-                        throw new RuntimeException(e);
-                    }
-                });
+        BasePermissionActivity.Result result = requestPermissions(permissions, () -> {
+            try {
+                clickAllowButton();
+                getUiDevice().waitForIdle();
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        });
 
         // Expect the permission is granted
-        assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
-                permissions, new boolean[]{true});
+        assertPermissionRequestResult(result, permissions, new boolean[]{true});
 
         assertEquals(PackageManager.PERMISSION_DENIED, getInstrumentation().getTargetContext()
                 .checkSelfPermission(Manifest.permission.SEND_SMS));
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp28/OWNERS b/hostsidetests/appsecurity/test-apps/UsePermissionApp28/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp28/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp28/src/com/android/cts/usepermission/UsePermissionTest28.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp28/src/com/android/cts/usepermission/UsePermissionTest28.java
index 1c32d39..4dd5349 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp28/src/com/android/cts/usepermission/UsePermissionTest28.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp28/src/com/android/cts/usepermission/UsePermissionTest28.java
@@ -23,9 +23,7 @@
 
 import static junit.framework.Assert.assertEquals;
 
-import android.Manifest;
 import android.content.Context;
-import android.content.pm.PackageManager;
 
 import org.junit.Test;
 
@@ -46,19 +44,15 @@
 
         // request only foreground permission. This should automatically also add the background
         // permission
-        BasePermissionActivity.Result result = requestPermissions(permissions,
-                REQUEST_CODE_PERMISSIONS,
-                BasePermissionActivity.class,
-                () -> {
-                    try {
-                        clickAllowAlwaysButton();
-                    } catch (Exception e) {
-                        throw new RuntimeException(e);
-                    }
-                });
+        BasePermissionActivity.Result result = requestPermissions(permissions, () -> {
+            try {
+                clickAllowAlwaysButton();
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        });
 
-        assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS, permissions,
-                new boolean[]{true});
+        assertPermissionRequestResult(result, permissions, new boolean[]{true});
 
         assertEquals(PERMISSION_GRANTED, context.checkSelfPermission(ACCESS_FINE_LOCATION));
         assertEquals(PERMISSION_GRANTED, context.checkSelfPermission(ACCESS_BACKGROUND_LOCATION));
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp29/OWNERS b/hostsidetests/appsecurity/test-apps/UsePermissionApp29/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp29/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp29/src/com/android/cts/usepermission/UsePermissionTest29.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp29/src/com/android/cts/usepermission/UsePermissionTest29.java
index e0e21eb..3ced1c4 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp29/src/com/android/cts/usepermission/UsePermissionTest29.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp29/src/com/android/cts/usepermission/UsePermissionTest29.java
@@ -21,6 +21,8 @@
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
+import static com.android.compatibility.common.util.UiAutomatorUtils.getUiDevice;
+
 import static junit.framework.Assert.assertEquals;
 
 import org.junit.Before;
@@ -48,25 +50,21 @@
 
     private BasePermissionActivity.Result requestPermissions(String[] permissions,
             UiInteraction... uiInteractions) throws Exception {
-        return super.requestPermissions(permissions,
-                REQUEST_CODE_PERMISSIONS,
-                BasePermissionActivity.class,
-                () -> {
-                    try {
-                        for (UiInteraction uiInteraction : uiInteractions) {
-                            uiInteraction.run();
-                            getUiDevice().waitForIdle();
-                        }
-                    } catch (Exception e) {
-                        throw new RuntimeException(e);
-                    }
-                });
+        return requestPermissions(permissions, () -> {
+            try {
+                for (UiInteraction uiInteraction : uiInteractions) {
+                    uiInteraction.run();
+                    getUiDevice().waitForIdle();
+                }
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        });
     }
 
-    private static void assertPermissionRequestResult(BasePermissionActivity.Result result,
+    protected static void assertPermissionRequestResult(BasePermissionActivity.Result result,
             String[] permissions, boolean... granted) {
-        BasePermissionsTest.assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
-                permissions, granted);
+        BasePermissionsTest.assertPermissionRequestResult(result, permissions, granted);
     }
 
     @Before
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/Android.mk
index 6becbc8..cddfe17 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/Android.mk
@@ -23,7 +23,8 @@
     androidx.test.rules \
     compatibility-device-util-axt \
     ctstestrunner-axt \
-    ub-uiautomator
+    ub-uiautomator \
+    compatibility-device-util-axt \
 
 LOCAL_SRC_FILES := $(call all-java-files-under, ../UsePermissionApp26/src)  \
     $(call all-java-files-under, ../UsePermissionApp29/src)  \
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/OWNERS b/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml
index e587e09..18f6096 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml
@@ -35,6 +35,7 @@
         <activity android:name=".ReceiveUriActivity" android:exported="true"
                 android:launchMode="singleTop" />
         <service android:name=".ReceiveUriService" android:exported="true" />
+        <activity android:name=".GetResultActivity" android:exported="true" />
     </application>
 
     <instrumentation android:targetPackage="com.android.cts.usespermissiondiffcertapp"
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/OWNERS b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
index 30743c9..d999895 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
@@ -16,38 +16,26 @@
 
 package com.android.cts.usespermissiondiffcertapp;
 
-import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_CLEAR_PRIMARY_CLIP;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_GRANT_URI;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_REVOKE_URI;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_SET_PRIMARY_CLIP;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_START_ACTIVITY;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_START_SERVICE;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_VERIFY_OUTGOING_PERSISTED;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.EXTRA_INTENT;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.EXTRA_MODE;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.EXTRA_PACKAGE_NAME;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.EXTRA_URI;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertContentUriAllowed;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertContentUriNotAllowed;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertReadingContentUriAllowed;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertReadingContentUriNotAllowed;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertWritingContentUriAllowed;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertWritingContentUriNotAllowed;
 
-import android.content.ClipData;
-import android.content.ClipboardManager;
-import android.content.ContentResolver;
-import android.content.ContentValues;
+import static junit.framework.Assert.assertEquals;
+
+import android.content.Context;
 import android.content.Intent;
-import android.content.UriPermission;
-import android.database.Cursor;
 import android.net.Uri;
-import android.os.Bundle;
 import android.provider.CalendarContract;
 import android.provider.ContactsContract;
-import android.test.AndroidTestCase;
-import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
 
-import com.android.cts.permissiondeclareapp.UtilsProvider;
-
-import java.io.IOException;
-import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Tests that signature-enforced permissions cannot be accessed by apps signed
@@ -55,209 +43,79 @@
  * 
  * Accesses app cts/tests/appsecurity-tests/test-apps/PermissionDeclareApp/...
  */
-public class AccessPermissionWithDiffSigTest extends AndroidTestCase {
-    private static final Uri PERM_URI = Uri.parse("content://ctspermissionwithsignature");
-    private static final Uri PERM_URI_GRANTING = Uri.parse("content://ctspermissionwithsignaturegranting");
-    private static final Uri PERM_URI_PATH = Uri.parse("content://ctspermissionwithsignaturepath");
-    private static final Uri PERM_URI_PATH_RESTRICTING = Uri.parse(
+@RunWith(AndroidJUnit4.class)
+public class AccessPermissionWithDiffSigTest {
+    private static Context getContext() {
+        return InstrumentationRegistry.getTargetContext();
+    }
+
+    static final Uri PERM_URI = Uri.parse("content://ctspermissionwithsignature");
+    static final Uri PERM_URI_GRANTING = Uri.parse("content://ctspermissionwithsignaturegranting");
+    static final Uri PERM_URI_PATH = Uri.parse("content://ctspermissionwithsignaturepath");
+    static final Uri PERM_URI_PATH_RESTRICTING = Uri.parse(
             "content://ctspermissionwithsignaturepathrestricting");
-    private static final Uri PRIV_URI = Uri.parse("content://ctsprivateprovider");
-    private static final Uri PRIV_URI_GRANTING = Uri.parse("content://ctsprivateprovidergranting");
-    private static final String EXPECTED_MIME_TYPE = "got/theMIME";
+    static final Uri PRIV_URI = Uri.parse("content://ctsprivateprovider");
+    static final Uri PRIV_URI_GRANTING = Uri.parse("content://ctsprivateprovidergranting");
+    static final String EXPECTED_MIME_TYPE = "got/theMIME";
 
-    private static final Uri AMBIGUOUS_URI_COMPAT = Uri.parse("content://ctsambiguousprovidercompat");
-    private static final String EXPECTED_MIME_TYPE_AMBIGUOUS = "got/theUnspecifiedMIME";
-    private static final Uri AMBIGUOUS_URI = Uri.parse("content://ctsambiguousprovider");
+    static final Uri AMBIGUOUS_URI_COMPAT = Uri.parse("content://ctsambiguousprovidercompat");
+    static final String EXPECTED_MIME_TYPE_AMBIGUOUS = "got/theUnspecifiedMIME";
+    static final Uri AMBIGUOUS_URI = Uri.parse("content://ctsambiguousprovider");
 
-    private static final Uri[] GRANTABLE = new Uri[] {
+    static final Uri[] GRANTABLE = new Uri[] {
             Uri.withAppendedPath(PERM_URI_GRANTING, "foo"),
-            Uri.withAppendedPath(PRIV_URI_GRANTING, "foo"),
             Uri.withAppendedPath(PERM_URI_PATH, "foo"),
+            Uri.withAppendedPath(PRIV_URI_GRANTING, "foo"),
     };
 
-    private static final Uri[] NOT_GRANTABLE = new Uri[] {
-            Uri.withAppendedPath(PERM_URI, "foo"),
-            Uri.withAppendedPath(PRIV_URI, "foo"),
-            Uri.withAppendedPath(PERM_URI_PATH_RESTRICTING, "foo"),
+    static final Uri[] NOT_GRANTABLE = new Uri[] {
+            Uri.withAppendedPath(PERM_URI, "bar"),
+            Uri.withAppendedPath(PERM_URI_GRANTING, "bar"),
+            Uri.withAppendedPath(PERM_URI_PATH, "bar"),
+            Uri.withAppendedPath(PRIV_URI, "bar"),
+            Uri.withAppendedPath(PRIV_URI_GRANTING, "bar"),
+            Uri.withAppendedPath(AMBIGUOUS_URI, "bar"),
             CalendarContract.CONTENT_URI,
             ContactsContract.AUTHORITY_URI,
     };
 
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
+    static final int[] GRANTABLE_MODES = new int[] {
+            Intent.FLAG_GRANT_READ_URI_PERMISSION,
+            Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+    };
 
-        // Always dispose, usually to clean up from failed tests
-        ReceiveUriActivity.finishCurInstanceSync();
-    }
-
-    private void assertReadingContentUriNotAllowed(Uri uri, String msg) {
-        try {
-            getContext().getContentResolver().query(uri, null, null, null, null);
-            fail("expected SecurityException reading " + uri + ": " + msg);
-        } catch (SecurityException expected) {
-            assertNotNull("security exception's error message.", expected.getMessage());
-        }
-    }
-
-    private void assertReadingContentUriAllowed(Uri uri) {
-        try {
-            getContext().getContentResolver().query(uri, null, null, null, null);
-        } catch (SecurityException e) {
-            fail("unexpected SecurityException reading " + uri + ": " + e.getMessage());
-        }
-    }
-
-    private void assertReadingClipNotAllowed(ClipData clip) {
-        assertReadingClipNotAllowed(clip, null);
-    }
-
-    private void assertReadingClipNotAllowed(ClipData clip, String msg) {
-        for (int i=0; i<clip.getItemCount(); i++) {
-            ClipData.Item item = clip.getItemAt(i);
-            Uri uri = item.getUri();
-            if (uri != null) {
-                assertReadingContentUriNotAllowed(uri, msg);
-            } else {
-                Intent intent = item.getIntent();
-                uri = intent.getData();
-                if (uri != null) {
-                    assertReadingContentUriNotAllowed(uri, msg);
-                }
-                ClipData intentClip = intent.getClipData();
-                if (intentClip != null) {
-                    assertReadingClipNotAllowed(intentClip, msg);
-                }
-            }
-        }
-    }
-
-    private void assertOpenFileDescriptorModeNotAllowed(Uri uri, String msg, String mode) {
-        try {
-            getContext().getContentResolver().openFileDescriptor(uri, mode).close();
-            fail("expected SecurityException writing " + uri + ": " + msg);
-        } catch (IOException e) {
-            throw new IllegalStateException(e);
-        } catch (SecurityException expected) {
-            assertNotNull("security exception's error message.", expected.getMessage());
-        }
-    }
-
-    private void assertContentUriAllowed(Uri uri) {
-        assertReadingContentUriAllowed(uri);
-        assertWritingContentUriAllowed(uri);
-    }
-
-    private void assertContentUriNotAllowed(Uri uri, String msg) {
-        assertReadingContentUriNotAllowed(uri, msg);
-        assertWritingContentUriNotAllowed(uri, msg);
-    }
-
-    private void assertWritingContentUriNotAllowed(Uri uri, String msg) {
-        final ContentResolver resolver = getContext().getContentResolver();
-        try {
-            resolver.insert(uri, new ContentValues());
-            fail("expected SecurityException inserting " + uri + ": " + msg);
-        } catch (SecurityException expected) {
-            assertNotNull("security exception's error message.", expected.getMessage());
-        }
-
-        try {
-            resolver.update(uri, new ContentValues(), null, null);
-            fail("expected SecurityException updating " + uri + ": " + msg);
-        } catch (SecurityException expected) {
-            assertNotNull("security exception's error message.", expected.getMessage());
-        }
-
-        try {
-            resolver.delete(uri, null, null);
-            fail("expected SecurityException deleting " + uri + ": " + msg);
-        } catch (SecurityException expected) {
-            assertNotNull("security exception's error message.", expected.getMessage());
-        }
-
-        try {
-            getContext().getContentResolver().openOutputStream(uri).close();
-            fail("expected SecurityException writing " + uri + ": " + msg);
-        } catch (IOException e) {
-            throw new IllegalStateException(e);
-        } catch (SecurityException expected) {
-            assertNotNull("security exception's error message.", expected.getMessage());
-        }
-
-        assertOpenFileDescriptorModeNotAllowed(uri, msg, "w");
-        assertOpenFileDescriptorModeNotAllowed(uri, msg, "wt");
-        assertOpenFileDescriptorModeNotAllowed(uri, msg, "wa");
-        assertOpenFileDescriptorModeNotAllowed(uri, msg, "rw");
-        assertOpenFileDescriptorModeNotAllowed(uri, msg, "rwt");
-    }
-
-    private void assertWritingContentUriAllowed(Uri uri) {
-        final ContentResolver resolver = getContext().getContentResolver();
-        try {
-            resolver.insert(uri, new ContentValues());
-            resolver.update(uri, new ContentValues(), null, null);
-            resolver.delete(uri, null, null);
-
-            resolver.openOutputStream(uri).close();
-            resolver.openFileDescriptor(uri, "w").close();
-            resolver.openFileDescriptor(uri, "wt").close();
-            resolver.openFileDescriptor(uri, "wa").close();
-            resolver.openFileDescriptor(uri, "rw").close();
-            resolver.openFileDescriptor(uri, "rwt").close();
-        } catch (IOException e) {
-            fail("unexpected IOException writing " + uri + ": " + e.getMessage());
-        } catch (SecurityException e) {
-            fail("unexpected SecurityException writing " + uri + ": " + e.getMessage());
-        }
-    }
-
-    private void assertWritingClipNotAllowed(ClipData clip) {
-        assertWritingClipNotAllowed(clip, null);
-    }
-
-    private void assertWritingClipNotAllowed(ClipData clip, String msg) {
-        for (int i=0; i<clip.getItemCount(); i++) {
-            ClipData.Item item = clip.getItemAt(i);
-            Uri uri = item.getUri();
-            if (uri != null) {
-                assertWritingContentUriNotAllowed(uri, msg);
-            } else {
-                Intent intent = item.getIntent();
-                uri = intent.getData();
-                if (uri != null) {
-                    assertWritingContentUriNotAllowed(uri, msg);
-                }
-                ClipData intentClip = intent.getClipData();
-                if (intentClip != null) {
-                    assertWritingClipNotAllowed(intentClip, msg);
-                }
-            }
-        }
-    }
+    static final int[] NOT_GRANTABLE_MODES = new int[] {
+            Intent.FLAG_GRANT_READ_URI_PERMISSION,
+            Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+            Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION,
+            Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION,
+            Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION,
+            Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION,
+    };
 
     /**
      * Test that the ctspermissionwithsignature content provider cannot be read,
      * since this app lacks the required certs
      */
+    @Test
     public void testReadProviderWithDiff() {
-        assertReadingContentUriRequiresPermission(PERM_URI,
-                "com.android.cts.permissionWithSignature");
+        assertReadingContentUriNotAllowed(PERM_URI, null);
     }
 
     /**
      * Test that the ctspermissionwithsignature content provider cannot be written,
      * since this app lacks the required certs
      */
+    @Test
     public void testWriteProviderWithDiff() {
-        assertWritingContentUriRequiresPermission(PERM_URI,
-                "com.android.cts.permissionWithSignature");
+        assertWritingContentUriNotAllowed(PERM_URI, null);
     }
 
     /**
      * Test that the ctsprivateprovider content provider cannot be read,
      * since it is not exported from its app.
      */
+    @Test
     public void testReadProviderWhenPrivate() {
         assertReadingContentUriNotAllowed(PRIV_URI, "shouldn't read private provider");
     }
@@ -266,6 +124,7 @@
      * Test that the ctsambiguousprovider content provider cannot be read,
      * since it doesn't have an "exported=" line.
      */
+    @Test
     public void testReadProviderWhenAmbiguous() {
         assertReadingContentUriNotAllowed(AMBIGUOUS_URI, "shouldn't read ambiguous provider");
     }
@@ -276,6 +135,7 @@
      * Test that the ctsambiguousprovidercompat content provider can be read for older
      * API versions, because it didn't specify either exported=true or exported=false.
      */
+    @Test
     public void testReadProviderWhenAmbiguousCompat() {
         assertReadingContentUriAllowed(AMBIGUOUS_URI_COMPAT);
     }
@@ -286,6 +146,7 @@
      * Test that the ctsambiguousprovidercompat content provider can be written for older
      * API versions, because it didn't specify either exported=true or exported=false.
      */
+    @Test
     public void testWriteProviderWhenAmbiguousCompat() {
         assertWritingContentUriAllowed(AMBIGUOUS_URI_COMPAT);
     }
@@ -294,6 +155,7 @@
      * Test that the ctsprivateprovider content provider cannot be written,
      * since it is not exported from its app.
      */
+    @Test
     public void testWriteProviderWhenPrivate() {
         assertWritingContentUriNotAllowed(PRIV_URI, "shouldn't write private provider");
     }
@@ -302,858 +164,16 @@
      * Test that the ctsambiguousprovider content provider cannot be written,
      * since it doesn't have an exported= line.
      */
+    @Test
     public void testWriteProviderWhenAmbiguous() {
         assertWritingContentUriNotAllowed(AMBIGUOUS_URI, "shouldn't write ambiguous provider");
     }
 
-    private static ClipData makeSingleClipData(Uri uri) {
-        return new ClipData("foo", new String[] { "foo/bar" },
-                new ClipData.Item(uri));
-    }
-
-    private static ClipData makeMultiClipData(Uri uri) {
-        Uri grantClip1Uri = Uri.withAppendedPath(uri, "clip1");
-        Uri grantClip2Uri = Uri.withAppendedPath(uri, "clip2");
-        Uri grantClip3Uri = Uri.withAppendedPath(uri, "clip3");
-        Uri grantClip4Uri = Uri.withAppendedPath(uri, "clip4");
-        Uri grantClip5Uri = Uri.withAppendedPath(uri, "clip5");
-        ClipData clip = new ClipData("foo", new String[] { "foo/bar" },
-                new ClipData.Item(grantClip1Uri));
-        clip.addItem(new ClipData.Item(grantClip2Uri));
-        // Intents in the ClipData should allow their data: and clip URIs
-        // to be granted, but only respect the grant flags of the top-level
-        // Intent.
-        clip.addItem(new ClipData.Item(new Intent(Intent.ACTION_VIEW, grantClip3Uri)));
-        Intent intent = new Intent(Intent.ACTION_VIEW, grantClip4Uri);
-        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
-                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-        clip.addItem(new ClipData.Item(intent));
-        intent = new Intent(Intent.ACTION_VIEW);
-        intent.setClipData(new ClipData("foo", new String[] { "foo/bar" },
-                new ClipData.Item(grantClip5Uri)));
-        clip.addItem(new ClipData.Item(intent));
-        return clip;
-    }
-
-    private static Intent makeClipIntent(ClipData clip, int flags) {
-        Intent intent = new Intent();
-        intent.setClipData(clip);
-        intent.addFlags(flags);
-        return intent;
-    }
-
-    private static Intent makeClipIntent(Uri uri, int flags) {
-        return makeClipIntent(makeMultiClipData(uri), flags);
-    }
-
-    private void doTryGrantUriActivityPermissionToSelf(Uri uri, int mode) {
-        Uri grantDataUri = Uri.withAppendedPath(uri, "data");
-        Intent grantIntent = new Intent();
-        grantIntent.setData(grantDataUri);
-        grantIntent.addFlags(mode | Intent.FLAG_ACTIVITY_NEW_TASK);
-        grantIntent.setClass(getContext(), ReceiveUriActivity.class);
-        try {
-            ReceiveUriActivity.clearStarted();
-            getContext().startActivity(grantIntent);
-            ReceiveUriActivity.waitForStart();
-            fail("expected SecurityException granting " + grantDataUri + " to activity");
-        } catch (SecurityException e) {
-            // This is what we want.
-        }
-
-        grantIntent = makeClipIntent(uri, mode | Intent.FLAG_ACTIVITY_NEW_TASK);
-        grantIntent.setClass(getContext(), ReceiveUriActivity.class);
-        try {
-            ReceiveUriActivity.clearStarted();
-            getContext().startActivity(grantIntent);
-            ReceiveUriActivity.waitForStart();
-            fail("expected SecurityException granting " + grantIntent.getClipData() + " to activity");
-        } catch (SecurityException e) {
-            // This is what we want.
-        }
-    }
-
-    /**
-     * Test that we can't grant a permission to ourself.
-     */
-    public void testGrantReadUriActivityPermissionToSelf() {
-        doTryGrantUriActivityPermissionToSelf(
-                Uri.withAppendedPath(PERM_URI_GRANTING, "foo"),
-                Intent.FLAG_GRANT_READ_URI_PERMISSION);
-    }
-
-    /**
-     * Test that we can't grant a permission to ourself.
-     */
-    public void testGrantWriteUriActivityPermissionToSelf() {
-        doTryGrantUriActivityPermissionToSelf(
-                Uri.withAppendedPath(PERM_URI_GRANTING, "foo"),
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-    }
-
-    /**
-     * Test that we can't grant a permission to ourself.
-     */
-    public void testGrantReadUriActivityPrivateToSelf() {
-        doTryGrantUriActivityPermissionToSelf(
-                Uri.withAppendedPath(PRIV_URI_GRANTING, "foo"),
-                Intent.FLAG_GRANT_READ_URI_PERMISSION);
-    }
-
-    /**
-     * Test that we can't grant a permission to ourself.
-     */
-    public void testGrantWriteUriActivityPrivateToSelf() {
-        doTryGrantUriActivityPermissionToSelf(
-                Uri.withAppendedPath(PRIV_URI_GRANTING, "foo"),
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-    }
-
-    private void doTryGrantUriServicePermissionToSelf(Uri uri, int mode) {
-        Uri grantDataUri = Uri.withAppendedPath(uri, "data");
-        Intent grantIntent = new Intent();
-        grantIntent.setData(grantDataUri);
-        grantIntent.addFlags(mode);
-        grantIntent.setClass(getContext(), ReceiveUriService.class);
-        try {
-            getContext().startService(grantIntent);
-            fail("expected SecurityException granting " + grantDataUri + " to service");
-        } catch (SecurityException e) {
-            // This is what we want.
-        }
-
-        grantIntent = makeClipIntent(uri, mode);
-        grantIntent.setClass(getContext(), ReceiveUriService.class);
-        try {
-            getContext().startService(grantIntent);
-            fail("expected SecurityException granting " + grantIntent.getClipData() + " to service");
-        } catch (SecurityException e) {
-            // This is what we want.
-        }
-    }
-
-    /**
-     * Test that we can't grant a permission to ourself.
-     */
-    public void testGrantReadUriServicePermissionToSelf() {
-        doTryGrantUriServicePermissionToSelf(
-                Uri.withAppendedPath(PERM_URI_GRANTING, "foo"),
-                Intent.FLAG_GRANT_READ_URI_PERMISSION);
-    }
-
-    /**
-     * Test that we can't grant a permission to ourself.
-     */
-    public void testGrantWriteUriServicePermissionToSelf() {
-        doTryGrantUriServicePermissionToSelf(
-                Uri.withAppendedPath(PERM_URI_GRANTING, "foo"),
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-    }
-
-    /**
-     * Test that we can't grant a permission to ourself.
-     */
-    public void testGrantReadUriServicePrivateToSelf() {
-        doTryGrantUriServicePermissionToSelf(
-                Uri.withAppendedPath(PRIV_URI_GRANTING, "foo"),
-                Intent.FLAG_GRANT_READ_URI_PERMISSION);
-    }
-
-    /**
-     * Test that we can't grant a permission to ourself.
-     */
-    public void testGrantWriteUriServicePrivateToSelf() {
-        doTryGrantUriServicePermissionToSelf(
-                Uri.withAppendedPath(PRIV_URI_GRANTING, "foo"),
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-    }
-
-    private void grantUriPermissionFail(Uri uri, int mode, boolean service) {
-        Uri grantDataUri = Uri.withAppendedPath(uri, "data");
-        Intent grantIntent = new Intent();
-        grantIntent.setData(grantDataUri);
-        grantIntent.addFlags(mode);
-        grantIntent.setClass(getContext(),
-                service ? ReceiveUriService.class : ReceiveUriActivity.class);
-        Intent intent = new Intent();
-        intent.setAction(service ? ACTION_START_SERVICE : ACTION_START_ACTIVITY);
-        intent.putExtra(EXTRA_INTENT, grantIntent);
-        try {
-            call(intent);
-            fail("Able to grant URI permission to " + grantDataUri + " when should not");
-        } catch (Exception expected) {
-        }
-
-        grantIntent = makeClipIntent(uri, mode);
-        grantIntent.setClass(getContext(),
-                service ? ReceiveUriService.class : ReceiveUriActivity.class);
-        intent = new Intent();
-        intent.setAction(service ? ACTION_START_SERVICE : ACTION_START_ACTIVITY);
-        intent.putExtra(EXTRA_INTENT, grantIntent);
-        try {
-            call(intent);
-            fail("Able to grant URI permission to " + grantIntent.getClipData()
-                    + " when should not");
-        } catch (Exception expected) {
-        }
-    }
-
-    private void doTestGrantUriPermissionFail(Uri uri) {
-        for (boolean service : new boolean[] { false, true }) {
-            for (int flags : new int[] {
-                    Intent.FLAG_GRANT_READ_URI_PERMISSION, Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-            }) {
-                grantUriPermissionFail(uri,
-                        flags, service);
-                grantUriPermissionFail(uri,
-                        flags | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, service);
-                grantUriPermissionFail(uri,
-                        flags | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, service);
-            }
-        }
-    }
-    
-    /**
-     * Test that the ctspermissionwithsignature content provider can not grant
-     * URI permissions to others.
-     */
-    public void testGrantPermissionNonGrantingFail() {
-        doTestGrantUriPermissionFail(PERM_URI);
-    }
-
-    /**
-     * Test that the ctspermissionwithsignaturegranting content provider can not grant
-     * URI permissions to paths outside of the grant tree
-     */
-    public void testGrantPermissionOutsideGrantingFail() {
-        doTestGrantUriPermissionFail(PERM_URI_GRANTING);
-        doTestGrantUriPermissionFail(Uri.withAppendedPath(PERM_URI_GRANTING, "invalid"));
-    }
-
-    /**
-     * Test that the ctsprivateprovider content provider can not grant
-     * URI permissions to others.
-     */
-    public void testGrantPrivateNonGrantingFail() {
-        doTestGrantUriPermissionFail(PRIV_URI);
-    }
-
-    /**
-     * Test that the ctsambiguousprovider content provider can not grant
-     * URI permissions to others.
-     */
-    public void testGrantAmbiguousNonGrantingFail() {
-        doTestGrantUriPermissionFail(AMBIGUOUS_URI);
-    }
-
-    /**
-     * Test that the ctsprivateprovidergranting content provider can not grant
-     * URI permissions to paths outside of the grant tree
-     */
-    public void testGrantPrivateOutsideGrantingFail() {
-        doTestGrantUriPermissionFail(PRIV_URI_GRANTING);
-        doTestGrantUriPermissionFail(Uri.withAppendedPath(PRIV_URI_GRANTING, "invalid"));
-    }
-
-    private void call(Intent intent) {
-        final Bundle extras = new Bundle();
-        extras.putParcelable(Intent.EXTRA_INTENT, intent);
-        getContext().getContentResolver().call(UtilsProvider.URI, "", "", extras);
-    }
-
-    private void grantClipUriPermission(ClipData clip, int mode, boolean service) {
-        Intent grantIntent = new Intent();
-        if (clip.getItemCount() == 1) {
-            grantIntent.setData(clip.getItemAt(0).getUri());
-        } else {
-            grantIntent.setClipData(clip);
-            // Make this Intent unique from the one that started it.
-            for (int i=0; i<clip.getItemCount(); i++) {
-                Uri uri = clip.getItemAt(i).getUri();
-                if (uri != null) {
-                    grantIntent.addCategory(uri.toString());
-                }
-            }
-        }
-        grantIntent.addFlags(mode);
-        grantIntent.setClass(getContext(),
-                service ? ReceiveUriService.class : ReceiveUriActivity.class);
-        Intent intent = new Intent();
-        intent.setAction(service ? ACTION_START_SERVICE : ACTION_START_ACTIVITY);
-        intent.putExtra(EXTRA_INTENT, grantIntent);
-        call(intent);
-    }
-
-    private void grantClipUriPermissionViaContext(Uri uri, int mode) {
-        Intent intent = new Intent();
-        intent.setAction(ACTION_GRANT_URI);
-        intent.putExtra(EXTRA_PACKAGE_NAME, getContext().getPackageName());
-        intent.putExtra(EXTRA_URI, uri);
-        intent.putExtra(EXTRA_MODE, mode);
-        call(intent);
-    }
-
-    private void revokeClipUriPermissionViaContext(Uri uri, int mode) {
-        Intent intent = new Intent();
-        intent.setAction(ACTION_REVOKE_URI);
-        intent.putExtra(EXTRA_URI, uri);
-        intent.putExtra(EXTRA_MODE, mode);
-        call(intent);
-    }
-
-    private void setPrimaryClip(ClipData clip) {
-        Intent intent = new Intent();
-        intent.setAction(ACTION_SET_PRIMARY_CLIP);
-        intent.setClipData(clip);
-        call(intent);
-    }
-
-    private void clearPrimaryClip() {
-        Intent intent = new Intent();
-        intent.setAction(ACTION_CLEAR_PRIMARY_CLIP);
-        call(intent);
-    }
-
-    private void assertReadingClipAllowed(ClipData clip) {
-        for (int i=0; i<clip.getItemCount(); i++) {
-            ClipData.Item item = clip.getItemAt(i);
-            Uri uri = item.getUri();
-            if (uri != null) {
-                Cursor c = getContext().getContentResolver().query(uri,
-                        null, null, null, null);
-                if (c != null) {
-                    c.close();
-                }
-            } else {
-                Intent intent = item.getIntent();
-                uri = intent.getData();
-                if (uri != null) {
-                    Cursor c = getContext().getContentResolver().query(uri,
-                            null, null, null, null);
-                    if (c != null) {
-                        c.close();
-                    }
-                }
-                ClipData intentClip = intent.getClipData();
-                if (intentClip != null) {
-                    assertReadingClipAllowed(intentClip);
-                }
-            }
-        }
-    }
-
-    private void doTestGrantActivityUriReadPermission(Uri uri, boolean useClip) {
-        final Uri subUri = Uri.withAppendedPath(uri, "foo");
-        final Uri subSubUri = Uri.withAppendedPath(subUri, "bar");
-        final Uri sub2Uri = Uri.withAppendedPath(uri, "yes");
-        final Uri sub2SubUri = Uri.withAppendedPath(sub2Uri, "no");
-
-        final ClipData subClip = useClip ? makeMultiClipData(subUri) : makeSingleClipData(subUri);
-        final ClipData sub2Clip = useClip ? makeMultiClipData(sub2Uri) : makeSingleClipData(sub2Uri);
-
-        // Precondition: no current access.
-        assertReadingClipNotAllowed(subClip, "shouldn't read when starting test");
-        assertReadingClipNotAllowed(sub2Clip, "shouldn't read when starting test");
-
-        // --------------------------------
-
-        ReceiveUriActivity.clearStarted();
-        grantClipUriPermission(subClip, Intent.FLAG_GRANT_READ_URI_PERMISSION, false);
-        ReceiveUriActivity.waitForStart();
-
-        // See if we now have access to the provider.
-        assertReadingClipAllowed(subClip);
-
-        // But not writing.
-        assertWritingClipNotAllowed(subClip, "shouldn't write from granted read");
-
-        // And not to the base path.
-        assertReadingContentUriNotAllowed(uri, "shouldn't read non-granted base URI");
-
-        // And not to a sub path.
-        assertReadingContentUriNotAllowed(subSubUri, "shouldn't read non-granted sub URI");
-
-        // --------------------------------
-
-        ReceiveUriActivity.clearNewIntent();
-        grantClipUriPermission(sub2Clip, Intent.FLAG_GRANT_READ_URI_PERMISSION, false);
-        ReceiveUriActivity.waitForNewIntent();
-
-        if (false) {
-            synchronized (this) {
-                Log.i("**", "******************************* WAITING!!!");
-                try {
-                    wait(10000);
-                } catch (InterruptedException e) {
-                }
-            }
-        }
-
-        // See if we now have access to the provider.
-        assertReadingClipAllowed(sub2Clip);
-
-        // And still have access to the original URI.
-        assertReadingClipAllowed(subClip);
-
-        // But not writing.
-        assertWritingClipNotAllowed(sub2Clip, "shouldn't write from granted read");
-
-        // And not to the base path.
-        assertReadingContentUriNotAllowed(uri, "shouldn't read non-granted base URI");
-
-        // And not to a sub path.
-        assertReadingContentUriNotAllowed(sub2SubUri, "shouldn't read non-granted sub URI");
-
-        // And make sure we can't generate a permission to a running activity.
-        doTryGrantUriActivityPermissionToSelf(
-                Uri.withAppendedPath(uri, "hah"),
-                Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        doTryGrantUriActivityPermissionToSelf(
-                Uri.withAppendedPath(uri, "hah"),
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
-        // --------------------------------
-
-        // Dispose of activity.
-        ReceiveUriActivity.finishCurInstanceSync();
-
-        synchronized (this) {
-            Log.i("**", "******************************* WAITING!!!");
-            try {
-                wait(100);
-            } catch (InterruptedException e) {
-            }
-        }
-
-        // Ensure reading no longer allowed.
-        assertReadingClipNotAllowed(subClip, "shouldn't read after losing granted URI");
-        assertReadingClipNotAllowed(sub2Clip, "shouldn't read after losing granted URI");
-    }
-
-    private void assertWritingClipAllowed(ClipData clip) {
-        for (int i=0; i<clip.getItemCount(); i++) {
-            ClipData.Item item = clip.getItemAt(i);
-            Uri uri = item.getUri();
-            if (uri != null) {
-                getContext().getContentResolver().insert(uri, new ContentValues());
-            } else {
-                Intent intent = item.getIntent();
-                uri = intent.getData();
-                if (uri != null) {
-                    getContext().getContentResolver().insert(uri, new ContentValues());
-                }
-                ClipData intentClip = intent.getClipData();
-                if (intentClip != null) {
-                    assertWritingClipAllowed(intentClip);
-                }
-            }
-        }
-    }
-
-    private void doTestGrantActivityUriWritePermission(Uri uri, boolean useClip) {
-        final Uri subUri = Uri.withAppendedPath(uri, "foo");
-        final Uri subSubUri = Uri.withAppendedPath(subUri, "bar");
-        final Uri sub2Uri = Uri.withAppendedPath(uri, "yes");
-        final Uri sub2SubUri = Uri.withAppendedPath(sub2Uri, "no");
-
-        final ClipData subClip = useClip ? makeMultiClipData(subUri) : makeSingleClipData(subUri);
-        final ClipData sub2Clip = useClip ? makeMultiClipData(sub2Uri) : makeSingleClipData(sub2Uri);
-
-        // Precondition: no current access.
-        assertWritingClipNotAllowed(subClip, "shouldn't write when starting test");
-        assertWritingClipNotAllowed(sub2Clip, "shouldn't write when starting test");
-
-        // --------------------------------
-
-        ReceiveUriActivity.clearStarted();
-        grantClipUriPermission(subClip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false);
-        ReceiveUriActivity.waitForStart();
-
-        // See if we now have access to the provider.
-        assertWritingClipAllowed(subClip);
-
-        // But not reading.
-        assertReadingClipNotAllowed(subClip, "shouldn't read from granted write");
-
-        // And not to the base path.
-        assertWritingContentUriNotAllowed(uri, "shouldn't write non-granted base URI");
-
-        // And not a sub-path.
-        assertWritingContentUriNotAllowed(subSubUri, "shouldn't write non-granted sub URI");
-
-        // --------------------------------
-
-        ReceiveUriActivity.clearNewIntent();
-        grantClipUriPermission(sub2Clip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false);
-        ReceiveUriActivity.waitForNewIntent();
-
-        if (false) {
-            synchronized (this) {
-                Log.i("**", "******************************* WAITING!!!");
-                try {
-                    wait(10000);
-                } catch (InterruptedException e) {
-                }
-            }
-        }
-
-        // See if we now have access to the provider.
-        assertWritingClipAllowed(sub2Clip);
-
-        // And still have access to the original URI.
-        assertWritingClipAllowed(subClip);
-
-        // But not reading.
-        assertReadingClipNotAllowed(sub2Clip, "shouldn't read from granted write");
-
-        // And not to the base path.
-        assertWritingContentUriNotAllowed(uri, "shouldn't write non-granted base URI");
-
-        // And not a sub-path.
-        assertWritingContentUriNotAllowed(sub2SubUri, "shouldn't write non-granted sub URI");
-
-        // And make sure we can't generate a permission to a running activity.
-        doTryGrantUriActivityPermissionToSelf(
-                Uri.withAppendedPath(uri, "hah"),
-                Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        doTryGrantUriActivityPermissionToSelf(
-                Uri.withAppendedPath(uri, "hah"),
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
-        // --------------------------------
-
-        // Dispose of activity.
-        ReceiveUriActivity.finishCurInstanceSync();
-
-        synchronized (this) {
-            Log.i("**", "******************************* WAITING!!!");
-            try {
-                wait(100);
-            } catch (InterruptedException e) {
-            }
-        }
-
-        // Ensure writing no longer allowed.
-        assertWritingClipNotAllowed(subClip, "shouldn't write after losing granted URI");
-        assertWritingClipNotAllowed(sub2Clip, "shouldn't write after losing granted URI");
-    }
-
-    /**
-     * Test that the ctspermissionwithsignaturegranting content provider can grant a read
-     * permission.
-     */
-    public void testGrantReadPermissionFromStartActivity() {
-        doTestGrantActivityUriReadPermission(PERM_URI_GRANTING, false);
-        doTestGrantActivityUriReadPermission(PERM_URI_GRANTING, true);
-    }
-
-    /**
-     * Test that the ctspermissionwithsignaturegranting content provider can grant a write
-     * permission.
-     */
-    public void testGrantWritePermissionFromStartActivity() {
-        doTestGrantActivityUriWritePermission(PERM_URI_GRANTING, true);
-        doTestGrantActivityUriWritePermission(PERM_URI_GRANTING, false);
-    }
-
-    /**
-     * Test that the ctsprivateprovidergranting content provider can grant a read
-     * permission.
-     */
-    public void testGrantReadPrivateFromStartActivity() {
-        doTestGrantActivityUriReadPermission(PRIV_URI_GRANTING, false);
-        doTestGrantActivityUriReadPermission(PRIV_URI_GRANTING, true);
-    }
-
-    /**
-     * Test that the ctsprivateprovidergranting content provider can grant a write
-     * permission.
-     */
-    public void testGrantWritePrivateFromStartActivity() {
-        doTestGrantActivityUriWritePermission(PRIV_URI_GRANTING, true);
-        doTestGrantActivityUriWritePermission(PRIV_URI_GRANTING, false);
-    }
-
-    private void doTestGrantServiceUriReadPermission(Uri uri, boolean useClip) {
-        final Uri subUri = Uri.withAppendedPath(uri, "foo");
-        final Uri subSubUri = Uri.withAppendedPath(subUri, "bar");
-        final Uri sub2Uri = Uri.withAppendedPath(uri, "yes");
-        final Uri sub2SubUri = Uri.withAppendedPath(sub2Uri, "no");
-
-        ReceiveUriService.stop(getContext());
-
-        final ClipData subClip = useClip ? makeMultiClipData(subUri) : makeSingleClipData(subUri);
-        final ClipData sub2Clip = useClip ? makeMultiClipData(sub2Uri) : makeSingleClipData(sub2Uri);
-
-        // Precondition: no current access.
-        assertReadingClipNotAllowed(subClip, "shouldn't read when starting test");
-        assertReadingClipNotAllowed(sub2Clip, "shouldn't read when starting test");
-
-        // --------------------------------
-
-        ReceiveUriService.clearStarted();
-        grantClipUriPermission(subClip, Intent.FLAG_GRANT_READ_URI_PERMISSION, true);
-        ReceiveUriService.waitForStart();
-
-        int firstStartId = ReceiveUriService.getCurStartId();
-
-        // See if we now have access to the provider.
-        assertReadingClipAllowed(subClip);
-
-        // But not writing.
-        assertWritingClipNotAllowed(subClip, "shouldn't write from granted read");
-
-        // And not to the base path.
-        assertReadingContentUriNotAllowed(uri, "shouldn't read non-granted base URI");
-
-        // And not to a sub path.
-        assertReadingContentUriNotAllowed(subSubUri, "shouldn't read non-granted sub URI");
-
-        // --------------------------------
-
-        // Send another Intent to it.
-        ReceiveUriService.clearStarted();
-        grantClipUriPermission(sub2Clip, Intent.FLAG_GRANT_READ_URI_PERMISSION, true);
-        ReceiveUriService.waitForStart();
-
-        if (false) {
-            synchronized (this) {
-                Log.i("**", "******************************* WAITING!!!");
-                try {
-                    wait(10000);
-                } catch (InterruptedException e) {
-                }
-            }
-        }
-
-        // See if we now have access to the provider.
-        assertReadingClipAllowed(sub2Clip);
-
-        // And still to the previous URI.
-        assertReadingClipAllowed(subClip);
-
-        // But not writing.
-        assertWritingClipNotAllowed(sub2Clip, "shouldn't write from granted read");
-
-        // And not to the base path.
-        assertReadingContentUriNotAllowed(uri, "shouldn't read non-granted base URI");
-
-        // And not to a sub path.
-        assertReadingContentUriNotAllowed(sub2SubUri, "shouldn't read non-granted sub URI");
-
-        // --------------------------------
-
-        // Stop the first command.
-        ReceiveUriService.stopCurWithId(firstStartId);
-
-        // Ensure reading no longer allowed.
-        assertReadingClipNotAllowed(subClip, "shouldn't read after losing granted URI");
-
-        // And make sure we can't generate a permission to a running service.
-        doTryGrantUriActivityPermissionToSelf(subUri,
-                Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        doTryGrantUriActivityPermissionToSelf(subUri,
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
-        // --------------------------------
-
-        // Dispose of service.
-        ReceiveUriService.stopSync(getContext());
-
-        // Ensure reading no longer allowed.
-        assertReadingClipNotAllowed(subClip, "shouldn't read after losing granted URI");
-        assertReadingClipNotAllowed(sub2Clip, "shouldn't read after losing granted URI");
-    }
-
-    private void doTestGrantServiceUriWritePermission(Uri uri, boolean useClip) {
-        final Uri subUri = Uri.withAppendedPath(uri, "foo");
-        final Uri subSubUri = Uri.withAppendedPath(subUri, "bar");
-        final Uri sub2Uri = Uri.withAppendedPath(uri, "yes");
-        final Uri sub2SubUri = Uri.withAppendedPath(sub2Uri, "no");
-
-        ReceiveUriService.stop(getContext());
-
-        final ClipData subClip = useClip ? makeMultiClipData(subUri) : makeSingleClipData(subUri);
-        final ClipData sub2Clip = useClip ? makeMultiClipData(sub2Uri) : makeSingleClipData(sub2Uri);
-
-        // Precondition: no current access.
-        assertReadingClipNotAllowed(subClip, "shouldn't read when starting test");
-        assertReadingClipNotAllowed(sub2Clip, "shouldn't read when starting test");
-
-        // --------------------------------
-
-        ReceiveUriService.clearStarted();
-        grantClipUriPermission(subClip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true);
-        ReceiveUriService.waitForStart();
-
-        int firstStartId = ReceiveUriService.getCurStartId();
-
-        // See if we now have access to the provider.
-        assertWritingClipAllowed(subClip);
-
-        // But not reading.
-        assertReadingClipNotAllowed(subClip, "shouldn't read from granted write");
-
-        // And not to the base path.
-        assertWritingContentUriNotAllowed(uri, "shouldn't write non-granted base URI");
-
-        // And not a sub-path.
-        assertWritingContentUriNotAllowed(subSubUri, "shouldn't write non-granted sub URI");
-
-        // --------------------------------
-
-        // Send another Intent to it.
-        ReceiveUriService.clearStarted();
-        grantClipUriPermission(sub2Clip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true);
-        ReceiveUriService.waitForStart();
-
-        // See if we now have access to the provider.
-        assertWritingClipAllowed(sub2Clip);
-
-        // And still to the previous URI.
-        assertWritingClipAllowed(subClip);
-
-        // But not reading.
-        assertReadingClipNotAllowed(sub2Clip, "shouldn't read from granted write");
-
-        // And not to the base path.
-        assertWritingContentUriNotAllowed(uri, "shouldn't write non-granted base URI");
-
-        // And not a sub-path.
-        assertWritingContentUriNotAllowed(sub2SubUri, "shouldn't write non-granted sub URI");
-
-        if (false) {
-            synchronized (this) {
-                Log.i("**", "******************************* WAITING!!!");
-                try {
-                    wait(10000);
-                } catch (InterruptedException e) {
-                }
-            }
-        }
-
-        // --------------------------------
-
-        // Stop the first command.
-        ReceiveUriService.stopCurWithId(firstStartId);
-
-        // Ensure writing no longer allowed.
-        assertWritingClipNotAllowed(subClip, "shouldn't write after losing granted URI");
-
-        // And make sure we can't generate a permission to a running service.
-        doTryGrantUriActivityPermissionToSelf(subUri,
-                Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        doTryGrantUriActivityPermissionToSelf(subUri,
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
-        // --------------------------------
-
-        // Dispose of service.
-        ReceiveUriService.stopSync(getContext());
-
-        // Ensure writing no longer allowed.
-        assertWritingClipNotAllowed(subClip, "shouldn't write after losing granted URI");
-        assertWritingClipNotAllowed(sub2Clip, "shouldn't write after losing granted URI");
-    }
-
-    public void testGrantReadPermissionFromStartService() {
-        doTestGrantServiceUriReadPermission(PERM_URI_GRANTING, false);
-        doTestGrantServiceUriReadPermission(PERM_URI_GRANTING, true);
-    }
-
-    public void testGrantWritePermissionFromStartService() {
-        doTestGrantServiceUriWritePermission(PERM_URI_GRANTING, false);
-        doTestGrantServiceUriWritePermission(PERM_URI_GRANTING, true);
-    }
-
-    public void testGrantReadPrivateFromStartService() {
-        doTestGrantServiceUriReadPermission(PRIV_URI_GRANTING, false);
-        doTestGrantServiceUriReadPermission(PRIV_URI_GRANTING, true);
-    }
-
-    public void testGrantWritePrivateFromStartService() {
-        doTestGrantServiceUriWritePermission(PRIV_URI_GRANTING, false);
-        doTestGrantServiceUriWritePermission(PRIV_URI_GRANTING, true);
-    }
-
-    /**
-     * Test that ctspermissionwithsignaturepath can't grant read permissions
-     * on paths it doesn't have permission to.
-     */
-    public void testGrantReadUriActivityPathPermissionToSelf() {
-        doTryGrantUriActivityPermissionToSelf(PERM_URI_PATH,
-                Intent.FLAG_GRANT_READ_URI_PERMISSION);
-    }
-
-    /**
-     * Test that ctspermissionwithsignaturepath can't grant write permissions
-     * on paths it doesn't have permission to.
-     */
-    public void testGrantWriteUriActivityPathPermissionToSelf() {
-        doTryGrantUriActivityPermissionToSelf(PERM_URI_PATH,
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-    }
-
-    /**
-     * Test that ctspermissionwithsignaturepath can't grant read permissions
-     * on paths it doesn't have permission to.
-     */
-    public void testGrantReadUriActivitySubPathPermissionToSelf() {
-        doTryGrantUriActivityPermissionToSelf(
-                Uri.withAppendedPath(PERM_URI_PATH, "foo"),
-                Intent.FLAG_GRANT_READ_URI_PERMISSION);
-    }
-
-    /**
-     * Test that ctspermissionwithsignaturepath can't grant write permissions
-     * on paths it doesn't have permission to.
-     */
-    public void testGrantWriteUriActivitySubPathPermissionToSelf() {
-        doTryGrantUriActivityPermissionToSelf(
-                Uri.withAppendedPath(PERM_URI_PATH, "foo"),
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-    }
-
-    /**
-     * Test that the ctspermissionwithsignaturepath content provider can grant a read
-     * permission.
-     */
-    public void testGrantReadPathPermissionFromStartActivity() {
-        doTestGrantActivityUriReadPermission(PERM_URI_PATH, false);
-        doTestGrantActivityUriReadPermission(PERM_URI_PATH, true);
-    }
-
-    /**
-     * Test that the ctspermissionwithsignaturepath content provider can grant a write
-     * permission.
-     */
-    public void testGrantWritePathPermissionFromStartActivity() {
-        doTestGrantActivityUriWritePermission(PERM_URI_PATH, false);
-        doTestGrantActivityUriWritePermission(PERM_URI_PATH, true);
-    }
-
-    /**
-     * Test that the ctspermissionwithsignaturepath content provider can grant a read
-     * permission.
-     */
-    public void testGrantReadPathPermissionFromStartService() {
-        doTestGrantServiceUriReadPermission(PERM_URI_PATH, false);
-        doTestGrantServiceUriReadPermission(PERM_URI_PATH, true);
-    }
-
-    /**
-     * Test that the ctspermissionwithsignaturepath content provider can grant a write
-     * permission.
-     */
-    public void testGrantWritePathPermissionFromStartService() {
-        doTestGrantServiceUriWritePermission(PERM_URI_PATH, false);
-        doTestGrantServiceUriWritePermission(PERM_URI_PATH, true);
-    }
-
     /**
      * Verify that we can access paths outside the {@code path-permission}
      * protections, which should only rely on {@code provider} permissions.
      */
+    @Test
     public void testRestrictingProviderNoMatchingPath() {
         assertReadingContentUriAllowed(PERM_URI_PATH_RESTRICTING);
         assertWritingContentUriAllowed(PERM_URI_PATH_RESTRICTING);
@@ -1168,6 +188,7 @@
      * Verify that paths under {@code path-permission} restriction aren't
      * allowed, even though the {@code provider} requires no permissions.
      */
+    @Test
     public void testRestrictingProviderMatchingPathDenied() {
         // rejected by "foo" prefix
         final Uri test1 = PERM_URI_PATH_RESTRICTING.buildUpon().appendPath("foo").build();
@@ -1184,6 +205,7 @@
     /**
      * Test that shady {@link Uri} are blocked by {@code path-permission}.
      */
+    @Test
     public void testRestrictingProviderMatchingShadyPaths() {
         assertContentUriAllowed(
                 Uri.parse("content://ctspermissionwithsignaturepathrestricting/"));
@@ -1205,6 +227,7 @@
      * Verify that at least one {@code path-permission} rule will grant access,
      * even if the caller doesn't hold another matching {@code path-permission}.
      */
+    @Test
     public void testRestrictingProviderMultipleMatchingPath() {
         // allowed by narrow "foo/bar" prefix
         final Uri test1 = PERM_URI_PATH_RESTRICTING.buildUpon()
@@ -1219,6 +242,7 @@
         assertWritingContentUriAllowed(test2);
     }
 
+    @Test
     public void testGetMimeTypePermission() {
         // Precondition: no current access.
         assertReadingContentUriNotAllowed(PERM_URI, "shouldn't read when starting test");
@@ -1228,6 +252,7 @@
         assertEquals(getContext().getContentResolver().getType(PERM_URI), EXPECTED_MIME_TYPE);
     }
 
+    @Test
     public void testGetMimeTypePrivate() {
         // Precondition: no current access.
         assertReadingContentUriNotAllowed(PRIV_URI, "shouldn't read when starting test");
@@ -1237,6 +262,7 @@
         assertEquals(getContext().getContentResolver().getType(PRIV_URI), EXPECTED_MIME_TYPE);
     }
 
+    @Test
     public void testGetMimeTypeAmbiguous() {
         // Precondition: no current access.
         assertReadingContentUriNotAllowed(AMBIGUOUS_URI, "shouldn't read when starting test");
@@ -1253,425 +279,10 @@
      * application, even if that application didn't explicitly declare either
      * exported=true or exported=false
      */
+    @Test
     public void testGetMimeTypeAmbiguousCompat() {
         // All apps should be able to get MIME type even if provider is private.
         assertEquals(EXPECTED_MIME_TYPE_AMBIGUOUS,
                 getContext().getContentResolver().getType(AMBIGUOUS_URI_COMPAT));
     }
-
-    /**
-     * Validate behavior of persistable permission grants.
-     */
-    public void testGrantPersistableUriPermission() {
-        final ContentResolver resolver = getContext().getContentResolver();
-
-        final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo");
-        final ClipData clip = makeSingleClipData(target);
-
-        // Make sure we can't see the target
-        assertReadingClipNotAllowed(clip, "reading should have failed");
-        assertWritingClipNotAllowed(clip, "writing should have failed");
-
-        // Make sure we can't take a grant we don't have
-        try {
-            resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
-            fail("taking read should have failed");
-        } catch (SecurityException expected) {
-        }
-
-        // And since we were just installed, no persisted grants yet
-        assertNoPersistedUriPermission();
-
-        // Now, let's grant ourselves some access
-        ReceiveUriActivity.clearStarted();
-        grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
-                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false);
-        ReceiveUriActivity.waitForStart();
-
-        // We should now have reading access, even before taking the persistable
-        // grant. Persisted grants should still be empty.
-        assertReadingClipAllowed(clip);
-        assertWritingClipNotAllowed(clip, "writing should have failed");
-        assertNoPersistedUriPermission();
-
-        // Take the read grant and verify we have it!
-        long before = System.currentTimeMillis();
-        resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        long after = System.currentTimeMillis();
-        assertPersistedUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION, before, after);
-
-        // Make sure we can't take a grant we don't have
-        try {
-            resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-            fail("taking write should have failed");
-        } catch (SecurityException expected) {
-        }
-
-        // Launch again giving ourselves persistable read and write access
-        ReceiveUriActivity.clearNewIntent();
-        grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
-                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false);
-        ReceiveUriActivity.waitForNewIntent();
-
-        // Previous persisted grant should be unchanged
-        assertPersistedUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION, before, after);
-
-        // We should have both read and write; read is persisted, and write
-        // isn't persisted yet.
-        assertReadingClipAllowed(clip);
-        assertWritingClipAllowed(clip);
-
-        // Take again, but still only read; should just update timestamp
-        before = System.currentTimeMillis();
-        resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        after = System.currentTimeMillis();
-        assertPersistedUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION, before, after);
-
-        // And take yet again, both read and write
-        before = System.currentTimeMillis();
-        resolver.takePersistableUriPermission(target,
-                Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-        after = System.currentTimeMillis();
-        assertPersistedUriPermission(target,
-                Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
-                before, after);
-
-        // Now drop the persisted grant; write first, then read
-        resolver.releasePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        assertPersistedUriPermission(target, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, before, after);
-        resolver.releasePersistableUriPermission(target, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-        assertNoPersistedUriPermission();
-
-        // And even though we dropped the persistable grants, our activity is
-        // still running with the global grants (until reboot).
-        assertReadingClipAllowed(clip);
-        assertWritingClipAllowed(clip);
-
-        ReceiveUriActivity.finishCurInstanceSync();
-    }
-
-    private void assertNoPersistedUriPermission() {
-        assertPersistedUriPermission(null, 0, -1, -1);
-    }
-
-    private void assertPersistedUriPermission(Uri uri, int flags, long before, long after) {
-        // Assert local
-        final List<UriPermission> perms = getContext()
-                .getContentResolver().getPersistedUriPermissions();
-        if (uri != null) {
-            assertEquals("expected exactly one permission", 1, perms.size());
-
-            final UriPermission perm = perms.get(0);
-            assertEquals("unexpected uri", uri, perm.getUri());
-
-            final long actual = perm.getPersistedTime();
-            if (before != -1) {
-                assertTrue("found " + actual + " before " + before, actual >= before);
-            }
-            if (after != -1) {
-                assertTrue("found " + actual + " after " + after, actual <= after);
-            }
-
-            final boolean expectedRead = (flags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0;
-            final boolean expectedWrite = (flags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0;
-            assertEquals("unexpected read status", expectedRead, perm.isReadPermission());
-            assertEquals("unexpected write status", expectedWrite, perm.isWritePermission());
-
-        } else {
-            assertEquals("expected zero permissions", 0, perms.size());
-        }
-
-        // And assert remote
-        Intent intent = new Intent();
-        intent.setAction(ACTION_VERIFY_OUTGOING_PERSISTED);
-        intent.putExtra(EXTRA_URI, uri);
-        call(intent);
-    }
-
-    /**
-     * Validate behavior of prefix permission grants.
-     */
-    public void testGrantPrefixUriPermission() throws Exception {
-        final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo1");
-        final Uri targetMeow = Uri.withAppendedPath(target, "meow");
-        final Uri targetMeowCat = Uri.withAppendedPath(targetMeow, "cat");
-
-        final ClipData clip = makeSingleClipData(target);
-        final ClipData clipMeow = makeSingleClipData(targetMeow);
-        final ClipData clipMeowCat = makeSingleClipData(targetMeowCat);
-
-        // Make sure we can't see the target
-        assertReadingClipNotAllowed(clip, "reading should have failed");
-        assertWritingClipNotAllowed(clip, "writing should have failed");
-
-        // Give ourselves prefix read access
-        ReceiveUriActivity.clearStarted();
-        grantClipUriPermission(clipMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION
-                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
-        ReceiveUriActivity.waitForStart();
-
-        // Verify prefix read access
-        assertReadingClipNotAllowed(clip, "reading should have failed");
-        assertReadingClipAllowed(clipMeow);
-        assertReadingClipAllowed(clipMeowCat);
-        assertWritingClipNotAllowed(clip, "writing should have failed");
-        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
-        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
-
-        // Now give ourselves exact write access
-        ReceiveUriActivity.clearNewIntent();
-        grantClipUriPermission(clip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false);
-        ReceiveUriActivity.waitForNewIntent();
-
-        // Verify we have exact write access, but not prefix write
-        assertReadingClipNotAllowed(clip, "reading should have failed");
-        assertReadingClipAllowed(clipMeow);
-        assertReadingClipAllowed(clipMeowCat);
-        assertWritingClipAllowed(clip);
-        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
-        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
-
-        ReceiveUriActivity.finishCurInstanceSync();
-    }
-
-    public void testGrantPersistablePrefixUriPermission() {
-        final ContentResolver resolver = getContext().getContentResolver();
-
-        final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo2");
-        final Uri targetMeow = Uri.withAppendedPath(target, "meow");
-
-        final ClipData clip = makeSingleClipData(target);
-        final ClipData clipMeow = makeSingleClipData(targetMeow);
-
-        // Make sure we can't see the target
-        assertReadingClipNotAllowed(clip, "reading should have failed");
-
-        // Give ourselves prefix read access
-        ReceiveUriActivity.clearStarted();
-        grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
-                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
-                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
-        ReceiveUriActivity.waitForStart();
-
-        // Verify prefix read access
-        assertReadingClipAllowed(clip);
-        assertReadingClipAllowed(clipMeow);
-
-        // Verify we can persist direct grant
-        long before = System.currentTimeMillis();
-        resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        long after = System.currentTimeMillis();
-        assertPersistedUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION, before, after);
-
-        // But we can't take anywhere under the prefix
-        try {
-            resolver.takePersistableUriPermission(targetMeow,
-                    Intent.FLAG_GRANT_READ_URI_PERMISSION);
-            fail("taking under prefix should have failed");
-        } catch (SecurityException expected) {
-        }
-
-        // Should still have access regardless of taking
-        assertReadingClipAllowed(clip);
-        assertReadingClipAllowed(clipMeow);
-
-        // And clean up our grants
-        resolver.releasePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        assertNoPersistedUriPermission();
-
-        ReceiveUriActivity.finishCurInstanceSync();
-    }
-
-    /**
-     * Validate behavior of directly granting/revoking permission grants.
-     */
-    public void testDirectGrantRevokeUriPermission() throws Exception {
-        final ContentResolver resolver = getContext().getContentResolver();
-
-        final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo3");
-        final Uri targetMeow = Uri.withAppendedPath(target, "meow");
-        final Uri targetMeowCat = Uri.withAppendedPath(targetMeow, "cat");
-
-        final ClipData clip = makeSingleClipData(target);
-        final ClipData clipMeow = makeSingleClipData(targetMeow);
-        final ClipData clipMeowCat = makeSingleClipData(targetMeowCat);
-
-        // Make sure we can't see the target
-        assertReadingClipNotAllowed(clipMeow, "reading should have failed");
-        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
-
-        // Give ourselves some grants:
-        // /meow/cat  WRITE|PERSISTABLE
-        // /meow      READ|PREFIX
-        // /meow      WRITE
-        grantClipUriPermissionViaContext(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
-        grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION
-                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
-        grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
-        long before = System.currentTimeMillis();
-        resolver.takePersistableUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-        long after = System.currentTimeMillis();
-        assertPersistedUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, before, after);
-
-        // Verify they look good
-        assertReadingClipNotAllowed(clip, "reading should have failed");
-        assertReadingClipAllowed(clipMeow);
-        assertReadingClipAllowed(clipMeowCat);
-        assertWritingClipNotAllowed(clip, "writing should have failed");
-        assertWritingClipAllowed(clipMeow);
-        assertWritingClipAllowed(clipMeowCat);
-
-        // Revoke anyone with write under meow
-        revokeClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
-        // This should have nuked persisted permission at lower level, but it
-        // shoulnd't have touched our prefix read.
-        assertReadingClipNotAllowed(clip, "reading should have failed");
-        assertReadingClipAllowed(clipMeow);
-        assertReadingClipAllowed(clipMeowCat);
-        assertWritingClipNotAllowed(clip, "writing should have failed");
-        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
-        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
-        assertNoPersistedUriPermission();
-
-        // Revoking read at top of tree should nuke everything else
-        revokeClipUriPermissionViaContext(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        assertReadingClipNotAllowed(clip, "reading should have failed");
-        assertReadingClipNotAllowed(clipMeow, "reading should have failed");
-        assertReadingClipNotAllowed(clipMeowCat, "reading should have failed");
-        assertWritingClipNotAllowed(clip, "writing should have failed");
-        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
-        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
-        assertNoPersistedUriPermission();
-    }
-
-    /**
-     * Validate behavior of a direct permission grant, where the receiver of
-     * that permission revokes it.
-     */
-    public void testDirectGrantReceiverRevokeUriPermission() throws Exception {
-        final ContentResolver resolver = getContext().getContentResolver();
-
-        final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo3");
-        final Uri targetMeow = Uri.withAppendedPath(target, "meow");
-        final Uri targetMeowCat = Uri.withAppendedPath(targetMeow, "cat");
-
-        final ClipData clip = makeSingleClipData(target);
-        final ClipData clipMeow = makeSingleClipData(targetMeow);
-        final ClipData clipMeowCat = makeSingleClipData(targetMeowCat);
-
-        // Make sure we can't see the target
-        assertReadingClipNotAllowed(clipMeow, "reading should have failed");
-        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
-
-        // Give ourselves some grants:
-        // /meow/cat  WRITE|PERSISTABLE
-        // /meow      READ|PREFIX
-        // /meow      WRITE
-        grantClipUriPermissionViaContext(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
-        grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION
-                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
-        grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
-        long before = System.currentTimeMillis();
-        resolver.takePersistableUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-        long after = System.currentTimeMillis();
-        assertPersistedUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, before, after);
-
-        // Verify they look good
-        assertReadingClipNotAllowed(clip, "reading should have failed");
-        assertReadingClipAllowed(clipMeow);
-        assertReadingClipAllowed(clipMeowCat);
-        assertWritingClipNotAllowed(clip, "writing should have failed");
-        assertWritingClipAllowed(clipMeow);
-        assertWritingClipAllowed(clipMeowCat);
-
-        // Revoke anyone with write under meow
-        getContext().revokeUriPermission(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
-        // This should have nuked persisted permission at lower level, but it
-        // shoulnd't have touched our prefix read.
-        assertReadingClipNotAllowed(clip, "reading should have failed");
-        assertReadingClipAllowed(clipMeow);
-        assertReadingClipAllowed(clipMeowCat);
-        assertWritingClipNotAllowed(clip, "writing should have failed");
-        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
-        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
-        assertNoPersistedUriPermission();
-
-        // Revoking read at top of tree should nuke everything else
-        getContext().revokeUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        assertReadingClipNotAllowed(clip, "reading should have failed");
-        assertReadingClipNotAllowed(clipMeow, "reading should have failed");
-        assertReadingClipNotAllowed(clipMeowCat, "reading should have failed");
-        assertWritingClipNotAllowed(clip, "writing should have failed");
-        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
-        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
-        assertNoPersistedUriPermission();
-    }
-
-    public void testClipboardWithPermission() throws Exception {
-        for (Uri target : GRANTABLE) {
-            final ClipData clip = makeSingleClipData(target);
-
-            // Normally we can't see the underlying clip data
-            assertReadingClipNotAllowed(clip);
-            assertWritingClipNotAllowed(clip);
-
-            // Use shell's permissions to ensure we can access the clipboard
-            InstrumentationRegistry.getInstrumentation().getUiAutomation()
-                    .adoptShellPermissionIdentity();
-            ClipData clipFromClipboard;
-            try {
-                // But if someone puts it on the clipboard, we can read it
-                setPrimaryClip(clip);
-                clipFromClipboard = getContext()
-                        .getSystemService(ClipboardManager.class).getPrimaryClip();
-            } finally {
-                InstrumentationRegistry.getInstrumentation().getUiAutomation()
-                        .dropShellPermissionIdentity();
-            }
-            assertClipDataEquals(clip, clipFromClipboard);
-            assertReadingClipAllowed(clipFromClipboard);
-            assertWritingClipNotAllowed(clipFromClipboard);
-
-            // And if clipboard is cleared, we lose access
-            clearPrimaryClip();
-            assertReadingClipNotAllowed(clipFromClipboard);
-            assertWritingClipNotAllowed(clipFromClipboard);
-        }
-    }
-
-    public void testClipboardWithoutPermission() throws Exception {
-        for (Uri target : NOT_GRANTABLE) {
-            final ClipData clip = makeSingleClipData(target);
-
-            // Can't see it directly
-            assertReadingClipNotAllowed(clip);
-            assertWritingClipNotAllowed(clip);
-
-            // Can't put on clipboard if we don't own it
-            try {
-                setPrimaryClip(clip);
-                fail("Unexpected ability to put protected data " + clip + " on clipboard!");
-            } catch (Exception expected) {
-            }
-        }
-    }
-
-    private static void assertClipDataEquals(ClipData expected, ClipData actual) {
-        assertEquals(expected.getItemCount(), actual.getItemCount());
-        for (int i = 0; i < expected.getItemCount(); i++) {
-            final ClipData.Item expectedItem = expected.getItemAt(i);
-            final ClipData.Item actualItem = actual.getItemAt(i);
-            assertEquals(expectedItem.getText(), actualItem.getText());
-            assertEquals(expectedItem.getHtmlText(), actualItem.getHtmlText());
-            assertEquals(expectedItem.getIntent(), actualItem.getIntent());
-            assertEquals(expectedItem.getUri(), actualItem.getUri());
-        }
-    }
 }
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/Asserts.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/Asserts.java
new file mode 100644
index 0000000..02483ca
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/Asserts.java
@@ -0,0 +1,315 @@
+/*
+ * 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.usespermissiondiffcertapp;
+
+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.ClipData;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.UriPermission;
+import android.database.Cursor;
+import android.net.Uri;
+
+import androidx.test.InstrumentationRegistry;
+
+import java.io.IOException;
+import java.util.List;
+
+public class Asserts {
+    private static Context getContext() {
+        return InstrumentationRegistry.getTargetContext();
+    }
+
+    static void assertAccess(ClipData clip, int mode) {
+        for (int i = 0; i < clip.getItemCount(); i++) {
+            ClipData.Item item = clip.getItemAt(i);
+            Uri uri = item.getUri();
+            if (uri != null) {
+                assertAccess(uri, mode);
+            } else {
+                Intent intent = item.getIntent();
+                uri = intent.getData();
+                if (uri != null) {
+                    assertAccess(uri, mode);
+                }
+                ClipData intentClip = intent.getClipData();
+                if (intentClip != null) {
+                    assertAccess(intentClip, mode);
+                }
+            }
+        }
+    }
+
+    static void assertAccess(Uri uri, int mode) {
+        if ((mode & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+            assertReadingContentUriAllowed(uri);
+        } else {
+            assertReadingContentUriNotAllowed(uri, null);
+        }
+        if ((mode & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+            assertWritingContentUriAllowed(uri);
+        } else {
+            assertWritingContentUriNotAllowed(uri, null);
+        }
+    }
+
+    static void assertReadingContentUriNotAllowed(Uri uri, String msg) {
+        try {
+            getContext().getContentResolver().query(uri, null, null, null, null);
+            fail("expected SecurityException reading " + uri + ": " + msg);
+        } catch (SecurityException expected) {
+            assertNotNull("security exception's error message.", expected.getMessage());
+        }
+    }
+
+    static void assertReadingContentUriAllowed(Uri uri) {
+        try {
+            getContext().getContentResolver().query(uri, null, null, null, null);
+        } catch (SecurityException e) {
+            fail("unexpected SecurityException reading " + uri + ": " + e.getMessage());
+        }
+    }
+
+    static void assertReadingClipNotAllowed(ClipData clip) {
+        assertReadingClipNotAllowed(clip, null);
+    }
+
+    static void assertReadingClipNotAllowed(ClipData clip, String msg) {
+        for (int i=0; i<clip.getItemCount(); i++) {
+            ClipData.Item item = clip.getItemAt(i);
+            Uri uri = item.getUri();
+            if (uri != null) {
+                assertReadingContentUriNotAllowed(uri, msg);
+            } else {
+                Intent intent = item.getIntent();
+                uri = intent.getData();
+                if (uri != null) {
+                    assertReadingContentUriNotAllowed(uri, msg);
+                }
+                ClipData intentClip = intent.getClipData();
+                if (intentClip != null) {
+                    assertReadingClipNotAllowed(intentClip, msg);
+                }
+            }
+        }
+    }
+
+    static void assertOpenFileDescriptorModeNotAllowed(Uri uri, String msg, String mode) {
+        try {
+            getContext().getContentResolver().openFileDescriptor(uri, mode).close();
+            fail("expected SecurityException writing " + uri + ": " + msg);
+        } catch (IOException e) {
+            throw new IllegalStateException(e);
+        } catch (SecurityException expected) {
+            assertNotNull("security exception's error message.", expected.getMessage());
+        }
+    }
+
+    static void assertContentUriAllowed(Uri uri) {
+        assertReadingContentUriAllowed(uri);
+        assertWritingContentUriAllowed(uri);
+    }
+
+    static void assertContentUriNotAllowed(Uri uri, String msg) {
+        assertReadingContentUriNotAllowed(uri, msg);
+        assertWritingContentUriNotAllowed(uri, msg);
+    }
+
+    static void assertWritingContentUriNotAllowed(Uri uri, String msg) {
+        final ContentResolver resolver = getContext().getContentResolver();
+        try {
+            resolver.insert(uri, new ContentValues());
+            fail("expected SecurityException inserting " + uri + ": " + msg);
+        } catch (SecurityException expected) {
+            assertNotNull("security exception's error message.", expected.getMessage());
+        }
+
+        try {
+            resolver.update(uri, new ContentValues(), null, null);
+            fail("expected SecurityException updating " + uri + ": " + msg);
+        } catch (SecurityException expected) {
+            assertNotNull("security exception's error message.", expected.getMessage());
+        }
+
+        try {
+            resolver.delete(uri, null, null);
+            fail("expected SecurityException deleting " + uri + ": " + msg);
+        } catch (SecurityException expected) {
+            assertNotNull("security exception's error message.", expected.getMessage());
+        }
+
+        try {
+            getContext().getContentResolver().openOutputStream(uri).close();
+            fail("expected SecurityException writing " + uri + ": " + msg);
+        } catch (IOException e) {
+            throw new IllegalStateException(e);
+        } catch (SecurityException expected) {
+            assertNotNull("security exception's error message.", expected.getMessage());
+        }
+
+        assertOpenFileDescriptorModeNotAllowed(uri, msg, "w");
+        assertOpenFileDescriptorModeNotAllowed(uri, msg, "wt");
+        assertOpenFileDescriptorModeNotAllowed(uri, msg, "wa");
+        assertOpenFileDescriptorModeNotAllowed(uri, msg, "rw");
+        assertOpenFileDescriptorModeNotAllowed(uri, msg, "rwt");
+    }
+
+    static void assertWritingContentUriAllowed(Uri uri) {
+        final ContentResolver resolver = getContext().getContentResolver();
+        try {
+            resolver.insert(uri, new ContentValues());
+            resolver.update(uri, new ContentValues(), null, null);
+            resolver.delete(uri, null, null);
+
+            resolver.openOutputStream(uri).close();
+            resolver.openFileDescriptor(uri, "w").close();
+            resolver.openFileDescriptor(uri, "wt").close();
+            resolver.openFileDescriptor(uri, "wa").close();
+            resolver.openFileDescriptor(uri, "rw").close();
+            resolver.openFileDescriptor(uri, "rwt").close();
+        } catch (IOException e) {
+            fail("unexpected IOException writing " + uri + ": " + e.getMessage());
+        } catch (SecurityException e) {
+            fail("unexpected SecurityException writing " + uri + ": " + e.getMessage());
+        }
+    }
+
+    static void assertWritingClipNotAllowed(ClipData clip) {
+        assertWritingClipNotAllowed(clip, null);
+    }
+
+    static void assertWritingClipNotAllowed(ClipData clip, String msg) {
+        for (int i=0; i<clip.getItemCount(); i++) {
+            ClipData.Item item = clip.getItemAt(i);
+            Uri uri = item.getUri();
+            if (uri != null) {
+                assertWritingContentUriNotAllowed(uri, msg);
+            } else {
+                Intent intent = item.getIntent();
+                uri = intent.getData();
+                if (uri != null) {
+                    assertWritingContentUriNotAllowed(uri, msg);
+                }
+                ClipData intentClip = intent.getClipData();
+                if (intentClip != null) {
+                    assertWritingClipNotAllowed(intentClip, msg);
+                }
+            }
+        }
+    }
+
+    static void assertReadingClipAllowed(ClipData clip) {
+        for (int i=0; i<clip.getItemCount(); i++) {
+            ClipData.Item item = clip.getItemAt(i);
+            Uri uri = item.getUri();
+            if (uri != null) {
+                Cursor c = getContext().getContentResolver().query(uri,
+                        null, null, null, null);
+                if (c != null) {
+                    c.close();
+                }
+            } else {
+                Intent intent = item.getIntent();
+                uri = intent.getData();
+                if (uri != null) {
+                    Cursor c = getContext().getContentResolver().query(uri,
+                            null, null, null, null);
+                    if (c != null) {
+                        c.close();
+                    }
+                }
+                ClipData intentClip = intent.getClipData();
+                if (intentClip != null) {
+                    assertReadingClipAllowed(intentClip);
+                }
+            }
+        }
+    }
+
+    static void assertWritingClipAllowed(ClipData clip) {
+        for (int i=0; i<clip.getItemCount(); i++) {
+            ClipData.Item item = clip.getItemAt(i);
+            Uri uri = item.getUri();
+            if (uri != null) {
+                getContext().getContentResolver().insert(uri, new ContentValues());
+            } else {
+                Intent intent = item.getIntent();
+                uri = intent.getData();
+                if (uri != null) {
+                    getContext().getContentResolver().insert(uri, new ContentValues());
+                }
+                ClipData intentClip = intent.getClipData();
+                if (intentClip != null) {
+                    assertWritingClipAllowed(intentClip);
+                }
+            }
+        }
+    }
+
+
+    static void assertClipDataEquals(ClipData expected, ClipData actual) {
+        assertEquals(expected.getItemCount(), actual.getItemCount());
+        for (int i = 0; i < expected.getItemCount(); i++) {
+            final ClipData.Item expectedItem = expected.getItemAt(i);
+            final ClipData.Item actualItem = actual.getItemAt(i);
+            assertEquals(expectedItem.getText(), actualItem.getText());
+            assertEquals(expectedItem.getHtmlText(), actualItem.getHtmlText());
+            assertEquals(expectedItem.getIntent(), actualItem.getIntent());
+            assertEquals(expectedItem.getUri(), actualItem.getUri());
+        }
+    }
+
+    static void assertNoPersistedUriPermission() {
+        assertPersistedUriPermission(null, 0, -1, -1);
+    }
+
+    static void assertPersistedUriPermission(Uri uri, int flags, long before, long after) {
+        // Assert local
+        final List<UriPermission> perms = getContext()
+                .getContentResolver().getPersistedUriPermissions();
+        if (uri != null) {
+            assertEquals("expected exactly one permission", 1, perms.size());
+
+            final UriPermission perm = perms.get(0);
+            assertEquals("unexpected uri", uri, perm.getUri());
+
+            final long actual = perm.getPersistedTime();
+            if (before != -1) {
+                assertTrue("found " + actual + " before " + before, actual >= before);
+            }
+            if (after != -1) {
+                assertTrue("found " + actual + " after " + after, actual <= after);
+            }
+
+            final boolean expectedRead = (flags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0;
+            final boolean expectedWrite = (flags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0;
+            assertEquals("unexpected read status", expectedRead, perm.isReadPermission());
+            assertEquals("unexpected write status", expectedWrite, perm.isWritePermission());
+
+        } else {
+            assertEquals("expected zero permissions", 0, perms.size());
+        }
+
+        Utils.verifyOutgoingPersisted(uri);
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/GetResultActivity.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/GetResultActivity.java
new file mode 100644
index 0000000..71e2cc1
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/GetResultActivity.java
@@ -0,0 +1,97 @@
+/*
+ * 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.usespermissiondiffcertapp;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.WindowManager;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class GetResultActivity extends Activity {
+    private static final String TAG = "GetResultActivity";
+
+    private LinkedBlockingQueue<Result> mResult;
+    private CountDownLatch mDestroyed;
+
+    public static class Result {
+        public final int requestCode;
+        public final int resultCode;
+        public final Intent data;
+
+        public Result(int requestCode, int resultCode, Intent data) {
+            this.requestCode = requestCode;
+            this.resultCode = resultCode;
+            this.data = data;
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+                | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+
+        if (mDestroyed != null) {
+            mDestroyed.countDown();
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        Log.d(TAG, "onActivityResult " + data);
+        try {
+            mResult.offer(new Result(requestCode, resultCode, data), 5, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public void clearResult() {
+        mResult = new LinkedBlockingQueue<>();
+        mDestroyed = new CountDownLatch(1);
+    }
+
+    public Result getResult() {
+        try {
+            return mResult.poll(5, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public void getDestroyed() {
+        try {
+            mDestroyed.await(5, TimeUnit.SECONDS);
+            SystemClock.sleep(200);
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsActivityTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsActivityTest.java
new file mode 100644
index 0000000..26a2eee
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsActivityTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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.usespermissiondiffcertapp;
+
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.GRANTABLE;
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.GRANTABLE_MODES;
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.NOT_GRANTABLE;
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.NOT_GRANTABLE_MODES;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertAccess;
+import static com.android.cts.usespermissiondiffcertapp.UriGrantsTest.TAG;
+import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermission;
+
+import static junit.framework.Assert.fail;
+
+import android.content.ClipData;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.SystemClock;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Function;
+
+@RunWith(AndroidJUnit4.class)
+public class UriGrantsActivityTest {
+    private static Context getContext() {
+        return InstrumentationRegistry.getTargetContext();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        // Always dispose, usually to clean up from failed tests
+        ReceiveUriActivity.finishCurInstanceSync();
+    }
+
+    @Test
+    public void testGrantableToActivity() {
+        for (Uri uri : GRANTABLE) {
+            for (int mode : GRANTABLE_MODES) {
+                Log.d(TAG, "Testing " + uri + " " + mode);
+                assertGrantableToActivity(uri, mode, UriGrantsTest::makeSingleClipData);
+                assertGrantableToActivity(uri, mode, UriGrantsTest::makeMultiClipData);
+            }
+        }
+    }
+
+    private void assertGrantableToActivity(Uri uri, int mode, Function<Uri, ClipData> clipper) {
+        final Uri subUri = Uri.withAppendedPath(uri, "foo");
+        final Uri subSubUri = Uri.withAppendedPath(subUri, "bar");
+        final Uri sub2Uri = Uri.withAppendedPath(uri, "yes");
+        final Uri sub2SubUri = Uri.withAppendedPath(sub2Uri, "no");
+
+        final ClipData subClip = clipper.apply(subUri);
+        final ClipData sub2Clip = clipper.apply(sub2Uri);
+
+        assertAccess(uri, 0);
+        assertAccess(subClip, 0);
+        assertAccess(subUri, 0);
+        assertAccess(subSubUri, 0);
+        assertAccess(sub2Clip, 0);
+        assertAccess(sub2Uri, 0);
+        assertAccess(sub2SubUri, 0);
+
+        // --------------------------------
+
+        ReceiveUriActivity.clearStarted();
+        grantClipUriPermission(subClip, mode, false);
+        ReceiveUriActivity.waitForStart();
+
+        assertAccess(uri, 0);
+        assertAccess(subClip, mode);
+        assertAccess(subUri, mode);
+        assertAccess(subSubUri, 0);
+        assertAccess(sub2Clip, 0);
+        assertAccess(sub2Uri, 0);
+        assertAccess(sub2SubUri, 0);
+
+        // --------------------------------
+
+        ReceiveUriActivity.clearNewIntent();
+        grantClipUriPermission(sub2Clip, mode, false);
+        ReceiveUriActivity.waitForNewIntent();
+
+        assertAccess(uri, 0);
+        assertAccess(subClip, mode);
+        assertAccess(subUri, mode);
+        assertAccess(subSubUri, 0);
+        assertAccess(sub2Clip, mode);
+        assertAccess(sub2Uri, mode);
+        assertAccess(sub2SubUri, 0);
+
+        // And make sure we can't generate a permission to a running activity.
+        assertNotGrantableToActivity(Uri.withAppendedPath(uri, "hah"), mode, clipper);
+
+        // --------------------------------
+
+        // Dispose of activity.
+        ReceiveUriActivity.finishCurInstanceSync();
+        SystemClock.sleep(200);
+
+        assertAccess(uri, 0);
+        assertAccess(subClip, 0);
+        assertAccess(subUri, 0);
+        assertAccess(subSubUri, 0);
+        assertAccess(sub2Clip, 0);
+        assertAccess(sub2Uri, 0);
+        assertAccess(sub2SubUri, 0);
+    }
+
+    @Test
+    public void testNotGrantableToActivity() {
+        for (Uri uri : NOT_GRANTABLE) {
+            for (int mode : NOT_GRANTABLE_MODES) {
+                Log.d(TAG, "Testing " + uri + " " + mode);
+                assertNotGrantableToActivity(uri, mode, UriGrantsTest::makeSingleClipData);
+                assertNotGrantableToActivity(uri, mode, UriGrantsTest::makeMultiClipData);
+            }
+        }
+    }
+
+    private void assertNotGrantableToActivity(Uri uri, int mode, Function<Uri, ClipData> clipper) {
+        final Uri subUri = Uri.withAppendedPath(uri, "foo");
+        final ClipData subClip = clipper.apply(subUri);
+
+        Intent grantIntent = new Intent();
+        grantIntent.setClipData(subClip);
+        grantIntent.addFlags(mode | Intent.FLAG_ACTIVITY_NEW_TASK);
+        grantIntent.setClass(getContext(), ReceiveUriActivity.class);
+        try {
+            ReceiveUriActivity.clearStarted();
+            getContext().startActivity(grantIntent);
+            ReceiveUriActivity.waitForStart();
+            fail("expected SecurityException granting " + subClip + " to activity");
+        } catch (SecurityException e) {
+            // This is what we want.
+        }
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsClipboardTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsClipboardTest.java
new file mode 100644
index 0000000..4e6cfb8
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsClipboardTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.usespermissiondiffcertapp;
+
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.GRANTABLE;
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.NOT_GRANTABLE;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertClipDataEquals;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertReadingClipAllowed;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertReadingClipNotAllowed;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertWritingClipNotAllowed;
+import static com.android.cts.usespermissiondiffcertapp.UriGrantsTest.TAG;
+import static com.android.cts.usespermissiondiffcertapp.UriGrantsTest.makeSingleClipData;
+import static com.android.cts.usespermissiondiffcertapp.Utils.clearPrimaryClip;
+import static com.android.cts.usespermissiondiffcertapp.Utils.setPrimaryClip;
+
+import static junit.framework.Assert.fail;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.net.Uri;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class UriGrantsClipboardTest {
+    private static Context getContext() {
+        return InstrumentationRegistry.getTargetContext();
+    }
+
+    @Test
+    public void testClipboardWithPermission() throws Exception {
+        for (Uri target : GRANTABLE) {
+            Log.d(TAG, "Testing " + target);
+            final ClipData clip = makeSingleClipData(target);
+
+            // Normally we can't see the underlying clip data
+            assertReadingClipNotAllowed(clip);
+            assertWritingClipNotAllowed(clip);
+
+            // Use shell's permissions to ensure we can access the clipboard
+            InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                    .adoptShellPermissionIdentity();
+            ClipData clipFromClipboard;
+            try {
+                // But if someone puts it on the clipboard, we can read it
+                setPrimaryClip(clip);
+                clipFromClipboard = getContext()
+                        .getSystemService(ClipboardManager.class).getPrimaryClip();
+            } finally {
+                InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                        .dropShellPermissionIdentity();
+            }
+            assertClipDataEquals(clip, clipFromClipboard);
+            assertReadingClipAllowed(clipFromClipboard);
+            assertWritingClipNotAllowed(clipFromClipboard);
+
+            // And if clipboard is cleared, we lose access
+            clearPrimaryClip();
+            assertReadingClipNotAllowed(clipFromClipboard);
+            assertWritingClipNotAllowed(clipFromClipboard);
+        }
+    }
+
+    @Test
+    public void testClipboardWithoutPermission() throws Exception {
+        for (Uri target : NOT_GRANTABLE) {
+            Log.d(TAG, "Testing " + target);
+            final ClipData clip = makeSingleClipData(target);
+
+            // Can't see it directly
+            assertReadingClipNotAllowed(clip);
+            assertWritingClipNotAllowed(clip);
+
+            // Can't put on clipboard if we don't own it
+            try {
+                setPrimaryClip(clip);
+                fail("Unexpected ability to put protected data " + clip + " on clipboard!");
+            } catch (Exception expected) {
+            }
+        }
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsResultTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsResultTest.java
new file mode 100644
index 0000000..a8eea3e
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsResultTest.java
@@ -0,0 +1,165 @@
+/*
+ * 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.usespermissiondiffcertapp;
+
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.GRANTABLE;
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.GRANTABLE_MODES;
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.NOT_GRANTABLE;
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.NOT_GRANTABLE_MODES;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertAccess;
+import static com.android.cts.usespermissiondiffcertapp.UriGrantsTest.TAG;
+
+import android.app.Instrumentation;
+import android.content.ClipData;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Function;
+
+@RunWith(AndroidJUnit4.class)
+public class UriGrantsResultTest {
+    private static final int REQUEST_CODE = 42;
+
+    private Instrumentation mInstrumentation;
+    private Context mContext;
+    private GetResultActivity mActivity;
+
+    public void createActivity() throws Exception {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mContext = InstrumentationRegistry.getTargetContext();
+
+        final Intent intent = new Intent(mContext, GetResultActivity.class);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mActivity = (GetResultActivity) mInstrumentation.startActivitySync(intent);
+        mInstrumentation.waitForIdleSync();
+        mActivity.clearResult();
+    }
+
+    public void destroyActivity() throws Exception {
+        mActivity.finish();
+    }
+
+    @Test
+    public void testGrantableToResult() throws Exception {
+        for (Uri uri : GRANTABLE) {
+            for (int mode : GRANTABLE_MODES) {
+                Log.d(TAG, "Testing " + uri + " " + mode);
+                assertGrantableToResult(uri, mode, UriGrantsTest::makeSingleClipData);
+                assertGrantableToResult(uri, mode, UriGrantsTest::makeMultiClipData);
+            }
+        }
+    }
+
+    private void assertGrantableToResult(Uri uri, int mode,
+            Function<Uri, ClipData> clipper) throws Exception {
+        try {
+            createActivity();
+            assertGrantableToResultInternal(uri, mode, clipper);
+        } finally {
+            destroyActivity();
+        }
+    }
+
+    private void assertGrantableToResultInternal(Uri uri, int mode,
+            Function<Uri, ClipData> clipper) {
+        final Uri subUri = Uri.withAppendedPath(uri, "foo");
+        final Uri subSubUri = Uri.withAppendedPath(subUri, "bar");
+        final ClipData subClip = clipper.apply(subUri);
+
+        assertAccess(uri, 0);
+        assertAccess(subClip, 0);
+        assertAccess(subUri, 0);
+        assertAccess(subSubUri, 0);
+
+        // --------------------------------
+
+        final Intent intent = buildIntent(subClip, mode);
+        mActivity.startActivityForResult(intent, REQUEST_CODE);
+        mActivity.getResult();
+
+        assertAccess(uri, 0);
+        assertAccess(subClip, mode);
+        assertAccess(subUri, mode);
+        assertAccess(subSubUri, 0);
+
+        // --------------------------------
+
+        // Dispose of activity.
+        mActivity.finish();
+        mActivity.getDestroyed();
+
+        assertAccess(uri, 0);
+        assertAccess(subClip, 0);
+        assertAccess(subUri, 0);
+        assertAccess(subSubUri, 0);
+    }
+
+    @Test
+    public void testNotGrantableToResult() throws Exception {
+        for (Uri uri : NOT_GRANTABLE) {
+            for (int mode : NOT_GRANTABLE_MODES) {
+                Log.d(TAG, "Testing " + uri + " " + mode);
+                assertNotGrantableToResult(uri, mode, UriGrantsTest::makeSingleClipData);
+                assertNotGrantableToResult(uri, mode, UriGrantsTest::makeMultiClipData);
+            }
+        }
+    }
+
+    private void assertNotGrantableToResult(Uri uri, int mode,
+            Function<Uri, ClipData> clipper) throws Exception {
+        try {
+            createActivity();
+            assertNotGrantableToResultInternal(uri, mode, clipper);
+        } finally {
+            destroyActivity();
+        }
+    }
+
+    private void assertNotGrantableToResultInternal(Uri uri, int mode,
+            Function<Uri, ClipData> clipper) {
+        final Uri subUri = Uri.withAppendedPath(uri, "foo");
+        final Uri subSubUri = Uri.withAppendedPath(subUri, "bar");
+        final ClipData subClip = clipper.apply(subUri);
+
+        final Intent intent = buildIntent(subClip, mode);
+        mActivity.startActivityForResult(intent, REQUEST_CODE);
+        mActivity.getResult();
+
+        assertAccess(uri, 0);
+        assertAccess(subClip, 0);
+        assertAccess(subUri, 0);
+        assertAccess(subSubUri, 0);
+    }
+
+    private Intent buildIntent(ClipData clip, int mode) {
+        final Intent intent = new Intent();
+        intent.setComponent(new ComponentName("com.android.cts.permissiondeclareapp",
+                "com.android.cts.permissiondeclareapp.SendResultActivity"));
+        intent.putExtra(Intent.EXTRA_TEXT, clip);
+        intent.putExtra(Intent.EXTRA_INDEX, mode);
+        return intent;
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsServiceTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsServiceTest.java
new file mode 100644
index 0000000..7be059f
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsServiceTest.java
@@ -0,0 +1,166 @@
+/*
+ * 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.usespermissiondiffcertapp;
+
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.GRANTABLE;
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.GRANTABLE_MODES;
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.NOT_GRANTABLE;
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.NOT_GRANTABLE_MODES;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertAccess;
+import static com.android.cts.usespermissiondiffcertapp.UriGrantsTest.TAG;
+import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermission;
+
+import static junit.framework.Assert.fail;
+
+import android.content.ClipData;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Function;
+
+@RunWith(AndroidJUnit4.class)
+public class UriGrantsServiceTest {
+    private static Context getContext() {
+        return InstrumentationRegistry.getTargetContext();
+    }
+
+    @Test
+    public void testGrantableToService() {
+        for (Uri uri : GRANTABLE) {
+            for (int mode : GRANTABLE_MODES) {
+                Log.d(TAG, "Testing " + uri + " " + mode);
+                assertGrantableToService(uri, mode, UriGrantsTest::makeSingleClipData);
+                assertGrantableToService(uri, mode, UriGrantsTest::makeMultiClipData);
+            }
+        }
+    }
+
+    private void assertGrantableToService(Uri uri, int mode, Function<Uri, ClipData> clipper) {
+        final Uri subUri = Uri.withAppendedPath(uri, "foo");
+        final Uri subSubUri = Uri.withAppendedPath(subUri, "bar");
+        final Uri sub2Uri = Uri.withAppendedPath(uri, "yes");
+        final Uri sub2SubUri = Uri.withAppendedPath(sub2Uri, "no");
+
+        ReceiveUriService.stop(getContext());
+
+        final ClipData subClip = clipper.apply(subUri);
+        final ClipData sub2Clip = clipper.apply(sub2Uri);
+
+        assertAccess(uri, 0);
+        assertAccess(subClip, 0);
+        assertAccess(subUri, 0);
+        assertAccess(subSubUri, 0);
+        assertAccess(sub2Clip, 0);
+        assertAccess(sub2Uri, 0);
+        assertAccess(sub2SubUri, 0);
+
+        // --------------------------------
+
+        ReceiveUriService.clearStarted();
+        grantClipUriPermission(subClip, mode, true);
+        ReceiveUriService.waitForStart();
+
+        int firstStartId = ReceiveUriService.getCurStartId();
+
+        assertAccess(uri, 0);
+        assertAccess(subClip, mode);
+        assertAccess(subUri, mode);
+        assertAccess(subSubUri, 0);
+        assertAccess(sub2Clip, 0);
+        assertAccess(sub2Uri, 0);
+        assertAccess(sub2SubUri, 0);
+
+        // --------------------------------
+
+        // Send another Intent to it.
+        ReceiveUriService.clearStarted();
+        grantClipUriPermission(sub2Clip, mode, true);
+        ReceiveUriService.waitForStart();
+
+        assertAccess(uri, 0);
+        assertAccess(subClip, mode);
+        assertAccess(subUri, mode);
+        assertAccess(subSubUri, 0);
+        assertAccess(sub2Clip, mode);
+        assertAccess(sub2Uri, mode);
+        assertAccess(sub2SubUri, 0);
+
+        // And make sure we can't generate a permission to a running service.
+        assertNotGrantableToService(Uri.withAppendedPath(uri, "hah"), mode, clipper);
+
+        // --------------------------------
+
+        // Stop the first command.
+        ReceiveUriService.stopCurWithId(firstStartId);
+
+        assertAccess(uri, 0);
+        assertAccess(subClip, 0);
+        assertAccess(subUri, 0);
+        assertAccess(subSubUri, 0);
+        assertAccess(sub2Clip, mode);
+        assertAccess(sub2Uri, mode);
+        assertAccess(sub2SubUri, 0);
+
+        // --------------------------------
+
+        // Dispose of service.
+        ReceiveUriService.stopSync(getContext());
+
+        assertAccess(uri, 0);
+        assertAccess(subClip, 0);
+        assertAccess(subUri, 0);
+        assertAccess(subSubUri, 0);
+        assertAccess(sub2Clip, 0);
+        assertAccess(sub2Uri, 0);
+        assertAccess(sub2SubUri, 0);
+    }
+
+    @Test
+    public void testNotGrantableToService() {
+        for (Uri uri : NOT_GRANTABLE) {
+            for (int mode : NOT_GRANTABLE_MODES) {
+                Log.d(TAG, "Testing " + uri + " " + mode);
+                assertNotGrantableToService(uri, mode, UriGrantsTest::makeSingleClipData);
+                assertNotGrantableToService(uri, mode, UriGrantsTest::makeMultiClipData);
+            }
+        }
+    }
+
+    private void assertNotGrantableToService(Uri uri, int mode, Function<Uri, ClipData> clipper) {
+        final Uri subUri = Uri.withAppendedPath(uri, "foo");
+        final ClipData subClip = clipper.apply(subUri);
+
+        Intent grantIntent = new Intent();
+        grantIntent.setClipData(subClip);
+        grantIntent.addFlags(mode);
+        grantIntent.setClass(getContext(), ReceiveUriService.class);
+        try {
+            getContext().startService(grantIntent);
+            fail("expected SecurityException granting " + subClip + " to service");
+        } catch (SecurityException e) {
+            // This is what we want.
+        }
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsTest.java
new file mode 100644
index 0000000..a955dbc
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsTest.java
@@ -0,0 +1,402 @@
+/*
+ * 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.usespermissiondiffcertapp;
+
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.PERM_URI_GRANTING;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertNoPersistedUriPermission;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertPersistedUriPermission;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertReadingClipAllowed;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertReadingClipNotAllowed;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertWritingClipAllowed;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertWritingClipNotAllowed;
+import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermission;
+import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermissionViaContext;
+import static com.android.cts.usespermissiondiffcertapp.Utils.revokeClipUriPermissionViaContext;
+
+import static junit.framework.Assert.fail;
+
+import android.content.ClipData;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class UriGrantsTest {
+    static final String TAG = "UriGrantsTest";
+
+    private static Context getContext() {
+        return InstrumentationRegistry.getTargetContext();
+    }
+
+    static ClipData makeSingleClipData(Uri uri) {
+        return new ClipData("foo", new String[] { "foo/bar" },
+                new ClipData.Item(uri));
+    }
+
+    static ClipData makeMultiClipData(Uri uri) {
+        Uri grantClip1Uri = uri;
+        Uri grantClip2Uri = Uri.withAppendedPath(uri, "clip2");
+        Uri grantClip3Uri = Uri.withAppendedPath(uri, "clip3");
+        Uri grantClip4Uri = Uri.withAppendedPath(uri, "clip4");
+        Uri grantClip5Uri = Uri.withAppendedPath(uri, "clip5");
+        ClipData clip = new ClipData("foo", new String[] { "foo/bar" },
+                new ClipData.Item(grantClip1Uri));
+        clip.addItem(new ClipData.Item(grantClip2Uri));
+        // Intents in the ClipData should allow their data: and clip URIs
+        // to be granted, but only respect the grant flags of the top-level
+        // Intent.
+        clip.addItem(new ClipData.Item(new Intent(Intent.ACTION_VIEW, grantClip3Uri)));
+        Intent intent = new Intent(Intent.ACTION_VIEW, grantClip4Uri);
+        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        clip.addItem(new ClipData.Item(intent));
+        intent = new Intent(Intent.ACTION_VIEW);
+        intent.setClipData(new ClipData("foo", new String[] { "foo/bar" },
+                new ClipData.Item(grantClip5Uri)));
+        clip.addItem(new ClipData.Item(intent));
+        return clip;
+    }
+
+    /**
+     * Validate behavior of persistable permission grants.
+     */
+    @Test
+    public void testGrantPersistableUriPermission() {
+        final ContentResolver resolver = getContext().getContentResolver();
+
+        final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo");
+        final ClipData clip = makeSingleClipData(target);
+
+        // Make sure we can't see the target
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+
+        // Make sure we can't take a grant we don't have
+        try {
+            resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            fail("taking read should have failed");
+        } catch (SecurityException expected) {
+        }
+
+        // And since we were just installed, no persisted grants yet
+        assertNoPersistedUriPermission();
+
+        // Now, let's grant ourselves some access
+        ReceiveUriActivity.clearStarted();
+        grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false);
+        ReceiveUriActivity.waitForStart();
+
+        // We should now have reading access, even before taking the persistable
+        // grant. Persisted grants should still be empty.
+        assertReadingClipAllowed(clip);
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+        assertNoPersistedUriPermission();
+
+        // Take the read grant and verify we have it!
+        long before = System.currentTimeMillis();
+        resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        long after = System.currentTimeMillis();
+        assertPersistedUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION, before, after);
+
+        // Make sure we can't take a grant we don't have
+        try {
+            resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+            fail("taking write should have failed");
+        } catch (SecurityException expected) {
+        }
+
+        // Launch again giving ourselves persistable read and write access
+        ReceiveUriActivity.clearNewIntent();
+        grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false);
+        ReceiveUriActivity.waitForNewIntent();
+
+        // Previous persisted grant should be unchanged
+        assertPersistedUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION, before, after);
+
+        // We should have both read and write; read is persisted, and write
+        // isn't persisted yet.
+        assertReadingClipAllowed(clip);
+        assertWritingClipAllowed(clip);
+
+        // Take again, but still only read; should just update timestamp
+        before = System.currentTimeMillis();
+        resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        after = System.currentTimeMillis();
+        assertPersistedUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION, before, after);
+
+        // And take yet again, both read and write
+        before = System.currentTimeMillis();
+        resolver.takePersistableUriPermission(target,
+                Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        after = System.currentTimeMillis();
+        assertPersistedUriPermission(target,
+                Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+                before, after);
+
+        // Now drop the persisted grant; write first, then read
+        resolver.releasePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        assertPersistedUriPermission(target, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, before, after);
+        resolver.releasePersistableUriPermission(target, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        assertNoPersistedUriPermission();
+
+        // And even though we dropped the persistable grants, our activity is
+        // still running with the global grants (until reboot).
+        assertReadingClipAllowed(clip);
+        assertWritingClipAllowed(clip);
+
+        ReceiveUriActivity.finishCurInstanceSync();
+    }
+
+    /**
+     * Validate behavior of prefix permission grants.
+     */
+    @Test
+    public void testGrantPrefixUriPermission() throws Exception {
+        final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo1");
+        final Uri targetMeow = Uri.withAppendedPath(target, "meow");
+        final Uri targetMeowCat = Uri.withAppendedPath(targetMeow, "cat");
+
+        final ClipData clip = makeSingleClipData(target);
+        final ClipData clipMeow = makeSingleClipData(targetMeow);
+        final ClipData clipMeowCat = makeSingleClipData(targetMeowCat);
+
+        // Make sure we can't see the target
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+
+        // Give ourselves prefix read access
+        ReceiveUriActivity.clearStarted();
+        grantClipUriPermission(clipMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
+        ReceiveUriActivity.waitForStart();
+
+        // Verify prefix read access
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertReadingClipAllowed(clipMeow);
+        assertReadingClipAllowed(clipMeowCat);
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+
+        // Now give ourselves exact write access
+        ReceiveUriActivity.clearNewIntent();
+        grantClipUriPermission(clip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false);
+        ReceiveUriActivity.waitForNewIntent();
+
+        // Verify we have exact write access, but not prefix write
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertReadingClipAllowed(clipMeow);
+        assertReadingClipAllowed(clipMeowCat);
+        assertWritingClipAllowed(clip);
+        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+
+        ReceiveUriActivity.finishCurInstanceSync();
+    }
+
+    @Test
+    public void testGrantPersistablePrefixUriPermission() {
+        final ContentResolver resolver = getContext().getContentResolver();
+
+        final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo2");
+        final Uri targetMeow = Uri.withAppendedPath(target, "meow");
+
+        final ClipData clip = makeSingleClipData(target);
+        final ClipData clipMeow = makeSingleClipData(targetMeow);
+
+        // Make sure we can't see the target
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+
+        // Give ourselves prefix read access
+        ReceiveUriActivity.clearStarted();
+        grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
+        ReceiveUriActivity.waitForStart();
+
+        // Verify prefix read access
+        assertReadingClipAllowed(clip);
+        assertReadingClipAllowed(clipMeow);
+
+        // Verify we can persist direct grant
+        long before = System.currentTimeMillis();
+        resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        long after = System.currentTimeMillis();
+        assertPersistedUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION, before, after);
+
+        // But we can't take anywhere under the prefix
+        try {
+            resolver.takePersistableUriPermission(targetMeow,
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            fail("taking under prefix should have failed");
+        } catch (SecurityException expected) {
+        }
+
+        // Should still have access regardless of taking
+        assertReadingClipAllowed(clip);
+        assertReadingClipAllowed(clipMeow);
+
+        // And clean up our grants
+        resolver.releasePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        assertNoPersistedUriPermission();
+
+        ReceiveUriActivity.finishCurInstanceSync();
+    }
+
+    /**
+     * Validate behavior of directly granting/revoking permission grants.
+     */
+    @Test
+    public void testDirectGrantRevokeUriPermission() throws Exception {
+        final ContentResolver resolver = getContext().getContentResolver();
+
+        final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo3");
+        final Uri targetMeow = Uri.withAppendedPath(target, "meow");
+        final Uri targetMeowCat = Uri.withAppendedPath(targetMeow, "cat");
+
+        final ClipData clip = makeSingleClipData(target);
+        final ClipData clipMeow = makeSingleClipData(targetMeow);
+        final ClipData clipMeowCat = makeSingleClipData(targetMeowCat);
+
+        // Make sure we can't see the target
+        assertReadingClipNotAllowed(clipMeow, "reading should have failed");
+        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+
+        // Give ourselves some grants:
+        // /meow/cat  WRITE|PERSISTABLE
+        // /meow      READ|PREFIX
+        // /meow      WRITE
+        grantClipUriPermissionViaContext(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
+        grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+        grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+        long before = System.currentTimeMillis();
+        resolver.takePersistableUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        long after = System.currentTimeMillis();
+        assertPersistedUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, before, after);
+
+        // Verify they look good
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertReadingClipAllowed(clipMeow);
+        assertReadingClipAllowed(clipMeowCat);
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+        assertWritingClipAllowed(clipMeow);
+        assertWritingClipAllowed(clipMeowCat);
+
+        // Revoke anyone with write under meow
+        revokeClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+        // This should have nuked persisted permission at lower level, but it
+        // shoulnd't have touched our prefix read.
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertReadingClipAllowed(clipMeow);
+        assertReadingClipAllowed(clipMeowCat);
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+        assertNoPersistedUriPermission();
+
+        // Revoking read at top of tree should nuke everything else
+        revokeClipUriPermissionViaContext(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertReadingClipNotAllowed(clipMeow, "reading should have failed");
+        assertReadingClipNotAllowed(clipMeowCat, "reading should have failed");
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+        assertNoPersistedUriPermission();
+    }
+
+    /**
+     * Validate behavior of a direct permission grant, where the receiver of
+     * that permission revokes it.
+     */
+    @Test
+    public void testDirectGrantReceiverRevokeUriPermission() throws Exception {
+        final ContentResolver resolver = getContext().getContentResolver();
+
+        final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo3");
+        final Uri targetMeow = Uri.withAppendedPath(target, "meow");
+        final Uri targetMeowCat = Uri.withAppendedPath(targetMeow, "cat");
+
+        final ClipData clip = makeSingleClipData(target);
+        final ClipData clipMeow = makeSingleClipData(targetMeow);
+        final ClipData clipMeowCat = makeSingleClipData(targetMeowCat);
+
+        // Make sure we can't see the target
+        assertReadingClipNotAllowed(clipMeow, "reading should have failed");
+        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+
+        // Give ourselves some grants:
+        // /meow/cat  WRITE|PERSISTABLE
+        // /meow      READ|PREFIX
+        // /meow      WRITE
+        grantClipUriPermissionViaContext(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
+        grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+        grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+        long before = System.currentTimeMillis();
+        resolver.takePersistableUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        long after = System.currentTimeMillis();
+        assertPersistedUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, before, after);
+
+        // Verify they look good
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertReadingClipAllowed(clipMeow);
+        assertReadingClipAllowed(clipMeowCat);
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+        assertWritingClipAllowed(clipMeow);
+        assertWritingClipAllowed(clipMeowCat);
+
+        // Revoke anyone with write under meow
+        getContext().revokeUriPermission(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+        // This should have nuked persisted permission at lower level, but it
+        // shoulnd't have touched our prefix read.
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertReadingClipAllowed(clipMeow);
+        assertReadingClipAllowed(clipMeowCat);
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+        assertNoPersistedUriPermission();
+
+        // Revoking read at top of tree should nuke everything else
+        getContext().revokeUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertReadingClipNotAllowed(clipMeow, "reading should have failed");
+        assertReadingClipNotAllowed(clipMeowCat, "reading should have failed");
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+        assertNoPersistedUriPermission();
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/Utils.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/Utils.java
new file mode 100644
index 0000000..48cac57
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/Utils.java
@@ -0,0 +1,111 @@
+/*
+ * 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.usespermissiondiffcertapp;
+
+import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_CLEAR_PRIMARY_CLIP;
+import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_GRANT_URI;
+import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_REVOKE_URI;
+import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_SET_PRIMARY_CLIP;
+import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_START_ACTIVITY;
+import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_START_SERVICE;
+import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_VERIFY_OUTGOING_PERSISTED;
+import static com.android.cts.permissiondeclareapp.UtilsProvider.EXTRA_INTENT;
+import static com.android.cts.permissiondeclareapp.UtilsProvider.EXTRA_MODE;
+import static com.android.cts.permissiondeclareapp.UtilsProvider.EXTRA_PACKAGE_NAME;
+import static com.android.cts.permissiondeclareapp.UtilsProvider.EXTRA_URI;
+
+import android.content.ClipData;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.cts.permissiondeclareapp.UtilsProvider;
+
+public class Utils {
+    private static Context getContext() {
+        return InstrumentationRegistry.getTargetContext();
+    }
+
+    private static void call(Intent intent) {
+        final Bundle extras = new Bundle();
+        extras.putParcelable(Intent.EXTRA_INTENT, intent);
+        getContext().getContentResolver().call(UtilsProvider.URI, "", "", extras);
+    }
+
+    static void grantClipUriPermission(ClipData clip, int mode, boolean service) {
+        Intent grantIntent = new Intent();
+        if (clip.getItemCount() == 1) {
+            grantIntent.setData(clip.getItemAt(0).getUri());
+        } else {
+            grantIntent.setClipData(clip);
+            // Make this Intent unique from the one that started it.
+            for (int i=0; i<clip.getItemCount(); i++) {
+                Uri uri = clip.getItemAt(i).getUri();
+                if (uri != null) {
+                    grantIntent.addCategory(uri.toString());
+                }
+            }
+        }
+        grantIntent.addFlags(mode);
+        grantIntent.setClass(getContext(),
+                service ? ReceiveUriService.class : ReceiveUriActivity.class);
+        Intent intent = new Intent();
+        intent.setAction(service ? ACTION_START_SERVICE : ACTION_START_ACTIVITY);
+        intent.putExtra(EXTRA_INTENT, grantIntent);
+        call(intent);
+    }
+
+    static void grantClipUriPermissionViaContext(Uri uri, int mode) {
+        Intent intent = new Intent();
+        intent.setAction(ACTION_GRANT_URI);
+        intent.putExtra(EXTRA_PACKAGE_NAME, getContext().getPackageName());
+        intent.putExtra(EXTRA_URI, uri);
+        intent.putExtra(EXTRA_MODE, mode);
+        call(intent);
+    }
+
+    static void revokeClipUriPermissionViaContext(Uri uri, int mode) {
+        Intent intent = new Intent();
+        intent.setAction(ACTION_REVOKE_URI);
+        intent.putExtra(EXTRA_URI, uri);
+        intent.putExtra(EXTRA_MODE, mode);
+        call(intent);
+    }
+
+    static void setPrimaryClip(ClipData clip) {
+        Intent intent = new Intent();
+        intent.setAction(ACTION_SET_PRIMARY_CLIP);
+        intent.setClipData(clip);
+        call(intent);
+    }
+
+    static void clearPrimaryClip() {
+        Intent intent = new Intent();
+        intent.setAction(ACTION_CLEAR_PRIMARY_CLIP);
+        call(intent);
+    }
+
+    static void verifyOutgoingPersisted(Uri uri) {
+        Intent intent = new Intent();
+        intent.setAction(ACTION_VERIFY_OUTGOING_PERSISTED);
+        intent.putExtra(EXTRA_URI, uri);
+        call(intent);
+    }
+}
diff --git a/hostsidetests/backup/AutoRestoreApp/Android.bp b/hostsidetests/backup/AutoRestoreApp/Android.bp
new file mode 100644
index 0000000..227fe13
--- /dev/null
+++ b/hostsidetests/backup/AutoRestoreApp/Android.bp
@@ -0,0 +1,34 @@
+// 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.
+
+android_test_helper_app {
+    name: "CtsAutoRestoreApp",
+    defaults: ["cts_defaults"],
+    static_libs: [
+        "androidx.test.rules",
+        "platform-test-annotations",
+        "testng",
+        "truth-prebuilt",
+        "compatibility-device-util-axt",
+    ],
+    srcs: ["src/**/*.java"],
+    // tag this module as a cts test artifact
+    test_suites: [
+        "arcts",
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    platform_apis: true,
+}
diff --git a/libs/rollback/testapp/res_v1/values/values.xml b/hostsidetests/backup/AutoRestoreApp/AndroidManifest.xml
similarity index 61%
copy from libs/rollback/testapp/res_v1/values/values.xml
copy to hostsidetests/backup/AutoRestoreApp/AndroidManifest.xml
index 8e71917..de6fa63 100644
--- a/libs/rollback/testapp/res_v1/values/values.xml
+++ b/hostsidetests/backup/AutoRestoreApp/AndroidManifest.xml
@@ -14,6 +14,15 @@
      limitations under the License.
 -->
 
-<resources>
-    <integer name="app_version">1</integer>
-</resources>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.cts.backup.autorestoreapp">
+
+    <application android:label="AutoRestoreApp" >
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.cts.backup.autorestoreapp" />
+
+</manifest>
diff --git a/hostsidetests/backup/AutoRestoreApp/src/android/cts/backup/autorestoreapp/AutoRestoreTest.java b/hostsidetests/backup/AutoRestoreApp/src/android/cts/backup/autorestoreapp/AutoRestoreTest.java
new file mode 100644
index 0000000..1448f93
--- /dev/null
+++ b/hostsidetests/backup/AutoRestoreApp/src/android/cts/backup/autorestoreapp/AutoRestoreTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.cts.backup.autorestoreapp;
+
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+
+import static com.google.common.truth.Truth.assertThat;
+
+
+import android.app.backup.BackupManager;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Device side routines to be invoked by the host side AutoRestoreHostSideTest. These are not
+ * designed to be called in any other way, as they rely on state set up by the host side test.
+ */
+@RunWith(AndroidJUnit4.class)
+@AppModeFull
+public class AutoRestoreTest {
+    private static final String SHARED_PREFERENCES_FILE = "auto_restore_shared_prefs";
+    private static final String PREF_KEY = "auto_restore_pref";
+    private static final int PREF_VALUE = 123;
+
+    private BackupManager mBackupManager;
+    private SharedPreferences mPreferences;
+
+    @Before
+    public void setUp() {
+        Context context = getTargetContext();
+        mBackupManager = new BackupManager(context);
+        mPreferences = context.getSharedPreferences(SHARED_PREFERENCES_FILE, Context.MODE_PRIVATE);
+    }
+
+    @Test
+    public void saveValuesToSharedPrefs() {
+        mPreferences.edit().putInt(PREF_KEY, PREF_VALUE).commit();
+    }
+
+    @Test
+    public void testCheckSharedPrefsExist() {
+        assertThat(mPreferences.getInt(PREF_KEY, 0)).isEqualTo(PREF_VALUE);
+    }
+
+    @Test
+    public void testCheckSharedPrefsDontExist() {
+        assertThat(mPreferences.getInt(PREF_KEY, 0)).isEqualTo(0);
+    }
+
+    @Test
+    public void enableAutoRestore() {
+        setAutoRestoreEnabled(true);
+    }
+
+    @Test
+    public void disableAutoRestore() {
+        setAutoRestoreEnabled(false);
+    }
+
+    private void setAutoRestoreEnabled(boolean enabled) {
+        SystemUtil.runWithShellPermissionIdentity(
+                () -> {
+                    mBackupManager.setAutoRestore(enabled);
+                });
+    }
+}
diff --git a/hostsidetests/backup/BackupTransportApp/Android.bp b/hostsidetests/backup/BackupTransportApp/Android.bp
new file mode 100644
index 0000000..240a460
--- /dev/null
+++ b/hostsidetests/backup/BackupTransportApp/Android.bp
@@ -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.
+
+android_test_helper_app {
+    name: "CtsBackupTransportApp",
+    defaults: ["cts_defaults"],
+    static_libs: [
+        "androidx.test.rules",
+        "platform-test-annotations",
+        "testng",
+        "truth-prebuilt",
+    ],
+    srcs: ["src/**/*.java"],
+    // tag this module as a cts test artifact
+    test_suites: [
+        "arcts",
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    sdk_version: "system_current",
+}
diff --git a/libs/rollback/testapp/res_v1/values/values.xml b/hostsidetests/backup/BackupTransportApp/AndroidManifest.xml
similarity index 60%
copy from libs/rollback/testapp/res_v1/values/values.xml
copy to hostsidetests/backup/BackupTransportApp/AndroidManifest.xml
index 8e71917..d3f4e66 100644
--- a/libs/rollback/testapp/res_v1/values/values.xml
+++ b/hostsidetests/backup/BackupTransportApp/AndroidManifest.xml
@@ -14,6 +14,15 @@
      limitations under the License.
 -->
 
-<resources>
-    <integer name="app_version">1</integer>
-</resources>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.cts.backup.backuptransportapp">
+
+    <application android:label="BackupTransportApp" >
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.cts.backup.backuptransportapp" />
+
+</manifest>
diff --git a/hostsidetests/backup/BackupTransportApp/src/android/cts/backup/backuptransportapp/BackupTransportTest.java b/hostsidetests/backup/BackupTransportApp/src/android/cts/backup/backuptransportapp/BackupTransportTest.java
new file mode 100644
index 0000000..7edcd65
--- /dev/null
+++ b/hostsidetests/backup/BackupTransportApp/src/android/cts/backup/backuptransportapp/BackupTransportTest.java
@@ -0,0 +1,234 @@
+/*
+ * 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.cts.backup.backuptransportapp;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.app.backup.BackupTransport;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Device side routines to be invoked by the host side BackupTransportHostSideTest. These are not
+ * designed to be called in any other way, as they rely on state set up by the host side test.
+ */
+@RunWith(AndroidJUnit4.class)
+@AppModeFull
+public class BackupTransportTest {
+    private BackupTransport mBackupTransport;
+
+    @Before
+    public void setUp() {
+        mBackupTransport = new BackupTransport();
+    }
+
+    @Test
+    public void testName_throwsException() {
+        assertThrows(UnsupportedOperationException.class, () -> mBackupTransport.name());
+    }
+
+    @Test
+    public void testConfigurationIntent_returnsNull() throws Exception {
+        assertThat(mBackupTransport.configurationIntent()).isNull();
+    }
+
+    @Test
+    public void testCurrentDestinationString_throwsException() {
+        assertThrows(
+                UnsupportedOperationException.class,
+                () -> mBackupTransport.currentDestinationString());
+    }
+
+    @Test
+    public void testDataManagementIntent_returnsNull() {
+        assertThat(mBackupTransport.dataManagementIntent()).isNull();
+    }
+
+    @Test
+    public void testDataManagementIntentLabel_throwsException() {
+        assertThrows(
+                UnsupportedOperationException.class,
+                () -> mBackupTransport.dataManagementIntentLabel());
+    }
+
+    @Test
+    public void testTransportDirName_throwsException() {
+        assertThrows(
+                UnsupportedOperationException.class, () -> mBackupTransport.transportDirName());
+    }
+
+    @Test
+    public void testInitializeDevice_returnsTransportError() {
+        assertThat(mBackupTransport.initializeDevice()).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+    }
+
+    @Test
+    public void testClearBackupData_returnsTransportError() {
+        assertThat(mBackupTransport.clearBackupData(null /* packageInfo */))
+                .isEqualTo(BackupTransport.TRANSPORT_ERROR);
+    }
+
+    @Test
+    public void testFinishBackup_returnsTransportError() {
+        assertThat(mBackupTransport.finishBackup()).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+    }
+
+    @Test
+    public void testRequestBackupTime_returnsZero() {
+        assertThat(mBackupTransport.requestBackupTime()).isEqualTo(0);
+    }
+
+    @Test
+    public void testPerformBackupWithFlags_returnsTransportError() {
+        assertThat(
+                        mBackupTransport.performBackup(
+                                null /* packageInfo */, null /* inFd */, 0 /* flags */))
+                .isEqualTo(BackupTransport.TRANSPORT_ERROR);
+    }
+
+    @Test
+    public void testPerformBackup_returnsTransportError() {
+        assertThat(mBackupTransport.performBackup(null /* packageInfo */, null /* inFd */))
+                .isEqualTo(BackupTransport.TRANSPORT_ERROR);
+    }
+
+    @Test
+    public void testGetAvailableRestoreSets_returnsNull() {
+        assertThat(mBackupTransport.getAvailableRestoreSets()).isNull();
+    }
+
+    @Test
+    public void testGetCurrentRestoreSet_returnsZero() {
+        assertThat(mBackupTransport.getCurrentRestoreSet()).isEqualTo(0);
+    }
+
+    @Test
+    public void testStartRestore_returnsTransportError() {
+        assertThat(mBackupTransport.startRestore(0 /* token */, null /* packages */))
+                .isEqualTo(BackupTransport.TRANSPORT_ERROR);
+    }
+
+    @Test
+    public void testNextRestorePackage_returnsNull() {
+        assertThat(mBackupTransport.nextRestorePackage()).isNull();
+    }
+
+    @Test
+    public void testGetRestoreData_returnsTransportError() {
+        assertThat(mBackupTransport.getRestoreData(null /* outFd */))
+                .isEqualTo(BackupTransport.TRANSPORT_ERROR);
+    }
+
+    @Test
+    public void testFinishRestore_throwsException() {
+        assertThrows(UnsupportedOperationException.class, () -> mBackupTransport.finishRestore());
+    }
+
+    @Test
+    public void testRequestFullBackupTime_returnsZero() {
+        assertThat(mBackupTransport.requestFullBackupTime()).isEqualTo(0);
+    }
+
+    @Test
+    public void testPerformFullBackupWithFlags_returnsTransportPackageRejected() {
+        assertThat(
+                        mBackupTransport.performFullBackup(
+                                null /* testPackage */, null /* socket */, 0 /* flags */))
+                .isEqualTo(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+    }
+
+    @Test
+    public void testPerformFullBackup_returnsTransportPackageRejected() {
+        assertThat(mBackupTransport.performFullBackup(null /* targetPackage */, null /* socket */))
+                .isEqualTo(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+    }
+
+    @Test
+    public void testCheckFullBackupSize_whenZero_returnsTransportOk() {
+        assertThat(mBackupTransport.checkFullBackupSize(0)).isEqualTo(BackupTransport.TRANSPORT_OK);
+    }
+
+    @Test
+    public void testCheckFullBackupSize_whenMaxLong_returnsTransportOk() {
+        assertThat(mBackupTransport.checkFullBackupSize(Long.MAX_VALUE))
+                .isEqualTo(BackupTransport.TRANSPORT_OK);
+    }
+
+    @Test
+    public void testSendBackupData_returnsTransportError() {
+        assertThat(mBackupTransport.sendBackupData(0 /* numBytes */))
+                .isEqualTo(BackupTransport.TRANSPORT_ERROR);
+    }
+
+    @Test
+    public void testCancelFullBackup_throwsException() {
+        assertThrows(
+                UnsupportedOperationException.class, () -> mBackupTransport.cancelFullBackup());
+    }
+
+    @Test
+    public void testIsAppEligibleForBackup_isFullBackup_returnsTrue() {
+        assertThat(
+                        mBackupTransport.isAppEligibleForBackup(
+                                null /* targetPackage */, true /* isFullBackup */))
+                .isTrue();
+    }
+
+    @Test
+    public void testIsAppEligibleForBackup_isNotFullBackup_returnsTrue() {
+        assertThat(
+                        mBackupTransport.isAppEligibleForBackup(
+                                null /* targetPackage */, false /* isFullBackup */))
+                .isTrue();
+    }
+
+    @Test
+    public void testGetBackupQuota_isFullBackup_returnsMaxValue() {
+        assertThat(mBackupTransport.getBackupQuota(null /* packageName */, true /* isFullBackup */))
+                .isEqualTo(Long.MAX_VALUE);
+    }
+
+    @Test
+    public void testGetBackupQuota_isNotFullBackup_returnsMaxValue() {
+        assertThat(
+                        mBackupTransport.getBackupQuota(
+                                null /* packageName */, false /* isFullBackup */))
+                .isEqualTo(Long.MAX_VALUE);
+    }
+
+    @Test
+    public void testGetNextFullRestoreDataChunk_returnsZero() {
+        assertThat(mBackupTransport.getNextFullRestoreDataChunk(null /* socket */)).isEqualTo(0);
+    }
+
+    @Test
+    public void testAbortFullRestore_returnsTransportOk() {
+        assertThat(mBackupTransport.abortFullRestore()).isEqualTo(BackupTransport.TRANSPORT_OK);
+    }
+
+    @Test
+    public void testGetTransportFlags_returnsZero() {
+        assertThat(mBackupTransport.getTransportFlags()).isEqualTo(0);
+    }
+}
diff --git a/hostsidetests/backup/KeyValueApp/src/android/cts/backup/keyvaluerestoreapp/KeyValueBackupAgent.java b/hostsidetests/backup/KeyValueApp/src/android/cts/backup/keyvaluerestoreapp/KeyValueBackupAgent.java
index f79baa9..85a96b1 100644
--- a/hostsidetests/backup/KeyValueApp/src/android/cts/backup/keyvaluerestoreapp/KeyValueBackupAgent.java
+++ b/hostsidetests/backup/KeyValueApp/src/android/cts/backup/keyvaluerestoreapp/KeyValueBackupAgent.java
@@ -17,6 +17,10 @@
 package android.cts.backup.keyvaluerestoreapp;
 
 import android.app.backup.BackupAgentHelper;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.os.ParcelFileDescriptor;
+import java.io.IOException;
 
 public class KeyValueBackupAgent extends BackupAgentHelper {
 
@@ -31,4 +35,20 @@
         addHelper(KEY_BACKUP_TEST_FILES_PREFIX,
                 KeyValueBackupRestoreTest.getFileBackupHelper(this));
     }
+
+    @Override
+    public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+        ParcelFileDescriptor newState) throws IOException {
+        // Explicitly override and call super() to help go/android-api-coverage-dashboard pick up
+        // the test coverage (b/113067697).
+        super.onBackup(oldState, data, newState);
+    }
+
+    @Override
+    public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
+        throws IOException {
+        // Explicitly override and call super() to help go/android-api-coverage-dashboard pick up
+        // the test coverage (b/113067697).
+        super.onRestore(data, appVersionCode, newState);
+    }
 }
diff --git a/hostsidetests/backup/RestoreSessionTest/src/android/cts/backup/restoresessionapp/RestoreSessionTest.java b/hostsidetests/backup/RestoreSessionTest/src/android/cts/backup/restoresessionapp/RestoreSessionTest.java
index 06b9ae0..adb7822 100644
--- a/hostsidetests/backup/RestoreSessionTest/src/android/cts/backup/restoresessionapp/RestoreSessionTest.java
+++ b/hostsidetests/backup/RestoreSessionTest/src/android/cts/backup/restoresessionapp/RestoreSessionTest.java
@@ -24,7 +24,6 @@
 
 import static org.junit.Assert.assertNotEquals;
 
-import android.app.Instrumentation;
 import android.app.UiAutomation;
 import android.app.backup.BackupManager;
 import android.app.backup.BackupManagerMonitor;
@@ -34,10 +33,10 @@
 import android.content.Context;
 import android.os.Bundle;
 
+import androidx.annotation.Nullable;
 import android.platform.test.annotations.AppModeFull;
 import androidx.test.runner.AndroidJUnit4;
 
-// import com.android.compatibility.common.util.SystemUtil;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -55,15 +54,19 @@
 @RunWith(AndroidJUnit4.class)
 @AppModeFull
 public class RestoreSessionTest {
-    private static final String PACKAGE_1 = "android.cts.backup.restoresessionapp1";
-    private static final String PACKAGE_2 = "android.cts.backup.restoresessionapp2";
-    private static final String PACKAGE_3 = "android.cts.backup.restoresessionapp3";
+    private static final String[] PACKAGES = new String[] {
+        "android.cts.backup.restoresessionapp1",
+        "android.cts.backup.restoresessionapp2",
+        "android.cts.backup.restoresessionapp3"
+    };
 
+    private static final int PACKAGES_COUNT = 3;
     private static final int RESTORE_TIMEOUT_SECONDS = 10;
 
     private BackupManager mBackupManager;
     private Set<String> mRestorePackages;
     private Set<String> mNonRestorePackages;
+    private CountDownLatch mRestoreSetsLatch;
     private CountDownLatch mRestoreObserverLatch;
     private RestoreSession mRestoreSession;
     private UiAutomation mUiAutomation;
@@ -89,7 +92,7 @@
 
                     mRestoreToken = token;
 
-                    mRestoreObserverLatch.countDown();
+                    mRestoreSetsLatch.countDown();
                 }
 
                 @Override
@@ -129,19 +132,10 @@
         Context context = getTargetContext();
         mBackupManager = new BackupManager(context);
 
-        mRestorePackages = new HashSet<>();
-        mRestorePackages.add(PACKAGE_1);
-        mRestorePackages.add(PACKAGE_2);
-
-        mNonRestorePackages = new HashSet<>();
-        mNonRestorePackages.add(PACKAGE_3);
-
         mRestoreToken = 0L;
 
         mUiAutomation = getInstrumentation().getUiAutomation();
         mUiAutomation.adoptShellPermissionIdentity();
-
-        loadAvailableRestoreSets();
     }
 
     @After
@@ -151,11 +145,47 @@
 
     /**
      * Restore packages added to mRestorePackages and verify only those packages are restored. Use
+     * {@link RestoreSession#restorePackage(String, RestoreObserver)}
+     */
+
+    @Test
+    public void testRestorePackage() throws InterruptedException {
+        initPackagesToRestore(/* packagesCount */ 1);
+        testRestorePackagesInternal((BackupManagerMonitor monitor) -> {
+            mRestoreSession.restorePackage(
+                mRestorePackages.iterator().next(),
+                mRestoreObserver);
+        }, false);
+    }
+
+    /**
+     * Restore packages added to mRestorePackages and verify only those packages are restored. Use
+     * {@link RestoreSession#restorePackage(String, RestoreObserver, BackupManagerMonitor)}
+     */
+    @Test
+    public void testRestorePackageWithMonitorParam() throws InterruptedException {
+        initPackagesToRestore(/* packagesCount */ 1);
+        testRestorePackagesInternal((BackupManagerMonitor monitor) -> {
+            mRestoreSession.restorePackage(
+                mRestorePackages.iterator().next(),
+                mRestoreObserver,
+                monitor);
+        }, true);
+    }
+
+    /**
+     * Restore packages added to mRestorePackages and verify only those packages are restored. Use
      * {@link RestoreSession#restorePackages(long, RestoreObserver, Set)}
      */
     @Test
     public void testRestorePackages() throws InterruptedException {
-        testRestorePackagesInternal(false);
+        initPackagesToRestore(/* packagesCount */ 2);
+        testRestorePackagesInternal((BackupManagerMonitor monitor) -> {
+            mRestoreSession.restorePackages(
+                mRestoreToken,
+                mRestoreObserver,
+                mRestorePackages);
+        }, false);
     }
 
     /**
@@ -164,24 +194,30 @@
      */
     @Test
     public void testRestorePackagesWithMonitorParam() throws InterruptedException {
-        testRestorePackagesInternal(true);
+        initPackagesToRestore(/* packagesCount */ 2);
+        testRestorePackagesInternal((BackupManagerMonitor monitor) -> {
+            mRestoreSession.restorePackages(
+                mRestoreToken,
+                mRestoreObserver,
+                mRestorePackages,
+                monitor);
+        }, true);
     }
 
-    private void testRestorePackagesInternal(boolean useMonitorParam) throws InterruptedException {
-        // Wait for the callbacks from RestoreObserver: one for each package from
-        // mRestorePackages plus restoreStarting and restoreFinished.
-        mRestoreObserverLatch = new CountDownLatch(mRestorePackages.size() + 2);
+    private void testRestorePackagesInternal(RestoreRunner restoreRunner, boolean useMonitorParam)
+            throws InterruptedException {
         CountDownLatch backupMonitorLatch = null;
         if (useMonitorParam) {
             // Wait for the callbacks from BackupManagerMonitor: one for each package.
             backupMonitorLatch = new CountDownLatch(mRestorePackages.size());
-            mRestoreSession.restorePackages(
-                    mRestoreToken,
-                    mRestoreObserver,
-                    mRestorePackages,
-                    new TestBackupMonitor(backupMonitorLatch));
+            BackupManagerMonitor backupMonitor = new TestBackupMonitor(backupMonitorLatch);
+
+            loadAvailableRestoreSets(backupMonitor);
+
+            restoreRunner.runRestore(backupMonitor);
         } else {
-            mRestoreSession.restorePackages(mRestoreToken, mRestoreObserver, mRestorePackages);
+            loadAvailableRestoreSets(null);
+            restoreRunner.runRestore(null);
         }
 
         awaitResultAndAssertSuccess(mRestoreObserverLatch);
@@ -190,13 +226,16 @@
         }
     }
 
-    private void loadAvailableRestoreSets() throws InterruptedException {
+    private void loadAvailableRestoreSets(@Nullable BackupManagerMonitor monitor)
+            throws InterruptedException {
         // Wait for getAvailableRestoreSets to finish and the callback to be fired.
-        mRestoreObserverLatch = new CountDownLatch(1);
+        mRestoreSetsLatch = new CountDownLatch(1);
         mRestoreSession = mBackupManager.beginRestoreSession();
         assertEquals(
-                BackupManager.SUCCESS, mRestoreSession.getAvailableRestoreSets(mRestoreObserver));
-        awaitResultAndAssertSuccess(mRestoreObserverLatch);
+                BackupManager.SUCCESS, monitor == null
+                ? mRestoreSession.getAvailableRestoreSets(mRestoreObserver)
+                : mRestoreSession.getAvailableRestoreSets(mRestoreObserver, monitor));
+        awaitResultAndAssertSuccess(mRestoreSetsLatch);
 
         assertNotEquals("Restore set not found", 0L, mRestoreToken);
     }
@@ -215,6 +254,23 @@
         assertTrue("Restore timed out", waitResult);
     }
 
+    private void initPackagesToRestore(int packagesCount) {
+        mRestorePackages = new HashSet<>();
+        mNonRestorePackages = new HashSet<>();
+
+        for (int i = 0; i < PACKAGES_COUNT; i++) {
+            if (i < packagesCount) {
+                mRestorePackages.add(PACKAGES[i]);
+            } else {
+                mNonRestorePackages.add(PACKAGES[i]);
+            }
+        }
+
+        // Wait for the callbacks from RestoreObserver: one for each package from
+        // mRestorePackages plus restoreStarting and restoreFinished.
+        mRestoreObserverLatch = new CountDownLatch(mRestorePackages.size() + 2);
+    }
+
     private static class TestBackupMonitor extends BackupManagerMonitor {
         private final CountDownLatch mLatch;
 
@@ -234,4 +290,9 @@
             mLatch.countDown();
         }
     }
+
+    @FunctionalInterface
+    private interface RestoreRunner {
+        void runRestore(BackupManagerMonitor monitor) throws InterruptedException;
+    }
 }
diff --git a/hostsidetests/backup/src/android/cts/backup/AutoRestoreHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/AutoRestoreHostSideTest.java
new file mode 100644
index 0000000..99d147c
--- /dev/null
+++ b/hostsidetests/backup/src/android/cts/backup/AutoRestoreHostSideTest.java
@@ -0,0 +1,139 @@
+/*
+ * 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.cts.backup;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.AppModeFull;
+
+import com.android.compatibility.common.util.BackupUtils;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** Test verifying that {@link BackupManager#setAutoRestore(boolean)} setting is respected. */
+@RunWith(DeviceJUnit4ClassRunner.class)
+@AppModeFull
+public class AutoRestoreHostSideTest extends BaseBackupHostSideTest {
+    private static final String PACKAGE = "android.cts.backup.autorestoreapp";
+    private static final String CLASS = PACKAGE + ".AutoRestoreTest";
+    private static final String APK = "CtsAutoRestoreApp.apk";
+
+    private BackupUtils mBackupUtils;
+    private Optional<Boolean> mWasAutoRestoreEnabled = Optional.empty();
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mBackupUtils = getBackupUtils();
+        mWasAutoRestoreEnabled = Optional.of(isAutoRestoreEnabled());
+
+        installPackage(APK);
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+
+        if (mWasAutoRestoreEnabled.isPresent()) {
+            mBackupUtils.executeShellCommandSync(
+                    "bmgr autorestore " + (mWasAutoRestoreEnabled.get() ? "true" : "false"));
+            uninstallPackage(PACKAGE);
+
+            mWasAutoRestoreEnabled = Optional.empty();
+        }
+    }
+
+    /**
+     *
+     *
+     * <ol>
+     *   <li>Enable auto restore
+     *   <li>Write dummy values to shared preferences
+     *   <li>Backup the package
+     *   <li>Uninstall the package
+     *   <li>Install the package again and verify shared prefs are restored
+     * </ol>
+     */
+    @Test
+    public void testSetAutoRestore_autoRestoresDataWhenEnabled() throws Exception {
+        runDeviceProcedure("enableAutoRestore");
+
+        populateSharedPrefs();
+
+        backupAndReinstallPackage();
+
+        runDeviceProcedure("testCheckSharedPrefsExist");
+    }
+
+    /**
+     *
+     *
+     * <ol>
+     *   <li>Disable auto restore
+     *   <li>Write dummy values to shared preferences
+     *   <li>Backup the package
+     *   <li>Uninstall the package
+     *   <li>Install the package again and verify shared prefs aren't restored
+     * </ol>
+     */
+    @Test
+    public void testSetAutoRestore_dontAutoRestoresDataWhenDisabled() throws Exception {
+        runDeviceProcedure("disableAutoRestore");
+
+        populateSharedPrefs();
+
+        backupAndReinstallPackage();
+
+        runDeviceProcedure("testCheckSharedPrefsDontExist");
+    }
+
+    private void populateSharedPrefs() throws Exception {
+        runDeviceProcedure("saveValuesToSharedPrefs");
+        runDeviceProcedure("testCheckSharedPrefsExist");
+    }
+
+    private void backupAndReinstallPackage() throws Exception {
+        mBackupUtils.backupNowAndAssertSuccess(PACKAGE);
+        uninstallPackage(PACKAGE);
+        installPackage(APK);
+    }
+
+    private boolean isAutoRestoreEnabled() throws Exception {
+        String output = mBackupUtils.executeShellCommandAndReturnOutput("dumpsys backup");
+        Pattern pattern = Pattern.compile("Auto-restore is (enabled|disabled)");
+        Matcher matcher = pattern.matcher(output.trim());
+
+        assertThat(matcher.find()).isTrue();
+
+        return "enabled".equals(matcher.group(1));
+    }
+
+    private void runDeviceProcedure(String testName) throws Exception {
+        runDeviceTests(PACKAGE, CLASS, testName);
+    }
+}
diff --git a/hostsidetests/backup/src/android/cts/backup/BackupPreparer.java b/hostsidetests/backup/src/android/cts/backup/BackupPreparer.java
index 11a1dbc..d9a3975 100644
--- a/hostsidetests/backup/src/android/cts/backup/BackupPreparer.java
+++ b/hostsidetests/backup/src/android/cts/backup/BackupPreparer.java
@@ -16,6 +16,9 @@
 
 package android.cts.backup;
 
+import static org.junit.Assert.fail;
+
+import com.android.compatibility.common.util.BackupHostSideUtils;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.config.Option;
 import com.android.tradefed.config.OptionClass;
@@ -27,7 +30,11 @@
 import com.android.tradefed.targetprep.ITargetCleaner;
 import com.android.tradefed.targetprep.TargetSetupError;
 
+import java.io.IOException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletionException;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -38,6 +45,7 @@
  */
 @OptionClass(alias = "backup-preparer")
 public class BackupPreparer implements ITargetCleaner {
+    private static final long TRANSPORT_AVAILABLE_TIMEOUT_SECONDS = TimeUnit.MINUTES.toSeconds(5);
     @Option(name="enable-backup-if-needed", description=
             "Enable backup before all the tests and return to the original state after.")
     private boolean mEnableBackup = true;
@@ -53,9 +61,6 @@
     private static final String LOCAL_TRANSPORT =
             "com.android.localtransport/.LocalTransport";
 
-    private static final int BACKUP_PROVISIONING_TIMEOUT_SECONDS = 30;
-    private static final int BACKUP_PROVISIONING_POLL_INTERVAL_SECONDS = 1;
-
     private boolean mIsBackupSupported;
     private boolean mWasBackupEnabled;
     private String mOldTransport;
@@ -74,10 +79,8 @@
 
         if (mIsBackupSupported) {
             // Enable backup and select local backup transport
-            if (!hasBackupTransport(LOCAL_TRANSPORT)) {
-                throw new TargetSetupError("Device should have LocalTransport available",
-                        device.getDeviceDescriptor());
-            }
+            waitForTransport(LOCAL_TRANSPORT);
+
             if (mEnableBackup) {
                 CLog.i("Enabling backup on %s", mDevice.getSerialNumber());
                 mWasBackupEnabled = enableBackup(true);
@@ -87,7 +90,11 @@
                     mOldTransport = setBackupTransport(LOCAL_TRANSPORT);
                     CLog.d("Old transport : %s", mOldTransport);
                 }
-                waitForBackupInitialization();
+                try {
+                    BackupHostSideUtils.createBackupUtils(mDevice).waitForBackupInitialization();
+                } catch (IOException e) {
+                    throw new TargetSetupError("Backup not initialized", e);
+                }
             }
         }
     }
@@ -110,17 +117,56 @@
         }
     }
 
-    // Copied over from BackupQuotaTest
-    private boolean hasBackupTransport(String transport) throws DeviceNotAvailableException {
+    private void waitForTransport(String transport) throws TargetSetupError {
+        try {
+            waitUntilWithLastTry(
+                    "Local transport didn't become available",
+                    TRANSPORT_AVAILABLE_TIMEOUT_SECONDS,
+                    lastTry -> uncheck(() -> hasBackupTransport(transport, lastTry)));
+        } catch (InterruptedException e) {
+            throw new TargetSetupError(
+                    "Device should have LocalTransport available", mDevice.getDeviceDescriptor());
+        }
+    }
+
+    private boolean hasBackupTransport(
+            String transport, boolean logIfFail) throws DeviceNotAvailableException {
         String output = mDevice.executeShellCommand("bmgr list transports");
         for (String t : output.split(" ")) {
             if (transport.equals(t.trim())) {
                 return true;
             }
         }
+        if (logIfFail) {
+            CLog.d("bmgr list transports: " + output);
+        }
         return false;
     }
 
+    /**
+     * Calls {@code predicate} with {@code false} until time-out {@code timeoutSeconds} is reached,
+     * if {@code predicate} returns true, method returns. If time-out is reached before that, we
+     * call {@code predicate} with {@code true} one last time, if that last call returns false we
+     * fail with {@code message}.
+     *
+     * TODO: Move to CommonTestUtils
+     */
+    private static void waitUntilWithLastTry(
+            String message, long timeoutSeconds, Function<Boolean, Boolean> predicate)
+            throws InterruptedException {
+        int sleep = 125;
+        final long timeout = System.currentTimeMillis() + timeoutSeconds * 1000;
+        while (System.currentTimeMillis() < timeout) {
+            if (predicate.apply(false)) {
+                return;
+            }
+            Thread.sleep(sleep);
+        }
+        if (!predicate.apply(true)) {
+            fail(message);
+        }
+    }
+
     // Copied over from BackupQuotaTest
     private boolean enableBackup(boolean enable) throws DeviceNotAvailableException {
         boolean previouslyEnabled;
@@ -149,27 +195,6 @@
         }
     }
 
-    private void waitForBackupInitialization()
-        throws TargetSetupError, DeviceNotAvailableException {
-        long tryUntilNanos = System.nanoTime()
-            + TimeUnit.SECONDS.toNanos(BACKUP_PROVISIONING_TIMEOUT_SECONDS);
-        while (System.nanoTime() < tryUntilNanos) {
-            String output = mDevice.executeShellCommand("dumpsys backup");
-            if (output.matches("(?s)"  // DOTALL
-                + "^Backup Manager is .* not pending init.*")) {
-                return;
-            }
-            try {
-                Thread.sleep(TimeUnit.SECONDS.toMillis(BACKUP_PROVISIONING_POLL_INTERVAL_SECONDS));
-            } catch (InterruptedException e) {
-                Thread.currentThread().interrupt();
-                break;
-            }
-        }
-        throw new TargetSetupError("Timed out waiting for backup initialization",
-            mDevice.getDeviceDescriptor());
-    }
-
     // Copied over from BaseDevicePolicyTest
     private void waitForBroadcastIdle() throws DeviceNotAvailableException, TargetSetupError {
         CollectingOutputReceiver receiver = new CollectingOutputReceiver();
@@ -190,4 +215,13 @@
         }
     }
 
+    private static <T> T uncheck(Callable<T> callable) {
+        try {
+            return callable.call();
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new CompletionException(e);
+        }
+    }
 }
diff --git a/hostsidetests/backup/src/android/cts/backup/BackupTransportHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/BackupTransportHostSideTest.java
new file mode 100644
index 0000000..a6a4f3a
--- /dev/null
+++ b/hostsidetests/backup/src/android/cts/backup/BackupTransportHostSideTest.java
@@ -0,0 +1,37 @@
+/*
+ * 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.cts.backup;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+/** Test verifying the default implementation of {@link BackupTransport} */
+// TODO(b/135920714): Move this test to device-side once it's possible to use both system and test
+// APIs in the same target.
+public class BackupTransportHostSideTest extends BaseBackupHostSideTest {
+    private static final String PACKAGE = "android.cts.backup.backuptransportapp";
+    private static final String CLASS = PACKAGE + ".BackupTransportTest";
+    private static final String APK = "CtsBackupTransportApp.apk";
+
+    @Test
+    public void testBackupTransport() throws Exception {
+        installPackage(APK);
+        assertTrue(runDeviceTests(PACKAGE, CLASS));
+        uninstallPackage(PACKAGE);
+    }
+}
diff --git a/hostsidetests/backup/src/android/cts/backup/BaseMultiUserBackupHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/BaseMultiUserBackupHostSideTest.java
index 81737e9..c1171ac 100644
--- a/hostsidetests/backup/src/android/cts/backup/BaseMultiUserBackupHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/BaseMultiUserBackupHostSideTest.java
@@ -23,7 +23,7 @@
 import android.platform.test.annotations.AppModeFull;
 
 import com.android.compatibility.common.util.BackupUtils;
-import com.android.compatibility.common.util.HostSideTestUtils;
+import com.android.compatibility.common.util.CommonTestUtils;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.log.LogUtil.CLog;
@@ -58,7 +58,7 @@
             FULL_BACKUP_TEST_PACKAGE + ".ProfileFullBackupRestoreTest";
 
     protected final BackupUtils mBackupUtils = getBackupUtils();
-    protected ITestDevice mDevice;
+    private ITestDevice mDevice;
 
     // Store initial device state as Optional as tearDown() will execute even if we have assumption
     // failures in setUp().
@@ -138,13 +138,14 @@
      * Selects the local transport as the current transport for user {@code userId}. Returns the
      * {@link String} name of the local transport.
      */
-    String switchUserToLocalTransportAndAssertSuccess(int userId) throws IOException {
+    String switchUserToLocalTransportAndAssertSuccess(int userId)
+            throws IOException, InterruptedException {
         // Make sure the user has the local transport.
         String localTransport = mBackupUtils.getLocalTransportName();
 
         // TODO (b/121198010): Update dumpsys or add shell command to query status of transport
         // initialization. Transports won't be available until they are initialized/registered.
-        HostSideTestUtils.waitUntil("wait for user to have local transport",
+        CommonTestUtils.waitUntil("wait for user to have local transport",
                 TRANSPORT_INITIALIZATION_TIMEOUT_SECS,
                 () -> mBackupUtils.userHasBackupTransport(localTransport, userId));
 
diff --git a/hostsidetests/backup/src/android/cts/backup/MultiUserBackupStateTest.java b/hostsidetests/backup/src/android/cts/backup/MultiUserBackupStateTest.java
index 4fa4469..25c59d6 100644
--- a/hostsidetests/backup/src/android/cts/backup/MultiUserBackupStateTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/MultiUserBackupStateTest.java
@@ -20,7 +20,7 @@
 
 import android.platform.test.annotations.AppModeFull;
 
-import com.android.compatibility.common.util.HostSideTestUtils;
+import com.android.compatibility.common.util.CommonTestUtils;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
 import org.junit.After;
@@ -44,7 +44,7 @@
     public void setUp() throws Exception {
         super.setUp();
 
-        int profileUserId = createProfileUser(mDevice.getCurrentUser(), "MU-State");
+        int profileUserId = createProfileUser(getDevice().getCurrentUser(), "MU-State");
         mProfileUserId = Optional.of(profileUserId);
         startUser(profileUserId);
     }
@@ -54,7 +54,7 @@
     @Override
     public void tearDown() throws Exception {
         if (mProfileUserId.isPresent()) {
-            assertTrue(mDevice.removeUser(mProfileUserId.get()));
+            assertTrue(getDevice().removeUser(mProfileUserId.get()));
             mProfileUserId = Optional.empty();
         }
         super.tearDown();
@@ -77,10 +77,10 @@
 
         assertTrue(mBackupUtils.isBackupActivatedForUser(profileUserId));
 
-        assertTrue(mDevice.removeUser(profileUserId));
+        assertTrue(getDevice().removeUser(profileUserId));
         mProfileUserId = Optional.empty();
 
-        HostSideTestUtils.waitUntil("wait for backup to be deactivated for removed user",
+        CommonTestUtils.waitUntil("wait for backup to be deactivated for removed user",
                 TIMEOUT_SECONDS, () -> !mBackupUtils.isBackupActivatedForUser(profileUserId));
     }
 }
diff --git a/hostsidetests/backup/src/android/cts/backup/ProfileScheduledJobHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/ProfileScheduledJobHostSideTest.java
index 7eff018..874282b 100644
--- a/hostsidetests/backup/src/android/cts/backup/ProfileScheduledJobHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/ProfileScheduledJobHostSideTest.java
@@ -21,6 +21,9 @@
 import android.platform.test.annotations.AppModeFull;
 
 import com.android.compatibility.common.util.BackupUtils;
+import com.android.compatibility.common.util.ProtoUtils;
+import com.android.server.job.JobSchedulerServiceDumpProto;
+import com.android.server.job.JobStatusShortInfoProto;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -29,10 +32,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.IOException;
 import java.util.Optional;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 /** Test that key-value and full backup jobs are scheduled in a profile. */
 @RunWith(DeviceJUnit4ClassRunner.class)
@@ -60,7 +60,6 @@
     private static final int TIMEOUT_FOR_KEY_VALUE_SECONDS = 5 * 60; // 5 minutes.
     private static final int TIMEOUT_FOR_FULL_BACKUP_SECONDS = 5 * 60; // 5 minutes.
 
-    private static final String DUMPSYS_JOB_SCHEDULER = "dumpsys jobscheduler";
     private static final String JOB_SCHEDULER_RUN_COMMAND = "cmd jobscheduler run -f android";
 
     private final BackupUtils mBackupUtils = getBackupUtils();
@@ -196,10 +195,19 @@
     }
 
     /** Returns {@code true} if there is a system job scheduled with the specified parameters. */
-    private boolean isSystemJobScheduled(int jobId, String jobName) throws IOException {
-        String output = mBackupUtils.executeShellCommandAndReturnOutput(DUMPSYS_JOB_SCHEDULER);
-        String jobRegex = String.format("JOB #1000/%d.*%s", jobId, jobName);
-        Matcher matcher = Pattern.compile(jobRegex).matcher(output.trim());
-        return matcher.find();
+    private boolean isSystemJobScheduled(int jobId, String jobName) throws Exception {
+        // TODO: Look into making a higher level adb command or a system API for this instead.
+        //  (e.g. "adb shell cmd jobscheduler is-job-scheduled system JOB-ID JOB-NAME")
+        final JobSchedulerServiceDumpProto dump =
+                ProtoUtils.getProto(mDevice, JobSchedulerServiceDumpProto.parser(),
+                        ProtoUtils.DUMPSYS_JOB_SCHEDULER);
+        for (JobSchedulerServiceDumpProto.RegisteredJob job : dump.getRegisteredJobsList()) {
+            final JobStatusShortInfoProto info = job.getInfo();
+            if (info.getCallingUid() == 1000 && info.getJobId() == jobId
+                    && jobName.equals(info.getBatteryName())) {
+                return true;
+            }
+        }
+        return false;
     }
 }
diff --git a/hostsidetests/backup/src/android/cts/backup/RestoreSessionHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/RestoreSessionHostSideTest.java
index 9c5d890..89e2539 100644
--- a/hostsidetests/backup/src/android/cts/backup/RestoreSessionHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/RestoreSessionHostSideTest.java
@@ -72,10 +72,25 @@
         }
     }
 
+    /** Test {@link RestoreSession#restorePackage(RestoreObserver, String)} */
+    @Test
+    public void testRestorePackage() throws Exception {
+        testRestorePackagesInternal("testRestorePackage", /* packagesToRestore */ 1);
+    }
+
+    /**
+     * Test {@link RestoreSession#restorePackage(RestoreObserver, String, BackupManagerMonitor)}
+     */
+    @Test
+    public void testRestorePackageWithMonitorParam() throws Exception {
+        testRestorePackagesInternal("testRestorePackageWithMonitorParam",
+                /* packagesToRestore */ 1);
+    }
+
     /** Test {@link RestoreSession#restorePackages(long, RestoreObserver, Set)} */
     @Test
     public void testRestorePackages() throws Exception {
-        testRestorePackagesInternal("testRestorePackages");
+        testRestorePackagesInternal("testRestorePackages", /* packagesToRestore */ 2);
     }
 
     /**
@@ -83,7 +98,8 @@
      */
     @Test
     public void testRestorePackagesWithMonitorParam() throws Exception {
-        testRestorePackagesInternal("testRestorePackagesWithMonitorParam");
+        testRestorePackagesInternal("testRestorePackagesWithMonitorParam",
+                /* packagesToRestore */ 2);
     }
 
     /**
@@ -94,15 +110,16 @@
      *   <li>Write dummy values to shared preferences for each package
      *   <li>Backup each package (adb shell bmgr backupnow)
      *   <li>Clear shared preferences for each package
-     *   <li>Run restore for 2 of the packages and verify that only they were restored
-     *   <li>Verify that shared preferences for the 2 packages are restored correctly
+     *   <li>Run restore for the first {@code numPackagesToRestore}, verify only those are restored
+     *   <li>Verify that shared preferences for the restored packages are restored correctly
      * </ol>
      */
-    private void testRestorePackagesInternal(String deviceTestName) throws Exception {
+    private void testRestorePackagesInternal(String deviceTestName, int numPackagesToRestore)
+            throws Exception {
         installPackage(getApkNameForTestApp(1));
         installPackage(getApkNameForTestApp(2));
         installPackage(getApkNameForTestApp(3));
-        //
+
         // Write dummy value to shared preferences for all test packages.
         checkRestoreSessionDeviceTestForAllApps("testSaveValuesToSharedPrefs");
         checkRestoreSessionDeviceTestForAllApps("testCheckSharedPrefsExist");
@@ -119,11 +136,15 @@
         runRestoreSessionDeviceTestAndAssertSuccess(
                 MAIN_TEST_APP_PKG, DEVICE_MAIN_TEST_CLASS_NAME, deviceTestName);
 
-        // Check that shared prefs are only restored (and restored correctly) for the first 2
-        // packages.
-        checkRestoreSessionDeviceTest(1, "testCheckSharedPrefsExist");
-        checkRestoreSessionDeviceTest(2, "testCheckSharedPrefsExist");
-        checkRestoreSessionDeviceTest(3, "testCheckSharedPrefsDontExist");
+        // Check that shared prefs are only restored (and restored correctly) for the packages
+        // that need to be restored.
+        for (int i = 1; i <= TEST_APPS_COUNT; i++) {
+            if (i <= numPackagesToRestore) {
+                checkRestoreSessionDeviceTest(i, "testCheckSharedPrefsExist");
+            } else {
+                checkRestoreSessionDeviceTest(i, "testCheckSharedPrefsDontExist");
+            }
+        }
 
         uninstallPackage(getPackageNameForTestApp(1));
         uninstallPackage(getPackageNameForTestApp(2));
diff --git a/hostsidetests/bootstats/OWNERS b/hostsidetests/bootstats/OWNERS
new file mode 100644
index 0000000..1f99e96
--- /dev/null
+++ b/hostsidetests/bootstats/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 183496
+keunyoung@google.com
diff --git a/hostsidetests/classloaders/OWNERS b/hostsidetests/classloaders/OWNERS
new file mode 100644
index 0000000..137abb9
--- /dev/null
+++ b/hostsidetests/classloaders/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 86431
+calin@google.com
+ngeoffray@google.com
+sehr@google.com
diff --git a/hostsidetests/compilation/OWNERS b/hostsidetests/compilation/OWNERS
new file mode 100644
index 0000000..f386ff2
--- /dev/null
+++ b/hostsidetests/compilation/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 86431
+include ../classloaders/OWNERS
diff --git a/hostsidetests/compilation/app/Android.bp b/hostsidetests/compilation/app/Android.bp
index f4954c7..770baea 100644
--- a/hostsidetests/compilation/app/Android.bp
+++ b/hostsidetests/compilation/app/Android.bp
@@ -16,7 +16,7 @@
     name: "CtsCompilationApp",
     defaults: ["cts_support_defaults"],
     srcs: ["src/**/*.java"],
-    sdk_version: "current",
+    sdk_version: "29",
     // tag this module as a cts test artifact
     test_suites: [
         "cts",
diff --git a/hostsidetests/devicepolicy/OWNERS b/hostsidetests/devicepolicy/OWNERS
new file mode 100644
index 0000000..12341eb
--- /dev/null
+++ b/hostsidetests/devicepolicy/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 100560
+include /tests/admin/OWNERS
diff --git a/hostsidetests/devicepolicy/TEST_MAPPING b/hostsidetests/devicepolicy/TEST_MAPPING
new file mode 100644
index 0000000..a5ee3e2
--- /dev/null
+++ b/hostsidetests/devicepolicy/TEST_MAPPING
@@ -0,0 +1,20 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsDevicePolicyManagerTestCases",
+      "options": [
+        {
+          "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+        },
+        {
+          "exclude-annotation": "android.platform.test.annotations.LargeTest"
+        }
+      ]
+    }
+  ],
+  "postsubmit": [
+    {
+      "name": "CtsDevicePolicyManagerTestCases"
+    }
+  ]
+}
diff --git a/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/PreSelectedKeyAccessTest.java b/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/PreSelectedKeyAccessTest.java
new file mode 100644
index 0000000..605a0b4
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/PreSelectedKeyAccessTest.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 com.android.cts.certinstaller;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.security.KeyChain;
+import android.security.KeyChainException;
+import android.content.Context;
+import android.test.InstrumentationTestCase;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.X509Certificate;
+
+public class PreSelectedKeyAccessTest extends InstrumentationTestCase {
+    private static final String PRE_SELECTED_ALIAS = "pre-selected-rsa";
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    // Test that this app can access pre-granted alias
+    public void testAccessingPreSelectedAliasExpectingSuccess() throws
+            KeyChainException, NoSuchAlgorithmException, InvalidKeyException, SignatureException,
+            InterruptedException {
+        PrivateKey privateKey = KeyChain.getPrivateKey(getContext(), PRE_SELECTED_ALIAS);
+        assertThat(privateKey).isNotNull();
+        String algoIdentifier = "SHA256withRSA";
+
+        byte[] data = new String("hello").getBytes();
+        Signature sign = Signature.getInstance(algoIdentifier);
+        sign.initSign(privateKey);
+        sign.update(data);
+        byte[] signature = sign.sign();
+
+        X509Certificate[] certs = KeyChain.getCertificateChain(getContext(), PRE_SELECTED_ALIAS);
+        assertThat(certs).isNotEmpty();
+
+        PublicKey publicKey = certs[0].getPublicKey();
+        Signature verify = Signature.getInstance(algoIdentifier);
+        verify.initVerify(publicKey);
+        verify.update(data);
+        assertThat(verify.verify(signature)).isTrue();
+    }
+
+    public void testAccessingPreSelectedAliasWithoutGrant() throws
+            KeyChainException, InterruptedException {
+        assertThat(KeyChain.getPrivateKey(getContext(), PRE_SELECTED_ALIAS)).isNull();
+    }
+
+    private Context getContext() {
+        return getInstrumentation().getContext();
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceAdminPasswordTest.java b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceAdminPasswordTest.java
index 1e9759c..472f3de 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceAdminPasswordTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceAdminPasswordTest.java
@@ -67,28 +67,22 @@
         }
     }
 
-    private boolean waitUntilPasswordState(boolean expected) throws InterruptedException {
+    private boolean getPasswordState() throws InterruptedException {
         final int currentQuality = dpm.getPasswordQuality(mAdminComponent);
         dpm.setPasswordQuality(mAdminComponent, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
-
-        int numTries = 5;
-        boolean result = dpm.isActivePasswordSufficient();
-        while ((numTries > 0) && (result != expected)) {
-            numTries = numTries - 1;
-            Thread.sleep(100);
-            result = dpm.isActivePasswordSufficient();
+        try {
+            return dpm.isActivePasswordSufficient();
+        } finally {
+            dpm.setPasswordQuality(mAdminComponent, currentQuality);
         }
-
-        dpm.setPasswordQuality(mAdminComponent, currentQuality);
-        return expected == result;
     }
 
     private void assertHasPassword() throws InterruptedException {
-        assertTrue("No password set", waitUntilPasswordState(true));
+        assertTrue("No password set", getPasswordState());
     }
 
     private void assertNoPassword() throws InterruptedException {
-        assertTrue("Password is set", waitUntilPasswordState(false));
+        assertFalse("Password is set", getPasswordState());
     }
 
     /**
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
index fe2621e..5892c5c 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
@@ -378,17 +378,6 @@
     }
 
     private void assertPasswordSufficiency(boolean expectPasswordSufficient) {
-        int retries = 15;
-        // isActivePasswordSufficient() gets the result asynchronously so let's retry a few times
-        while (retries >= 0 && dpm.isActivePasswordSufficient() != expectPasswordSufficient) {
-            retries--;
-            try {
-                Thread.sleep(200);
-            } catch (InterruptedException e) {
-                break;
-            }
-        }
         assertEquals(expectPasswordSufficient, dpm.isActivePasswordSufficient());
-
     }
 }
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java
index 8f9b9b5..212d20f 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java
@@ -126,17 +126,6 @@
 
     protected void assertPasswordSufficiency(boolean expectPasswordSufficient) {
         waitUntilUserUnlocked();
-        int retries = 15;
-        // isActivePasswordSufficient() gets the result asynchronously so let's retry a few times
-        while (retries >= 0
-                && mDevicePolicyManager.isActivePasswordSufficient() != expectPasswordSufficient) {
-            retries--;
-            try {
-                Thread.sleep(200);
-            } catch (InterruptedException e) {
-                break;
-            }
-        }
         assertEquals(expectPasswordSufficient, mDevicePolicyManager.isActivePasswordSufficient());
     }
 
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerHelper.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerHelper.java
index 756058c..8e8a6d6 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerHelper.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerHelper.java
@@ -19,6 +19,7 @@
 import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL;
 
 import android.app.admin.DevicePolicyManager;
+import android.keystore.cts.KeyGenerationUtils;
 
 import java.util.Arrays;
 import java.util.List;
@@ -32,6 +33,9 @@
 
     private static final List<String> CERT_INSTALL_SCOPES = Arrays.asList(DELEGATION_CERT_INSTALL);
 
+    // MUST match the alias in PreSelectedKeyAccessTest
+    private static final String PRE_SELECTED_ALIAS = "pre-selected-rsa";
+
     private DevicePolicyManager mDpm;
 
     @Override
@@ -57,4 +61,19 @@
         assertFalse(mDpm.getDelegatePackages(ADMIN_RECEIVER_COMPONENT,
                 DELEGATION_CERT_INSTALL).contains(CERT_INSTALLER_PACKAGE));
     }
+
+    public void testManualGenerateKeyAndGrantAccess() {
+        KeyGenerationUtils.generateRsaKey(mDpm, ADMIN_RECEIVER_COMPONENT, PRE_SELECTED_ALIAS);
+        assertTrue(mDpm.grantKeyPairToApp(ADMIN_RECEIVER_COMPONENT, PRE_SELECTED_ALIAS,
+                    CERT_INSTALLER_PACKAGE));
+    }
+
+    public void testManualRemoveKeyGrant() {
+        assertTrue(mDpm.revokeKeyPairFromApp(ADMIN_RECEIVER_COMPONENT, PRE_SELECTED_ALIAS,
+                CERT_INSTALLER_PACKAGE));
+    }
+
+    public void testManualClearGeneratedKey() {
+        assertTrue(mDpm.removeKeyPair(ADMIN_RECEIVER_COMPONENT, PRE_SELECTED_ALIAS));
+    }
 }
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 fefe9cd..4b689d7 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
@@ -15,6 +15,9 @@
  */
 package com.android.cts.deviceandprofileowner;
 
+import static android.Manifest.permission.READ_CONTACTS;
+import static android.Manifest.permission.WRITE_CONTACTS;
+
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -47,7 +50,6 @@
             = "com.android.cts.permissionapp";
     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";
@@ -161,11 +163,19 @@
         assertPermissionRequest(PackageManager.PERMISSION_GRANTED);
     }
 
+    public void testAutoGrantMultiplePermissionsInGroup() throws Exception {
+        // set policy to autogrant
+        assertSetPermissionPolicy(DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT);
+        // both permissions should be granted
+        assertPermissionRequest(READ_CONTACTS, PackageManager.PERMISSION_GRANTED);
+        assertPermissionRequest(WRITE_CONTACTS, 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);
+        assertSetPermissionGrantState(CUSTOM_PERM_A_NAME,
+                DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
+        assertSetPermissionGrantState(CUSTOM_PERM_B_NAME,
+                DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
 
         /*
          * CUSTOM_PERM_A_NAME and CUSTOM_PERM_B_NAME are in the same permission group and one is
@@ -174,7 +184,7 @@
          * 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);
+        assertPermissionRequest(CUSTOM_PERM_B_NAME, PackageManager.PERMISSION_DENIED);
     }
 
     @Suppress // Flakey.
@@ -202,14 +212,14 @@
 
     public void testPermissionUpdate_setDeniedState() throws Exception {
         assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
-                PERMISSION_APP_PACKAGE_NAME, PERMISSION_NAME),
+                PERMISSION_APP_PACKAGE_NAME, READ_CONTACTS),
                 DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
         assertSetPermissionGrantState(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
     }
 
     public void testPermissionUpdate_setAutoDeniedPolicy() throws Exception {
         assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
-                PERMISSION_APP_PACKAGE_NAME, PERMISSION_NAME),
+                PERMISSION_APP_PACKAGE_NAME, READ_CONTACTS),
                 DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
         assertSetPermissionPolicy(DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY);
         assertPermissionRequest(PackageManager.PERMISSION_DENIED);
@@ -222,14 +232,14 @@
 
     public void testPermissionUpdate_setGrantedState() throws Exception {
         assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
-                PERMISSION_APP_PACKAGE_NAME, PERMISSION_NAME),
+                PERMISSION_APP_PACKAGE_NAME, READ_CONTACTS),
                 DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
         assertSetPermissionGrantState(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
     }
 
     public void testPermissionUpdate_setAutoGrantedPolicy() throws Exception {
         assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
-                PERMISSION_APP_PACKAGE_NAME, PERMISSION_NAME),
+                PERMISSION_APP_PACKAGE_NAME, READ_CONTACTS),
                 DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
         assertSetPermissionPolicy(DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT);
         assertPermissionRequest(PackageManager.PERMISSION_GRANTED);
@@ -266,14 +276,35 @@
     }
 
     private void assertPermissionRequest(int expected) throws Exception {
-        assertPermissionRequest(expected, null);
+        assertPermissionRequest(READ_CONTACTS, expected);
     }
 
-    private void assertPermissionRequest(int expected, String buttonResource) throws Exception {
-        assertPermissionRequest(expected, buttonResource, PERMISSION_NAME);
+    private void assertPermissionRequest(String permission, int expected) throws Exception {
+        assertPermissionRequest(permission, expected, null);
     }
 
-    private void assertPermissionRequest(int expected, String buttonResource, String permission)
+    private void assertPermissionRequest(int expected, String buttonResource)
+            throws Exception {
+        assertPermissionRequest(READ_CONTACTS, expected, buttonResource);
+    }
+
+    private void assertSetPermissionGrantState(int value) throws Exception {
+        assertSetPermissionGrantState(READ_CONTACTS, value);
+    }
+
+    private void assertPermissionGrantState(int expected) throws Exception {
+        assertPermissionGrantState(READ_CONTACTS, expected);
+    }
+
+    private void assertCannotSetPermissionGrantStateAppPreM(int value) throws Exception {
+        assertCannotSetPermissionGrantStateAppPreM(READ_CONTACTS, value);
+    }
+
+    private void assertCanSetPermissionGrantStateAppPreM(int value) throws Exception {
+        assertCanSetPermissionGrantStateAppPreM(READ_CONTACTS, value);
+    }
+
+    private void assertPermissionRequest(String permission, int expected, String buttonResource)
             throws Exception {
         Intent launchIntent = new Intent();
         launchIntent.setComponent(new ComponentName(PERMISSION_APP_PACKAGE_NAME,
@@ -288,13 +319,13 @@
                 PERMISSION_APP_PACKAGE_NAME));
     }
 
-    private void assertPermissionGrantState(int expected) throws Exception {
-        assertEquals(expected, mPackageManager.checkPermission(PERMISSION_NAME,
+    private void assertPermissionGrantState(String permission, int expected) throws Exception {
+        assertEquals(expected, mPackageManager.checkPermission(permission,
                 PERMISSION_APP_PACKAGE_NAME));
         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_CHECK_HAS_PERMISSION);
         launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
         mContext.startActivity(launchIntent);
@@ -308,10 +339,7 @@
                 value);
     }
 
-    private void assertSetPermissionGrantState(int value) throws Exception {
-        assertSetPermissionGrantState(value, PERMISSION_NAME);
-    }
-    private void assertSetPermissionGrantState(int value, String permission) throws Exception {
+    private void assertSetPermissionGrantState(String permission, int value) throws Exception {
         mDevicePolicyManager.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
                 PERMISSION_APP_PACKAGE_NAME, permission,
                 value);
@@ -331,32 +359,32 @@
                 PackageManager.PERMISSION_DENIED);
     }
 
-    private void assertCannotSetPermissionGrantStateAppPreM(int value) throws Exception {
+    private void assertCannotSetPermissionGrantStateAppPreM(String permission, int value) throws Exception {
         assertFalse(mDevicePolicyManager.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
-                SIMPLE_PRE_M_APP_PACKAGE_NAME, PERMISSION_NAME,
+                SIMPLE_PRE_M_APP_PACKAGE_NAME, permission,
                 value));
         assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
-                SIMPLE_PRE_M_APP_PACKAGE_NAME, PERMISSION_NAME),
+                SIMPLE_PRE_M_APP_PACKAGE_NAME, permission),
                 DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
 
         // Install time permissions should always be granted
         PackageInfo packageInfo = mContext.getPackageManager().getPackageInfo(
                 SIMPLE_PRE_M_APP_PACKAGE_NAME, 0);
         assertEquals(PackageManager.PERMISSION_GRANTED,
-                PermissionChecker.checkPermission(mContext, PERMISSION_NAME, -1,
+                PermissionChecker.checkPermission(mContext, permission, -1,
                         packageInfo.applicationInfo.uid, SIMPLE_PRE_M_APP_PACKAGE_NAME));
     }
 
-    private void assertCanSetPermissionGrantStateAppPreM(int value) throws Exception {
+    private void assertCanSetPermissionGrantStateAppPreM(String permission, int value) throws Exception {
         assertTrue(mDevicePolicyManager.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
-                SIMPLE_PRE_M_APP_PACKAGE_NAME, PERMISSION_NAME,
+                SIMPLE_PRE_M_APP_PACKAGE_NAME, permission,
                 value));
         assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
-                SIMPLE_PRE_M_APP_PACKAGE_NAME, PERMISSION_NAME),
+                SIMPLE_PRE_M_APP_PACKAGE_NAME, permission),
                 value);
 
         // Install time permissions should always be granted
-        assertEquals(mPackageManager.checkPermission(PERMISSION_NAME,
+        assertEquals(mPackageManager.checkPermission(permission,
                 SIMPLE_PRE_M_APP_PACKAGE_NAME),
                 PackageManager.PERMISSION_GRANTED);
 
@@ -365,7 +393,7 @@
 
         // For pre-M apps the access to the data might be prevented via app-ops. Hence check that
         // they are correctly set
-        boolean isGranted = (PermissionChecker.checkPermission(mContext, PERMISSION_NAME, -1,
+        boolean isGranted = (PermissionChecker.checkPermission(mContext, permission, -1,
                 packageInfo.applicationInfo.uid, SIMPLE_PRE_M_APP_PACKAGE_NAME)
                 == PackageManager.PERMISSION_GRANTED);
         switch (value) {
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DeviceIdentifiersTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DeviceIdentifiersTest.java
index ea60582..3c53de9 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DeviceIdentifiersTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DeviceIdentifiersTest.java
@@ -16,6 +16,7 @@
 package com.android.cts.deviceowner;
 
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.Build;
 import android.telephony.TelephonyManager;
 
@@ -72,39 +73,66 @@
         // SecurityException when querying for device identifiers.
         TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
                 Context.TELEPHONY_SERVICE);
+        // Allow the APIs to also return null if the telephony feature is not supported.
+        boolean hasTelephonyFeature =
+                mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
         try {
-            telephonyManager.getDeviceId();
-            fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getDeviceId"));
+            String deviceId = telephonyManager.getDeviceId();
+            if (hasTelephonyFeature) {
+                fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getDeviceId"));
+            } else {
+                assertEquals(null, deviceId);
+            }
         } catch (SecurityException expected) {
         }
 
         try {
-            telephonyManager.getImei();
-            fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getImei"));
+            String imei = telephonyManager.getImei();
+            if (hasTelephonyFeature) {
+                fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getImei"));
+            } else {
+                assertEquals(null, imei);
+            }
         } catch (SecurityException expected) {
         }
 
         try {
-            telephonyManager.getMeid();
-            fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getMeid"));
+            String meid = telephonyManager.getMeid();
+            if (hasTelephonyFeature) {
+                fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getMeid"));
+            } else {
+                assertEquals(null, meid);
+            }
         } catch (SecurityException expected) {
         }
 
         try {
-            telephonyManager.getSubscriberId();
-            fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getSubscriberId"));
+            String subscriberId = telephonyManager.getSubscriberId();
+            if (hasTelephonyFeature) {
+                fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getSubscriberId"));
+            } else {
+                assertEquals(null, subscriberId);
+            }
         } catch (SecurityException expected) {
         }
 
         try {
-            telephonyManager.getSimSerialNumber();
-            fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getSimSerialNumber"));
+            String simSerialNumber = telephonyManager.getSimSerialNumber();
+            if (hasTelephonyFeature) {
+                fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getSimSerialNumber"));
+            } else {
+                assertEquals(null, simSerialNumber);
+            }
         } catch (SecurityException expected) {
         }
 
         try {
-            Build.getSerial();
-            fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "Build#getSerial"));
+            String serial = Build.getSerial();
+            if (hasTelephonyFeature) {
+                fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "Build#getSerial"));
+            } else {
+              assertEquals(null, serial);
+            }
         } catch (SecurityException expected) {
         }
     }
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DevicePolicyLoggingTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DevicePolicyLoggingTest.java
index 9aad7b5..3d11999 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DevicePolicyLoggingTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DevicePolicyLoggingTest.java
@@ -16,6 +16,13 @@
 
 package com.android.cts.deviceowner;
 
+import static android.provider.Settings.Global.AUTO_TIME;
+import static android.provider.Settings.Global.AUTO_TIME_ZONE;
+import static android.provider.Settings.Global.DATA_ROAMING;
+import static android.provider.Settings.Global.USB_MASS_STORAGE_ENABLED;
+
+import android.provider.Settings;
+
 /**
  * Invocations of {@link android.app.admin.DevicePolicyManager} methods which are either not CTS
  * tested or the CTS tests call too many other methods. Used to verify that the relevant metrics
@@ -31,4 +38,29 @@
         mDevicePolicyManager.setStatusBarDisabled(getWho(), true);
         mDevicePolicyManager.setStatusBarDisabled(getWho(), false);
     }
+
+    public void testSetGlobalSettingLogged() {
+        final String autoTimeOriginalValue =
+                Settings.Global.getString(mContext.getContentResolver(), AUTO_TIME);
+        final String autoTimeZoneOriginalValue =
+                Settings.Global.getString(mContext.getContentResolver(), AUTO_TIME_ZONE);
+        final String dataRoamingOriginalValue =
+                Settings.Global.getString(mContext.getContentResolver(), DATA_ROAMING);
+        final String usbMassStorageOriginalValue =
+                Settings.Global.getString(mContext.getContentResolver(), USB_MASS_STORAGE_ENABLED);
+
+        try {
+            mDevicePolicyManager.setGlobalSetting(getWho(), AUTO_TIME, "1");
+            mDevicePolicyManager.setGlobalSetting(getWho(), AUTO_TIME_ZONE, "1");
+            mDevicePolicyManager.setGlobalSetting(getWho(), DATA_ROAMING, "1");
+            mDevicePolicyManager.setGlobalSetting(getWho(), USB_MASS_STORAGE_ENABLED, "1");
+        } finally {
+            mDevicePolicyManager.setGlobalSetting(getWho(), AUTO_TIME, autoTimeOriginalValue);
+            mDevicePolicyManager.setGlobalSetting(
+                    getWho(), AUTO_TIME_ZONE, autoTimeZoneOriginalValue);
+            mDevicePolicyManager.setGlobalSetting(getWho(), DATA_ROAMING, dataRoamingOriginalValue);
+            mDevicePolicyManager.setGlobalSetting(
+                    getWho(), USB_MASS_STORAGE_ENABLED, usbMassStorageOriginalValue);
+        }
+    }
 }
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DeviceIdentifiersTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DeviceIdentifiersTest.java
index 7c26fad..14e7b0c 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DeviceIdentifiersTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DeviceIdentifiersTest.java
@@ -16,6 +16,7 @@
 package com.android.cts.managedprofile;
 
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.Build;
 import android.telephony.TelephonyManager;
 
@@ -72,39 +73,66 @@
         // SecurityException when querying for device identifiers.
         TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
                 Context.TELEPHONY_SERVICE);
+        // Allow the APIs to also return null if the telephony feature is not supported.
+        boolean hasTelephonyFeature =
+                mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
         try {
-            telephonyManager.getDeviceId();
-            fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getDeviceId"));
+            String deviceId = telephonyManager.getDeviceId();
+            if (hasTelephonyFeature) {
+                fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getDeviceId"));
+            } else {
+                assertEquals(null, deviceId);
+            }
         } catch (SecurityException expected) {
         }
 
         try {
-            telephonyManager.getImei();
-            fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getImei"));
+            String imei = telephonyManager.getImei();
+            if (hasTelephonyFeature) {
+                fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getImei"));
+            } else {
+                assertEquals(null, imei);
+            }
         } catch (SecurityException expected) {
         }
 
         try {
-            telephonyManager.getMeid();
-            fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getMeid"));
+            String meid = telephonyManager.getMeid();
+            if (hasTelephonyFeature) {
+                fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getMeid"));
+            } else {
+                assertEquals(null, meid);
+            }
         } catch (SecurityException expected) {
         }
 
         try {
-            telephonyManager.getSubscriberId();
-            fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getSubscriberId"));
+            String subscriberId = telephonyManager.getSubscriberId();
+            if (hasTelephonyFeature) {
+                fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getSubscriberId"));
+            } else {
+                assertEquals(null, subscriberId);
+            }
         } catch (SecurityException expected) {
         }
 
         try {
-            telephonyManager.getSimSerialNumber();
-            fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getSimSerialNumber"));
+            String simSerialNumber = telephonyManager.getSimSerialNumber();
+            if (hasTelephonyFeature) {
+                fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getSimSerialNumber"));
+            } else {
+                assertEquals(null, simSerialNumber);
+            }
         } catch (SecurityException expected) {
         }
 
         try {
-            Build.getSerial();
-            fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "Build#getSerial"));
+            String serial = Build.getSerial();
+            if (hasTelephonyFeature) {
+                fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "Build#getSerial"));
+            } else {
+              assertEquals(null, serial);
+            }
         } catch (SecurityException expected) {
         }
     }
diff --git a/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/Android.bp b/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/Android.bp
index 2e8d9f4..f0f4716 100644
--- a/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/Android.bp
@@ -25,6 +25,7 @@
     srcs: ["src/**/*.java"],
     // tag this module as a cts test artifact
     test_suites: [
+        "arcts",
         "cts",
         "vts",
         "general-tests",
@@ -45,6 +46,7 @@
     srcs: ["src/**/*.java"],
     // tag this module as a cts test artifact
     test_suites: [
+        "arcts",
         "cts",
         "vts",
         "general-tests",
@@ -66,6 +68,7 @@
     srcs: ["src/**/*.java"],
     // tag this module as a cts test artifact
     test_suites: [
+        "arcts",
         "cts",
         "vts",
         "general-tests",
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AccountCheckHostSideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AccountCheckHostSideTest.java
index a79ae11..c279b28 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AccountCheckHostSideTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AccountCheckHostSideTest.java
@@ -16,6 +16,8 @@
 
 package com.android.cts.devicepolicy;
 
+import android.platform.test.annotations.LargeTest;
+
 import com.android.tradefed.log.LogUtil.CLog;
 
 import junit.framework.AssertionFailedError;
@@ -147,6 +149,7 @@
         return Integer.parseInt(count) > 0;
     }
 
+    @LargeTest
     public void testAccountCheck() throws Exception {
         if (!mHasFeature) {
             return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index 17bbf06..a1908ef 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -47,6 +47,8 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import javax.annotation.Nullable;
 
@@ -643,21 +645,15 @@
 
     protected int getUserSerialNumber(int userId) throws DeviceNotAvailableException{
         // TODO: Move this logic to ITestDevice.
-        // dumpsys user return lines like "UserInfo{0:Owner:13} serialNo=0"
-        String commandOutput = getDevice().executeShellCommand("dumpsys user");
-        String[] tokens = commandOutput.split("\\n");
-        for (String token : tokens) {
-            token = token.trim();
-            if (token.contains("UserInfo{" + userId + ":")) {
-                String[] split = token.split("serialNo=");
-                assertTrue(split.length == 2);
-                int serialNumber = Integer.parseInt(split[1]);
-                CLog.d("Serial number of user " + userId + ": "
-                        + serialNumber);
-                return serialNumber;
-            }
+        // dumpsys user output contains lines like "UserInfo{0:Owner:13} serialNo=0 isPrimary=true"
+        final Pattern pattern =
+                Pattern.compile("UserInfo\\{" + userId + ":[^\\n]*\\sserialNo=(\\d+)\\s");
+        final String commandOutput = getDevice().executeShellCommand("dumpsys user");
+        final Matcher matcher = pattern.matcher(commandOutput);
+        if (matcher.find()) {
+            return Integer.parseInt(matcher.group(1));
         }
-        fail("Couldn't find user " + userId);
+        fail("Couldn't find serial number for user " + userId);
         return -1;
     }
 
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseManagedProfileTest.java
new file mode 100644
index 0000000..56d5800
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseManagedProfileTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.devicepolicy;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil;
+
+public abstract class BaseManagedProfileTest extends BaseDevicePolicyTest {
+    protected static final String MANAGED_PROFILE_PKG = "com.android.cts.managedprofile";
+    protected static final String INTENT_SENDER_PKG = "com.android.cts.intent.sender";
+    protected static final String INTENT_RECEIVER_PKG = "com.android.cts.intent.receiver";
+    protected static final String ADMIN_RECEIVER_TEST_CLASS =
+            MANAGED_PROFILE_PKG + ".BaseManagedProfileTest$BasicAdminReceiver";
+    protected static final String INTENT_SENDER_APK = "CtsIntentSenderApp.apk";
+    protected static final String INTENT_RECEIVER_APK = "CtsIntentReceiverApp.apk";
+    protected static final String SIMPLE_APP_APK = "CtsSimpleApp.apk";
+    private static final String MANAGED_PROFILE_APK = "CtsManagedProfileApp.apk";
+    private static final String NOTIFICATION_PKG =
+            "com.android.cts.managedprofiletests.notificationsender";
+    //The maximum time to wait for user to be unlocked.
+    private static final long USER_UNLOCK_TIMEOUT_NANO = 30_000_000_000L;
+    private static final String USER_STATE_UNLOCKED = "RUNNING_UNLOCKED";
+    protected int mParentUserId;
+    // ID of the profile we'll create. This will always be a profile of the parent.
+    protected int mProfileUserId;
+    protected boolean mHasNfcFeature;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        // We need multi user to be supported in order to create a profile of the user owner.
+        mHasFeature = mHasFeature && hasDeviceFeature("android.software.managed_users");
+        mHasNfcFeature = hasDeviceFeature("android.hardware.nfc")
+                && hasDeviceFeature("android.sofware.nfc.beam");
+
+        if (mHasFeature) {
+            removeTestUsers();
+            mParentUserId = mPrimaryUserId;
+            mProfileUserId = createManagedProfile(mParentUserId);
+            startUser(mProfileUserId);
+
+            // Install the APK on both primary and profile user in one single transaction.
+            // If they were installed separately, the second installation would become an app
+            // update and result in the current running test process being killed.
+            installAppAsUser(MANAGED_PROFILE_APK, USER_ALL);
+            setProfileOwnerOrFail(MANAGED_PROFILE_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
+                    mProfileUserId);
+            waitForUserUnlock();
+        }
+    }
+
+    private void waitForUserUnlock() throws Exception {
+        final String command = String.format("am get-started-user-state %d", mProfileUserId);
+        final long deadline = System.nanoTime() + USER_UNLOCK_TIMEOUT_NANO;
+        while (System.nanoTime() <= deadline) {
+            if (getDevice().executeShellCommand(command).startsWith(USER_STATE_UNLOCKED)) {
+                return;
+            }
+            Thread.sleep(100);
+        }
+        fail("Profile user is not unlocked.");
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mHasFeature) {
+            removeUser(mProfileUserId);
+            getDevice().uninstallPackage(MANAGED_PROFILE_PKG);
+            getDevice().uninstallPackage(INTENT_SENDER_PKG);
+            getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
+            getDevice().uninstallPackage(NOTIFICATION_PKG);
+        }
+        super.tearDown();
+    }
+
+    protected void disableActivityForUser(String activityName, int userId)
+            throws DeviceNotAvailableException {
+        String command = "am start -W --user " + userId
+                + " --es extra-package " + MANAGED_PROFILE_PKG
+                + " --es extra-class-name " + MANAGED_PROFILE_PKG + "." + activityName
+                + " " + MANAGED_PROFILE_PKG + "/.ComponentDisablingActivity ";
+        LogUtil.CLog.d("Output for command " + command + ": "
+                + getDevice().executeShellCommand(command));
+    }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
index b59222e..09c900a 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
@@ -2,6 +2,8 @@
 
 import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
 
+import android.platform.test.annotations.FlakyTest;
+import android.platform.test.annotations.LargeTest;
 import android.stats.devicepolicy.EventId;
 
 import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
@@ -10,6 +12,7 @@
 import java.io.FileNotFoundException;
 import java.util.Collections;
 import java.util.Map;
+
 import javax.annotation.Nullable;
 
 /**
@@ -57,10 +60,14 @@
         installAppAsUser(SIMPLE_APP_APK, userId);
     }
 
+    @FlakyTest
+    @LargeTest
     public void testPrimaryUserToPrimaryUser() throws Exception {
         verifyCrossProfileAppsApi(mPrimaryUserId, mPrimaryUserId, NON_TARGET_USER_TEST_CLASS);
     }
 
+    @FlakyTest
+    @LargeTest
     public void testPrimaryUserToManagedProfile() throws Exception {
         if (!mHasManagedUserFeature) {
             return;
@@ -68,6 +75,7 @@
         verifyCrossProfileAppsApi(mPrimaryUserId, mProfileId, TARGET_USER_TEST_CLASS);
     }
 
+    @LargeTest
     public void testManagedProfileToPrimaryUser() throws Exception {
         if (!mHasManagedUserFeature) {
             return;
@@ -75,6 +83,7 @@
         verifyCrossProfileAppsApi(mProfileId, mPrimaryUserId, TARGET_USER_TEST_CLASS);
     }
 
+    @LargeTest
     public void testStartActivity() throws Exception {
         if (!mHasManagedUserFeature) {
             return;
@@ -82,6 +91,7 @@
         verifyCrossProfileAppsApi(mProfileId, mPrimaryUserId, START_ACTIVITY_TEST_CLASS);
     }
 
+    @LargeTest
     public void testPrimaryUserToSecondaryUser() throws Exception {
         if (!mCanTestMultiUser) {
             return;
@@ -89,6 +99,7 @@
         verifyCrossProfileAppsApi(mPrimaryUserId, mSecondaryUserId, NON_TARGET_USER_TEST_CLASS);
     }
 
+    @LargeTest
     public void testSecondaryUserToManagedProfile() throws Exception {
         if (!mCanTestMultiUser || !mHasManagedUserFeature) {
             return;
@@ -97,6 +108,7 @@
 
     }
 
+    @LargeTest
     public void testManagedProfileToSecondaryUser() throws Exception {
         if (!mCanTestMultiUser || !mHasManagedUserFeature) {
             return;
@@ -104,6 +116,7 @@
         verifyCrossProfileAppsApi(mProfileId, mSecondaryUserId, NON_TARGET_USER_TEST_CLASS);
     }
 
+    @LargeTest
     public void testStartMainActivity_logged() throws Exception {
         if (!mHasManagedUserFeature) {
             return;
@@ -123,6 +136,7 @@
                         .build());
     }
 
+    @LargeTest
     public void testGetTargetUserProfiles_logged() throws Exception {
         if (!mHasManagedUserFeature) {
             return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java
index eca6953..f01a88a 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java
@@ -16,11 +16,7 @@
 
 package com.android.cts.devicepolicy;
 
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.cts.devicepolicy.BaseDevicePolicyTest.Settings;
-
-import java.io.File;
-import java.lang.Exception;
+import android.platform.test.annotations.FlakyTest;
 
 /**
  * This class is used for tests that need to do something special before setting the device
@@ -101,6 +97,7 @@
         }
     }
 
+    @FlakyTest
     public void testCannotSetDeviceOwnerWhenAccountPresent() throws Exception {
         if (!mHasFeature) {
             return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminHostSideTestApi23.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminHostSideTestApi23.java
index 3a74953..7cd15b5 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminHostSideTestApi23.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminHostSideTestApi23.java
@@ -15,6 +15,8 @@
  */
 package com.android.cts.devicepolicy;
 
+import android.platform.test.annotations.FlakyTest;
+
 /**
  * BaseDeviceAdminHostSideTest for device admin targeting API level 23.
  */
@@ -27,6 +29,7 @@
     /**
      * Device admin with no BIND_DEVICE_ADMIN can still be activated, if the target SDK <= 23.
      */
+    @FlakyTest
     public void testAdminWithNoProtection() throws Exception {
         if (!mHasFeature) {
             return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceProfileOwnerTest.java
index a773737..f48a656 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceProfileOwnerTest.java
@@ -15,6 +15,8 @@
  */
 package com.android.cts.devicepolicy;
 
+import android.platform.test.annotations.LargeTest;
+
 public class DeviceAdminServiceProfileOwnerTest extends BaseDeviceAdminServiceTest {
 
     private int mUserId;
@@ -46,4 +48,10 @@
     protected void setAsOwnerOrFail(String component) throws Exception {
         setProfileOwnerOrFail(component, getUserId());
     }
+
+    @Override
+    @LargeTest
+    public void testAll() throws Throwable {
+        super.testAll();
+    }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index ceafda1..7b0a5bb 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -18,9 +18,12 @@
 
 import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
 
+import android.platform.test.annotations.FlakyTest;
+import android.platform.test.annotations.LargeTest;
 import android.platform.test.annotations.RequiresDevice;
 import android.stats.devicepolicy.EventId;
 
+import com.android.cts.devicepolicy.annotations.LockSettingsTest;
 import com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier;
 import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
 import com.android.tradefed.device.DeviceNotAvailableException;
@@ -564,6 +567,14 @@
         executeDeviceTestMethod(".PermissionsTest", "testPermissionPolicy");
     }
 
+    public void testAutoGrantMultiplePermissionsInGroup() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        installAppPermissionAppAsUser();
+        executeDeviceTestMethod(".PermissionsTest", "testAutoGrantMultiplePermissionsInGroup");
+    }
+
     public void testPermissionMixedPolicies() throws Exception {
         if (!mHasFeature) {
             return;
@@ -848,6 +859,36 @@
                     ".DirectDelegatedCertInstallerTest", mUserId));
     }
 
+    // This test generates a key pair and validates that an app can be silently granted
+    // access to it.
+    public void testSetKeyGrant() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+
+        // Install an app
+        installAppAsUser(CERT_INSTALLER_APK, mUserId);
+
+        try {
+            // First, generate a key and grant the cert installer access to it.
+            runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".DelegatedCertInstallerHelper",
+                    "testManualGenerateKeyAndGrantAccess", mUserId);
+            // Test the key is usable.
+            runDeviceTestsAsUser("com.android.cts.certinstaller",
+                    ".PreSelectedKeyAccessTest", "testAccessingPreSelectedAliasExpectingSuccess",
+                    mUserId);
+            // Remove the grant
+            runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".DelegatedCertInstallerHelper",
+                    "testManualRemoveKeyGrant", mUserId);
+            // Run another test to make sure the app no longer has access to the key.
+            runDeviceTestsAsUser("com.android.cts.certinstaller",
+                    ".PreSelectedKeyAccessTest", "testAccessingPreSelectedAliasWithoutGrant", mUserId);
+        } finally {
+            runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".DelegatedCertInstallerHelper",
+                    "testManualClearGeneratedKey", mUserId);
+        }
+    }
+
     // Sets restrictions and launches non-admin app, that tries to set wallpaper.
     // Non-admin apps must not violate any user restriction.
     public void testSetWallpaper_disallowed() throws Exception {
@@ -857,6 +898,11 @@
             return;
         }
 
+        if (!hasService("wallpaper")) {
+            CLog.d("testSetWallpaper_disallowed(): device does not support wallpapers");
+            return;
+        }
+
         installAppAsUser(CUSTOMIZATION_APP_APK, mUserId);
         try {
             changeUserRestrictionOrFail(DISALLOW_SET_WALLPAPER, true, mUserId);
@@ -873,6 +919,10 @@
         if (!mHasFeature) {
             return;
         }
+        if (!hasService("wallpaper")) {
+            CLog.d("testDisallowSetWallpaper_allowed(): device does not support wallpapers");
+            return;
+        }
         executeDeviceTestMethod(".CustomizationRestrictionsTest",
                 "testDisallowSetWallpaper_allowed");
     }
@@ -1058,6 +1108,7 @@
                     .build());
     }
 
+    @FlakyTest(bugId = 132226089)
     public void testLockTask() throws Exception {
         if (!mHasFeature) {
             return;
@@ -1084,6 +1135,7 @@
         }
     }
 
+    @LargeTest
     public void testLockTaskAfterReboot() throws Exception {
         if (!mHasFeature) {
             return;
@@ -1104,6 +1156,7 @@
         }
     }
 
+    @LargeTest
     public void testLockTaskAfterReboot_tryOpeningSettings() throws Exception {
         if (!mHasFeature) {
             return;
@@ -1285,6 +1338,7 @@
                     .build());
     }
 
+    @LockSettingsTest
     public void testResetPasswordWithToken() throws Exception {
         if (!mHasFeature || !mHasSecureLockScreen) {
             return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusProfileOwnerTest.java
index 25168ff..42b1135 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusProfileOwnerTest.java
@@ -18,11 +18,14 @@
 
 import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
 
+import android.platform.test.annotations.FlakyTest;
+import android.stats.devicepolicy.EventId;
+import android.platform.test.annotations.LargeTest;
+
 import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
 import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper.Builder;
-import java.util.List;
 
-import android.stats.devicepolicy.EventId;
+import java.util.List;
 
 /**
  * Tests for having both device owner and profile owner. Device owner is setup for you in
@@ -93,6 +96,7 @@
     /**
      * Both device owner and profile are the same package ({@link #COMP_DPC_PKG}).
      */
+    @LargeTest
     public void testBindDeviceAdminServiceAsUser_corpOwnedManagedProfile() throws Exception {
         if (!mHasFeature) {
             return;
@@ -116,6 +120,7 @@
      * Same as {@link #testBindDeviceAdminServiceAsUser_corpOwnedManagedProfile} except
      * creating managed profile through ManagedProvisioning like normal flow
      */
+    @FlakyTest
     public void testBindDeviceAdminServiceAsUser_corpOwnedManagedProfileWithManagedProvisioning()
             throws Exception {
         if (!mHasFeature) {
@@ -137,6 +142,7 @@
      * {@link #testBindDeviceAdminServiceAsUser_corpOwnedManagedProfileWithManagedProvisioning}
      * except we don't enable the profile.
      */
+    @FlakyTest
     public void testBindDeviceAdminServiceAsUser_canBindEvenIfProfileNotEnabled() throws Exception {
         if (!mHasFeature) {
             return;
@@ -174,6 +180,7 @@
      * Both device owner and profile are the same package ({@link #COMP_DPC_PKG}), as setup
      * by createAndManagedUser.
      */
+    @FlakyTest
     public void testBindDeviceAdminServiceAsUser_secondaryUser() throws Exception {
         if (!mHasFeature || !canCreateAdditionalUsers(1)) {
             return;
@@ -195,6 +202,7 @@
      * Test that the DO can talk to both a managed profile and managed secondary user at the same
      * time.
      */
+    @FlakyTest
     public void testBindDeviceAdminServiceAsUser_compPlusSecondaryUser() throws Exception {
         if (!mHasFeature || !canCreateAdditionalUsers(2)) {
             return;
@@ -416,6 +424,7 @@
         }
     }
 
+    @FlakyTest
     public void testRequestBugreportAvailableIfAffiliated() throws Exception {
         if (!mHasFeature) {
             return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index d223f03..91a09d0 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -18,6 +18,8 @@
 
 import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
 
+import android.platform.test.annotations.FlakyTest;
+import android.platform.test.annotations.LargeTest;
 import android.stats.devicepolicy.EventId;
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
@@ -100,6 +102,15 @@
     /** CreateAndManageUser is available and an additional user can be created. */
     private boolean mHasCreateAndManageUserFeature;
 
+    /**
+     * Copied from {@link android.app.admin.DevicePolicyManager}
+     */
+    private static final String GLOBAL_SETTING_AUTO_TIME = "auto_time";
+    private static final String GLOBAL_SETTING_AUTO_TIME_ZONE = "auto_time_zone";
+    private static final String GLOBAL_SETTING_DATA_ROAMING = "data_roaming";
+    private static final String GLOBAL_SETTING_USB_MASS_STORAGE_ENABLED =
+            "usb_mass_storage_enabled";
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -150,6 +161,7 @@
                 .build());
     }
 
+    @FlakyTest(bugId = 137088260)
     public void testWifi() throws Exception {
         if (!mHasFeature || !hasDeviceFeature("android.hardware.wifi")) {
             return;
@@ -175,6 +187,7 @@
         }
     }
 
+    @FlakyTest(bugId = 137071121)
     public void testCreateAndManageUser_LowStorage() throws Exception {
         if (!mHasCreateAndManageUserFeature) {
             return;
@@ -231,6 +244,7 @@
      * to the user.
      * {@link android.app.admin.DevicePolicyManager#switchUser} is tested.
      */
+    @FlakyTest(bugId = 131743223)
     public void testCreateAndManageUser_SwitchUser() throws Exception {
         if (!mHasCreateAndManageUserFeature || !canStartAdditionalUsers(1)) {
             return;
@@ -414,6 +428,7 @@
         }
     }
 
+    @FlakyTest(bugId = 126955083)
     public void testUserAddedOrRemovedBroadcasts() throws Exception {
         if (mHasCreateAndManageUserFeature) {
             executeDeviceTestMethod(".CreateAndManageUserTest",
@@ -448,6 +463,7 @@
         }
     }
 
+    @FlakyTest(bugId = 137093665)
     public void testSecurityLoggingWithSingleUser() throws Exception {
         if (!mHasFeature) {
             return;
@@ -531,6 +547,7 @@
         }
     }
 
+    @FlakyTest(bugId = 137092833)
     public void testNetworkLoggingWithSingleUser() throws Exception {
         if (!mHasFeature) {
             return;
@@ -548,6 +565,7 @@
                 Collections.singletonMap(ARG_NETWORK_LOGGING_BATCH_COUNT, Integer.toString(2)));
     }
 
+    @LargeTest
     public void testNetworkLogging_rebootResetsId() throws Exception {
         if (!mHasFeature) {
             return;
@@ -573,6 +591,7 @@
         executeDeviceTestMethod(".AffiliationTest", "testSetAffiliationId_containsEmptyString");
     }
 
+    @LargeTest
     public void testSystemUpdatePolicy() throws Exception {
         if (!mHasFeature) {
             return;
@@ -610,6 +629,7 @@
                     .build());
     }
 
+    @FlakyTest(bugId = 127101449)
     public void testWifiConfigLockdown() throws Exception {
         final boolean hasWifi = hasDeviceFeature("android.hardware.wifi");
         if (hasWifi && mHasFeature) {
@@ -698,6 +718,7 @@
                 "testIsProvisioningAllowedTrueForManagedProfileAction");
     }
 
+    @FlakyTest(bugId = 137096267)
     public void testAdminActionBookkeeping() throws Exception {
         if (!mHasFeature) {
             return;
@@ -837,6 +858,7 @@
         }
     }
 
+    @LargeTest
     public void testPackageInstallCache_multiUser() throws Exception {
         if (!mHasFeature || !canCreateAdditionalUsers(1)) {
             return;
@@ -906,6 +928,7 @@
         executeDeviceOwnerTest("OverrideApnTest");
     }
 
+    @FlakyTest(bugId = 134487729)
     public void testPrivateDnsPolicy() throws Exception {
         if (!mHasFeature) {
             return;
@@ -1011,6 +1034,30 @@
         }
     }
 
+    public void testSetGlobalSettingLogged() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        assertMetricsLogged(getDevice(), () -> {
+            executeDeviceTestMethod(".DevicePolicyLoggingTest", "testSetGlobalSettingLogged");
+        }, new DevicePolicyEventWrapper.Builder(EventId.SET_GLOBAL_SETTING_VALUE)
+                .setAdminPackageName(DEVICE_OWNER_PKG)
+                .setStrings(GLOBAL_SETTING_AUTO_TIME, "1")
+                .build(),
+        new DevicePolicyEventWrapper.Builder(EventId.SET_GLOBAL_SETTING_VALUE)
+                .setAdminPackageName(DEVICE_OWNER_PKG)
+                .setStrings(GLOBAL_SETTING_AUTO_TIME_ZONE, "1")
+                .build(),
+        new DevicePolicyEventWrapper.Builder(EventId.SET_GLOBAL_SETTING_VALUE)
+                .setAdminPackageName(DEVICE_OWNER_PKG)
+                .setStrings(GLOBAL_SETTING_DATA_ROAMING, "1")
+                .build(),
+        new DevicePolicyEventWrapper.Builder(EventId.SET_GLOBAL_SETTING_VALUE)
+                .setAdminPackageName(DEVICE_OWNER_PKG)
+                .setStrings(GLOBAL_SETTING_USB_MASS_STORAGE_ENABLED, "1")
+                .build());
+    }
+
     private void executeDeviceOwnerTest(String testClassName) throws Exception {
         if (!mHasFeature) {
             return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java
index c756d16..bf2f6d4 100755
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java
@@ -16,8 +16,8 @@
 
 package com.android.cts.devicepolicy;
 
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.device.DeviceNotAvailableException;
+import android.platform.test.annotations.FlakyTest;
+
 import com.android.tradefed.log.LogUtil.CLog;
 
 import java.util.Collections;
@@ -134,6 +134,7 @@
                 mParentUserId, Collections.singletonMap(PARAM_TEST_USER, mMainUserSerialNumber));
     }
 
+    @FlakyTest
     public void testLauncherCallbackPackageAddedProfile() throws Exception {
         if (!mHasFeature) {
             return;
@@ -146,6 +147,7 @@
                 mParentUserId, Collections.singletonMap(PARAM_TEST_USER, mProfileSerialNumber));
     }
 
+    @FlakyTest
     public void testLauncherCallbackPackageRemovedProfile() throws Exception {
         if (!mHasFeature) {
             return;
@@ -159,6 +161,7 @@
                 mParentUserId, Collections.singletonMap(PARAM_TEST_USER, mProfileSerialNumber));
     }
 
+    @FlakyTest
     public void testLauncherCallbackPackageChangedProfile() throws Exception {
         if (!mHasFeature) {
             return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsSingleUserTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsSingleUserTest.java
index 5d67a46..19a77d3 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsSingleUserTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsSingleUserTest.java
@@ -16,9 +16,7 @@
 
 package com.android.cts.devicepolicy;
 
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.log.LogUtil.CLog;
+import android.platform.test.annotations.FlakyTest;
 
 import java.util.Collections;
 
@@ -62,6 +60,7 @@
                 mCurrentUserId, Collections.singletonMap(PARAM_TEST_USER, mSerialNumber));
     }
 
+    @FlakyTest
     public void testLauncherCallbackPackageAddedMainUser() throws Exception {
         if (!mHasLauncherApps) {
             return;
@@ -75,6 +74,7 @@
                 mCurrentUserId, Collections.singletonMap(PARAM_TEST_USER, mSerialNumber));
     }
 
+    @FlakyTest
     public void testLauncherCallbackPackageRemovedMainUser() throws Exception {
         if (!mHasLauncherApps) {
             return;
@@ -88,6 +88,7 @@
                 mCurrentUserId, Collections.singletonMap(PARAM_TEST_USER, mSerialNumber));
     }
 
+    @FlakyTest
     public void testLauncherCallbackPackageChangedMainUser() throws Exception {
         if (!mHasLauncherApps) {
             return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileContactsTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileContactsTest.java
new file mode 100644
index 0000000..f667024
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileContactsTest.java
@@ -0,0 +1,416 @@
+/*
+ * 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.devicepolicy;
+
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+
+import android.platform.test.annotations.FlakyTest;
+import android.platform.test.annotations.LargeTest;
+import android.stats.devicepolicy.EventId;
+
+import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil;
+
+import java.util.concurrent.Callable;
+
+public class ManagedProfileContactsTest extends BaseManagedProfileTest {
+    private static final String DIRECTORY_PROVIDER_APK = "CtsContactDirectoryProvider.apk";
+    private static final String DIRECTORY_PROVIDER_PKG
+            = "com.android.cts.contactdirectoryprovider";
+    private static final String PRIMARY_DIRECTORY_PREFIX = "Primary";
+    private static final String MANAGED_DIRECTORY_PREFIX = "Managed";
+    private static final String DIRECTORY_PRIVOIDER_URI
+            = "content://com.android.cts.contact.directory.provider/";
+    private static final String SET_CUSTOM_DIRECTORY_PREFIX_METHOD = "set_prefix";
+
+    @LargeTest
+    public void testManagedContactsUris() throws Exception {
+        runManagedContactsTest(() -> {
+            ContactsTestSet contactsTestSet = new ContactsTestSet(ManagedProfileContactsTest.this,
+                    MANAGED_PROFILE_PKG, mParentUserId, mProfileUserId);
+
+            contactsTestSet.setCallerIdEnabled(true);
+            contactsTestSet.setContactsSearchEnabled(true);
+            contactsTestSet.checkIfCanLookupEnterpriseContacts(true);
+            contactsTestSet.checkIfCanFilterEnterpriseContacts(true);
+            contactsTestSet.checkIfCanFilterSelfContacts();
+            return null;
+        });
+    }
+
+    @FlakyTest
+    public void testManagedQuickContacts() throws Exception {
+        runManagedContactsTest(() -> {
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+                    "testQuickContact", mParentUserId);
+            return null;
+        });
+    }
+
+    @FlakyTest
+    public void testManagedContactsPolicies() throws Exception {
+        runManagedContactsTest(() -> {
+            ContactsTestSet contactsTestSet = new ContactsTestSet(ManagedProfileContactsTest.this,
+                    MANAGED_PROFILE_PKG, mParentUserId, mProfileUserId);
+            try {
+                contactsTestSet.setCallerIdEnabled(true);
+                contactsTestSet.setContactsSearchEnabled(false);
+                contactsTestSet.checkIfCanLookupEnterpriseContacts(true);
+                contactsTestSet.checkIfCanFilterEnterpriseContacts(false);
+                contactsTestSet.checkIfCanFilterSelfContacts();
+                contactsTestSet.setCallerIdEnabled(false);
+                contactsTestSet.setContactsSearchEnabled(true);
+                contactsTestSet.checkIfCanLookupEnterpriseContacts(false);
+                contactsTestSet.checkIfCanFilterEnterpriseContacts(true);
+                contactsTestSet.checkIfCanFilterSelfContacts();
+                contactsTestSet.setCallerIdEnabled(false);
+                contactsTestSet.setContactsSearchEnabled(false);
+                contactsTestSet.checkIfCanLookupEnterpriseContacts(false);
+                contactsTestSet.checkIfCanFilterEnterpriseContacts(false);
+                contactsTestSet.checkIfCanFilterSelfContacts();
+                contactsTestSet.checkIfNoEnterpriseDirectoryFound();
+                assertMetricsLogged(getDevice(), () -> {
+                    contactsTestSet.setCallerIdEnabled(true);
+                    contactsTestSet.setCallerIdEnabled(false);
+                }, new DevicePolicyEventWrapper
+                            .Builder(EventId.SET_CROSS_PROFILE_CALLER_ID_DISABLED_VALUE)
+                            .setAdminPackageName(MANAGED_PROFILE_PKG)
+                            .setBoolean(false)
+                            .build(),
+                    new DevicePolicyEventWrapper
+                            .Builder(EventId.SET_CROSS_PROFILE_CALLER_ID_DISABLED_VALUE)
+                            .setAdminPackageName(MANAGED_PROFILE_PKG)
+                            .setBoolean(true)
+                            .build());
+                assertMetricsLogged(getDevice(), () -> {
+                    contactsTestSet.setContactsSearchEnabled(true);
+                    contactsTestSet.setContactsSearchEnabled(false);
+                }, new DevicePolicyEventWrapper
+                            .Builder(EventId.SET_CROSS_PROFILE_CONTACTS_SEARCH_DISABLED_VALUE)
+                            .setAdminPackageName(MANAGED_PROFILE_PKG)
+                            .setBoolean(false)
+                            .build(),
+                    new DevicePolicyEventWrapper
+                            .Builder(EventId.SET_CROSS_PROFILE_CONTACTS_SEARCH_DISABLED_VALUE)
+                            .setAdminPackageName(MANAGED_PROFILE_PKG)
+                            .setBoolean(true)
+                            .build());
+                return null;
+            } finally {
+                // reset policies
+                contactsTestSet.setCallerIdEnabled(true);
+                contactsTestSet.setContactsSearchEnabled(true);
+            }
+        });
+    }
+
+    private void setDirectoryPrefix(String directoryName, int userId)
+            throws DeviceNotAvailableException {
+        String command = "content call --uri " + DIRECTORY_PRIVOIDER_URI
+                + " --user " + userId
+                + " --method " + SET_CUSTOM_DIRECTORY_PREFIX_METHOD
+                + " --arg " + directoryName;
+        LogUtil.CLog.d("Output for command " + command + ": "
+                + getDevice().executeShellCommand(command));
+    }
+
+    private void runManagedContactsTest(Callable<Void> callable) throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+
+        try {
+            // Allow cross profile contacts search.
+            // TODO test both on and off.
+            getDevice().executeShellCommand(
+                    "settings put --user " + mProfileUserId
+                    + " secure managed_profile_contact_remote_search 1");
+
+            // Add test account
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+                    "testAddTestAccount", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+                    "testAddTestAccount", mProfileUserId);
+
+            // Install directory provider to both primary and managed profile
+            installAppAsUser(DIRECTORY_PROVIDER_APK, USER_ALL);
+            setDirectoryPrefix(PRIMARY_DIRECTORY_PREFIX, mParentUserId);
+            setDirectoryPrefix(MANAGED_DIRECTORY_PREFIX, mProfileUserId);
+
+            // Check enterprise directory API works
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+                    "testGetDirectoryListInPrimaryProfile", mParentUserId);
+
+            // Insert Primary profile Contacts
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+                    "testPrimaryProfilePhoneAndEmailLookup_insertedAndfound", mParentUserId);
+            // Insert Managed profile Contacts
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+                    "testManagedProfilePhoneAndEmailLookup_insertedAndfound", mProfileUserId);
+            // Insert a primary contact with same phone & email as other
+            // enterprise contacts
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+                    "testPrimaryProfileDuplicatedPhoneEmailContact_insertedAndfound",
+                    mParentUserId);
+            // Insert a enterprise contact with same phone & email as other
+            // primary contacts
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+                    "testManagedProfileDuplicatedPhoneEmailContact_insertedAndfound",
+                    mProfileUserId);
+
+            callable.call();
+
+        } finally {
+            // Clean up in managed profile and primary profile
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+                    "testCurrentProfileContacts_removeContacts", mProfileUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+                    "testCurrentProfileContacts_removeContacts", mParentUserId);
+            getDevice().uninstallPackage(DIRECTORY_PROVIDER_PKG);
+        }
+    }
+
+    /*
+     * Container for running ContactsTest under multi-user environment
+     */
+    private static class ContactsTestSet {
+
+        private ManagedProfileContactsTest mManagedProfileContactsTest;
+        private String mManagedProfilePackage;
+        private int mParentUserId;
+        private int mProfileUserId;
+
+        public ContactsTestSet(ManagedProfileContactsTest managedProfileContactsTest,
+                String managedProfilePackage, int parentUserId, int profileUserId) {
+            mManagedProfileContactsTest = managedProfileContactsTest;
+            mManagedProfilePackage = managedProfilePackage;
+            mParentUserId = parentUserId;
+            mProfileUserId = profileUserId;
+        }
+
+        private void runDeviceTestsAsUser(String pkgName, String testClassName,
+                String testMethodName, Integer userId) throws DeviceNotAvailableException {
+            mManagedProfileContactsTest.runDeviceTestsAsUser(pkgName, testClassName, testMethodName,
+                    userId);
+        }
+
+        // Enable / Disable
+        public void setCallerIdEnabled(boolean enabled) throws DeviceNotAvailableException {
+            if (enabled) {
+                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                        "testSetCrossProfileCallerIdDisabled_false", mProfileUserId);
+            } else {
+                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                        "testSetCrossProfileCallerIdDisabled_true", mProfileUserId);
+            }
+        }
+
+        // Enable / Disable cross profile contacts search
+        public void setContactsSearchEnabled(boolean enabled) throws DeviceNotAvailableException {
+            if (enabled) {
+                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                        "testSetCrossProfileContactsSearchDisabled_false", mProfileUserId);
+            } else {
+                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                        "testSetCrossProfileContactsSearchDisabled_true", mProfileUserId);
+            }
+        }
+
+        public void checkIfCanLookupEnterpriseContacts(boolean expected)
+                throws DeviceNotAvailableException {
+            // Primary user cannot use ordinary phone/email lookup api to access
+            // managed contacts
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testPrimaryProfilePhoneLookup_canNotAccessEnterpriseContact", mParentUserId);
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testPrimaryProfileEmailLookup_canNotAccessEnterpriseContact", mParentUserId);
+            // Primary user can use ENTERPRISE_CONTENT_FILTER_URI to access
+            // primary contacts
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testPrimaryProfileEnterprisePhoneLookup_canAccessPrimaryContact",
+                    mParentUserId);
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testPrimaryProfileEnterpriseEmailLookup_canAccessPrimaryContact",
+                    mParentUserId);
+            // When there exist contacts with the same phone/email in primary &
+            // enterprise,
+            // primary user can use ENTERPRISE_CONTENT_FILTER_URI to access the
+            // primary contact.
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testPrimaryProfileEnterpriseEmailLookupDuplicated_canAccessPrimaryContact",
+                    mParentUserId);
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testPrimaryProfileEnterprisePhoneLookupDuplicated_canAccessPrimaryContact",
+                    mParentUserId);
+
+            // Managed user cannot use ordinary phone/email lookup api to access
+            // primary contacts
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testManagedProfilePhoneLookup_canNotAccessPrimaryContact", mProfileUserId);
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testManagedProfileEmailLookup_canNotAccessPrimaryContact", mProfileUserId);
+            // Managed user can use ENTERPRISE_CONTENT_FILTER_URI to access
+            // enterprise contacts
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testManagedProfileEnterprisePhoneLookup_canAccessEnterpriseContact",
+                    mProfileUserId);
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testManagedProfileEnterpriseEmailLookup_canAccessEnterpriseContact",
+                    mProfileUserId);
+            // Managed user cannot use ENTERPRISE_CONTENT_FILTER_URI to access
+            // primary contacts
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testManagedProfileEnterprisePhoneLookup_canNotAccessPrimaryContact",
+                    mProfileUserId);
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testManagedProfileEnterpriseEmailLookup_canNotAccessPrimaryContact",
+                    mProfileUserId);
+            // When there exist contacts with the same phone/email in primary &
+            // enterprise,
+            // managed user can use ENTERPRISE_CONTENT_FILTER_URI to access the
+            // enterprise contact.
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testManagedProfileEnterpriseEmailLookupDuplicated_canAccessEnterpriseContact",
+                    mProfileUserId);
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testManagedProfileEnterprisePhoneLookupDuplicated_canAccessEnterpriseContact",
+                    mProfileUserId);
+
+            // Check if phone lookup can access primary directories
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testPrimaryProfileEnterprisePhoneLookup_canAccessPrimaryDirectories",
+                    mParentUserId);
+
+            // Check if email lookup can access primary directories
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testPrimaryProfileEnterpriseEmailLookup_canAccessPrimaryDirectories",
+                    mParentUserId);
+
+            if (expected) {
+                // Primary user can use ENTERPRISE_CONTENT_FILTER_URI to access
+                // managed profile contacts
+                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                        "testPrimaryProfileEnterprisePhoneLookup_canAccessEnterpriseContact",
+                        mParentUserId);
+                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                        "testPrimaryProfileEnterpriseEmailLookup_canAccessEnterpriseContact",
+                        mParentUserId);
+
+                // Make sure SIP enterprise lookup works too.
+                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                        "testPrimaryProfileEnterpriseSipLookup_canAccessEnterpriseContact",
+                        mParentUserId);
+
+                // Check if phone lookup can access enterprise directories
+                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                        "testPrimaryProfileEnterprisePhoneLookup_canAccessManagedDirectories",
+                        mParentUserId);
+
+                // Check if email lookup can access enterprise directories
+                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                        "testPrimaryProfileEnterpriseEmailLookup_canAccessManagedDirectories",
+                        mParentUserId);
+            } else {
+                // Primary user cannot use ENTERPRISE_CONTENT_FILTER_URI to
+                // access managed contacts
+                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                        "testPrimaryProfileEnterprisePhoneLookup_canNotAccessEnterpriseContact",
+                        mParentUserId);
+                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                        "testPrimaryProfileEnterprisePhoneLookup_canNotAccessManagedDirectories",
+                        mParentUserId);
+
+                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                        "testPrimaryProfileEnterpriseEmailLookup_canNotAccessManagedDirectories",
+                        mParentUserId);
+                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                        "testPrimaryProfileEnterprisePhoneLookup_canNotAccessManagedDirectories",
+                        mParentUserId);
+            }
+        }
+
+        public void checkIfCanFilterSelfContacts() throws DeviceNotAvailableException {
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testPrimaryProfileEnterpriseCallableFilter_canAccessPrimaryDirectories",
+                    mParentUserId);
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testManagedProfileEnterpriseCallableFilter_canAccessManagedDirectories",
+                    mProfileUserId);
+
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testPrimaryProfileEnterpriseEmailFilter_canAccessPrimaryDirectories",
+                    mParentUserId);
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testEnterpriseProfileEnterpriseEmailFilter_canAccessManagedDirectories",
+                    mProfileUserId);
+
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testPrimaryProfileEnterpriseContactFilter_canAccessPrimaryDirectories",
+                    mParentUserId);
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testManagedProfileEnterpriseContactFilter_canAccessManagedDirectories",
+                    mProfileUserId);
+
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testPrimaryProfileEnterprisePhoneFilter_canAccessPrimaryDirectories",
+                    mParentUserId);
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testManagedProfileEnterprisePhoneFilter_canAccessManagedDirectories",
+                    mProfileUserId);
+        }
+
+        public void checkIfCanFilterEnterpriseContacts(boolean expected)
+                throws DeviceNotAvailableException {
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testFilterUriWhenDirectoryParamMissing", mParentUserId);
+            if (expected) {
+                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                        "testPrimaryProfileEnterpriseCallableFilter_canAccessManagedDirectories",
+                        mParentUserId);
+                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                        "testPrimaryProfileEnterpriseEmailFilter_canAccessManagedDirectories",
+                        mParentUserId);
+                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                        "testPrimaryProfileEnterpriseContactFilter_canAccessManagedDirectories",
+                        mParentUserId);
+                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                        "testPrimaryProfileEnterprisePhoneFilter_canAccessManagedDirectories",
+                        mParentUserId);
+            } else {
+                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                        "testPrimaryProfileEnterpriseCallableFilter_canNotAccessManagedDirectories",
+                        mParentUserId);
+                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                        "testPrimaryProfileEnterpriseEmailFilter_canNotAccessManagedDirectories",
+                        mParentUserId);
+                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                        "testPrimaryProfileEnterpriseContactFilter_canNotAccessManagedDirectories",
+                        mParentUserId);
+                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                        "testPrimaryProfileEnterprisePhoneFilter_canNotAccessManagedDirectories",
+                        mParentUserId);
+            }
+        }
+
+        public void checkIfNoEnterpriseDirectoryFound() throws DeviceNotAvailableException {
+            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testPrimaryProfileEnterpriseDirectories_canNotAccessManagedDirectories",
+                    mParentUserId);
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
new file mode 100644
index 0000000..83bcf5b
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
@@ -0,0 +1,512 @@
+/*
+ * 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.devicepolicy;
+
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+
+import android.platform.test.annotations.FlakyTest;
+import android.platform.test.annotations.LargeTest;
+import android.stats.devicepolicy.EventId;
+
+import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil;
+
+import java.util.Collections;
+
+public class ManagedProfileCrossProfileTest extends BaseManagedProfileTest {
+    private static final String NOTIFICATION_APK = "CtsNotificationSenderApp.apk";
+    private static final String WIDGET_PROVIDER_APK = "CtsWidgetProviderApp.apk";
+    private static final String WIDGET_PROVIDER_PKG = "com.android.cts.widgetprovider";
+    private static final String PARAM_PROFILE_ID = "profile-id";
+
+    @LargeTest
+    public void testCrossProfileIntentFilters() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        // Set up activities: ManagedProfileActivity will only be enabled in the managed profile and
+        // PrimaryUserActivity only in the primary one
+        disableActivityForUser("ManagedProfileActivity", mParentUserId);
+        disableActivityForUser("PrimaryUserActivity", mProfileUserId);
+
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
+                MANAGED_PROFILE_PKG + ".ManagedProfileTest", mProfileUserId);
+
+        assertMetricsLogged(getDevice(), () -> {
+            runDeviceTestsAsUser(
+                    MANAGED_PROFILE_PKG, MANAGED_PROFILE_PKG + ".ManagedProfileTest",
+                    "testAddCrossProfileIntentFilter_all", mProfileUserId);
+        }, new DevicePolicyEventWrapper.Builder(EventId.ADD_CROSS_PROFILE_INTENT_FILTER_VALUE)
+                .setAdminPackageName(MANAGED_PROFILE_PKG)
+                .setInt(1)
+                .setStrings("com.android.cts.managedprofile.ACTION_TEST_ALL_ACTIVITY")
+                .build());
+
+        // Set up filters from primary to managed profile
+        String command = "am start -W --user " + mProfileUserId + " " + MANAGED_PROFILE_PKG
+                + "/.PrimaryUserFilterSetterActivity";
+        LogUtil.CLog.d("Output for command " + command + ": "
+                + getDevice().executeShellCommand(command));
+        runDeviceTestsAsUser(
+                MANAGED_PROFILE_PKG, MANAGED_PROFILE_PKG + ".PrimaryUserTest", mParentUserId);
+        // TODO: Test with startActivity
+    }
+
+    @FlakyTest
+    public void testCrossProfileContent() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+
+        // Storage permission shouldn't be granted, we check if missing permissions are respected
+        // in ContentTest#testSecurity.
+        installAppAsUser(INTENT_SENDER_APK, false /* grantPermissions */, USER_ALL);
+        installAppAsUser(INTENT_RECEIVER_APK, USER_ALL);
+
+        // Test from parent to managed
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+                "testRemoveAllFilters", mProfileUserId);
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+                "testAddManagedCanAccessParentFilters", mProfileUserId);
+        runDeviceTestsAsUser(INTENT_SENDER_PKG, ".ContentTest", mParentUserId);
+
+        // Test from managed to parent
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+                "testRemoveAllFilters", mProfileUserId);
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+                "testAddParentCanAccessManagedFilters", mProfileUserId);
+        runDeviceTestsAsUser(INTENT_SENDER_PKG, ".ContentTest", mProfileUserId);
+
+    }
+
+    @FlakyTest
+    public void testCrossProfileNotificationListeners_EmptyWhitelist() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+
+        installAppAsUser(NOTIFICATION_APK, USER_ALL);
+
+        // Profile owner in the profile sets an empty whitelist
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
+                "testSetEmptyWhitelist", mProfileUserId,
+                Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
+        // Listener outside the profile can only see personal notifications.
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
+                "testCannotReceiveProfileNotifications", mParentUserId,
+                Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
+    }
+
+    public void testCrossProfileNotificationListeners_NullWhitelist() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+
+        installAppAsUser(NOTIFICATION_APK, USER_ALL);
+
+        // Profile owner in the profile sets a null whitelist
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
+                "testSetNullWhitelist", mProfileUserId,
+                Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
+        // Listener outside the profile can see profile and personal notifications
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
+                "testCanReceiveNotifications", mParentUserId,
+                Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
+    }
+
+    public void testCrossProfileNotificationListeners_InWhitelist() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+
+        installAppAsUser(NOTIFICATION_APK, USER_ALL);
+
+        // Profile owner in the profile adds listener to the whitelist
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
+                "testAddListenerToWhitelist", mProfileUserId,
+                Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
+        // Listener outside the profile can see profile and personal notifications
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
+                "testCanReceiveNotifications", mParentUserId,
+                Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
+    }
+
+    public void testCrossProfileNotificationListeners_setAndGet() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        installAppAsUser(NOTIFICATION_APK, USER_ALL);
+
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
+                "testSetAndGetPermittedCrossProfileNotificationListeners", mProfileUserId,
+                Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
+    }
+
+    @FlakyTest
+    public void testCrossProfileCopyPaste() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        installAppAsUser(INTENT_RECEIVER_APK, USER_ALL);
+        installAppAsUser(INTENT_SENDER_APK, USER_ALL);
+
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+                "testAllowCrossProfileCopyPaste", mProfileUserId);
+        // Test that managed can see what is copied in the parent.
+        testCrossProfileCopyPasteInternal(mProfileUserId, true);
+        // Test that the parent can see what is copied in managed.
+        testCrossProfileCopyPasteInternal(mParentUserId, true);
+
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+                "testDisallowCrossProfileCopyPaste", mProfileUserId);
+        // Test that managed can still see what is copied in the parent.
+        testCrossProfileCopyPasteInternal(mProfileUserId, true);
+        // Test that the parent cannot see what is copied in managed.
+        testCrossProfileCopyPasteInternal(mParentUserId, false);
+    }
+
+    private void testCrossProfileCopyPasteInternal(int userId, boolean shouldSucceed)
+            throws DeviceNotAvailableException {
+        final String direction = (userId == mParentUserId)
+                ? "testAddManagedCanAccessParentFilters"
+                : "testAddParentCanAccessManagedFilters";
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+                "testRemoveAllFilters", mProfileUserId);
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+                direction, mProfileUserId);
+        if (shouldSucceed) {
+            runDeviceTestsAsUser(INTENT_SENDER_PKG, ".CopyPasteTest",
+                    "testCanReadAcrossProfiles", userId);
+        } else {
+            runDeviceTestsAsUser(INTENT_SENDER_PKG, ".CopyPasteTest",
+                    "testCannotReadAcrossProfiles", userId);
+        }
+    }
+
+    @FlakyTest
+    public void testCrossProfileWidgets() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+
+        try {
+            installAppAsUser(WIDGET_PROVIDER_APK, USER_ALL);
+            getDevice().executeShellCommand("appwidget grantbind --user " + mParentUserId
+                    + " --package " + WIDGET_PROVIDER_PKG);
+            setIdleWhitelist(WIDGET_PROVIDER_PKG, true);
+            startWidgetHostService();
+
+            String commandOutput = changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG,
+                    "add-cross-profile-widget", mProfileUserId);
+            assertTrue("Command was expected to succeed " + commandOutput,
+                    commandOutput.contains("Status: ok"));
+
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileWidgetTest",
+                    "testCrossProfileWidgetProviderAdded", mProfileUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
+                    ".CrossProfileWidgetPrimaryUserTest",
+                    "testHasCrossProfileWidgetProvider_true", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
+                    ".CrossProfileWidgetPrimaryUserTest",
+                    "testHostReceivesWidgetUpdates_true", mParentUserId);
+
+            commandOutput = changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG,
+                    "remove-cross-profile-widget", mProfileUserId);
+            assertTrue("Command was expected to succeed " + commandOutput,
+                    commandOutput.contains("Status: ok"));
+
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileWidgetTest",
+                    "testCrossProfileWidgetProviderRemoved", mProfileUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
+                    ".CrossProfileWidgetPrimaryUserTest",
+                    "testHasCrossProfileWidgetProvider_false", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
+                    ".CrossProfileWidgetPrimaryUserTest",
+                    "testHostReceivesWidgetUpdates_false", mParentUserId);
+        } finally {
+            changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG, "remove-cross-profile-widget",
+                    mProfileUserId);
+            getDevice().uninstallPackage(WIDGET_PROVIDER_PKG);
+        }
+    }
+
+    @FlakyTest
+    public void testCrossProfileWidgetsLogged() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+
+        try {
+            installAppAsUser(WIDGET_PROVIDER_APK, USER_ALL);
+            getDevice().executeShellCommand("appwidget grantbind --user " + mParentUserId
+                    + " --package " + WIDGET_PROVIDER_PKG);
+            setIdleWhitelist(WIDGET_PROVIDER_PKG, true);
+            startWidgetHostService();
+
+            assertMetricsLogged(getDevice(), () -> {
+                changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG,
+                        "add-cross-profile-widget", mProfileUserId);
+                changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG,
+                        "remove-cross-profile-widget", mProfileUserId);
+            }, new DevicePolicyEventWrapper
+                        .Builder(EventId.ADD_CROSS_PROFILE_WIDGET_PROVIDER_VALUE)
+                        .setAdminPackageName(MANAGED_PROFILE_PKG)
+                        .build(),
+                new DevicePolicyEventWrapper
+                        .Builder(EventId.REMOVE_CROSS_PROFILE_WIDGET_PROVIDER_VALUE)
+                        .setAdminPackageName(MANAGED_PROFILE_PKG)
+                        .build());
+        } finally {
+            changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG, "remove-cross-profile-widget",
+                    mProfileUserId);
+            getDevice().uninstallPackage(WIDGET_PROVIDER_PKG);
+        }
+    }
+
+    public void testCrossProfileCalendarPackage() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        assertMetricsLogged(getDevice(), () -> {
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testCrossProfileCalendarPackage", mProfileUserId);
+        }, new DevicePolicyEventWrapper.Builder(EventId.SET_CROSS_PROFILE_CALENDAR_PACKAGES_VALUE)
+                    .setAdminPackageName(MANAGED_PROFILE_PKG)
+                    .setStrings(MANAGED_PROFILE_PKG)
+                    .build());
+    }
+
+    @FlakyTest
+    public void testCrossProfileCalendar() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        runCrossProfileCalendarTestsWhenWhitelistedAndEnabled();
+        runCrossProfileCalendarTestsWhenAllPackagesWhitelisted();
+        runCrossProfileCalendarTestsWhenDisabled();
+        runCrossProfileCalendarTestsWhenNotWhitelisted();
+    }
+
+    @FlakyTest
+    public void testDisallowSharingIntoPersonalFromProfile() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        // Set up activities: PrimaryUserActivity will only be enabled in the personal user
+        // This activity is used to find out the ground truth about the system's cross profile
+        // intent forwarding activity.
+        disableActivityForUser("PrimaryUserActivity", mProfileUserId);
+
+        // Tests from the profile side
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
+                ".DisallowSharingIntoProfileTest", "testSharingFromProfile", mProfileUserId);
+    }
+
+    public void testDisallowSharingIntoProfileFromPersonal() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        // Set up activities: ManagedProfileActivity will only be enabled in the managed profile
+        // This activity is used to find out the ground truth about the system's cross profile
+        // intent forwarding activity.
+        disableActivityForUser("ManagedProfileActivity", mParentUserId);
+
+        // Tests from the personal side, which is mostly driven from host side.
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".DisallowSharingIntoProfileTest",
+                "testSetUp", mProfileUserId);
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".DisallowSharingIntoProfileTest",
+                "testDisableSharingIntoProfile", mProfileUserId);
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".DisallowSharingIntoProfileTest",
+                "testSharingFromPersonalFails", mParentUserId);
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".DisallowSharingIntoProfileTest",
+                "testEnableSharingIntoProfile", mProfileUserId);
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".DisallowSharingIntoProfileTest",
+                "testSharingFromPersonalSucceeds", mParentUserId);
+    }
+
+    private void runCrossProfileCalendarTestsWhenWhitelistedAndEnabled() throws Exception {
+        try {
+            // Setup. Add the test package into cross-profile calendar whitelist, enable
+            // cross-profile calendar in settings, and insert test data into calendar provider.
+            // All setups should be done in managed profile.
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testWhitelistManagedProfilePackage", mProfileUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testAddTestCalendarDataForWorkProfile", mProfileUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testEnableCrossProfileCalendarSettings", mProfileUserId);
+
+            // Testing.
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testPrimaryProfile_getCorrectWorkCalendarsWhenEnabled", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testPrimaryProfile_getCorrectWorkEventsWhenEnabled", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testPrimaryProfile_getCorrectWorkInstancesWhenEnabled", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testPrimaryProfile_getCorrectWorkInstancesByDayWhenEnabled", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testPrimaryProfile_canAccessWorkInstancesSearch1", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testPrimaryProfile_canAccessWorkInstancesSearch2", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testPrimaryProfile_canAccessWorkInstancesSearchByDay", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testPrimaryProfile_getExceptionWhenQueryNonWhitelistedColumns", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testViewEventCrossProfile_intentReceivedWhenWhitelisted", mParentUserId);
+        } finally {
+            // Cleanup.
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testCleanupWhitelist", mProfileUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testCleanupTestCalendarDataForWorkProfile", mProfileUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testDisableCrossProfileCalendarSettings", mProfileUserId);
+        }
+    }
+
+    private void runCrossProfileCalendarTestsWhenAllPackagesWhitelisted() throws Exception {
+        try {
+            // Setup. Allow all packages to access cross-profile calendar APIs by setting
+            // the whitelist to null, enable cross-profile calendar in settings,
+            // and insert test data into calendar provider.
+            // All setups should be done in managed profile.
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testWhitelistAllPackages", mProfileUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testAddTestCalendarDataForWorkProfile", mProfileUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testEnableCrossProfileCalendarSettings", mProfileUserId);
+
+            // Testing.
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testPrimaryProfile_getCorrectWorkCalendarsWhenEnabled", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testPrimaryProfile_getCorrectWorkEventsWhenEnabled", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testPrimaryProfile_getCorrectWorkInstancesWhenEnabled", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testPrimaryProfile_getCorrectWorkInstancesByDayWhenEnabled", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testPrimaryProfile_canAccessWorkInstancesSearch1", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testPrimaryProfile_canAccessWorkInstancesSearch2", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testPrimaryProfile_canAccessWorkInstancesSearchByDay", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testPrimaryProfile_getExceptionWhenQueryNonWhitelistedColumns", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testViewEventCrossProfile_intentReceivedWhenWhitelisted", mParentUserId);
+        } finally {
+            // Cleanup.
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testCleanupWhitelist", mProfileUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testCleanupTestCalendarDataForWorkProfile", mProfileUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testDisableCrossProfileCalendarSettings", mProfileUserId);
+        }
+    }
+
+    private void runCrossProfileCalendarTestsWhenDisabled() throws Exception {
+        try {
+            // Setup. Add the test package into cross-profile calendar whitelist,
+            // and insert test data into calendar provider. But disable cross-profile calendar
+            // in settings. Thus cross-profile calendar Uris should not be accessible.
+            // All setups should be done in managed profile.
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testWhitelistManagedProfilePackage", mProfileUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testAddTestCalendarDataForWorkProfile", mProfileUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testDisableCrossProfileCalendarSettings", mProfileUserId);
+
+            // Testing.
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testPrimaryProfile_cannotAccessWorkCalendarsWhenDisabled", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testPrimaryProfile_cannotAccessWorkEventsWhenDisabled", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testPrimaryProfile_cannotAccessWorkInstancesWhenDisabled", mParentUserId);
+        } finally {
+            // Cleanup.
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testCleanupWhitelist", mProfileUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testCleanupTestCalendarDataForWorkProfile", mProfileUserId);
+        }
+    }
+
+    private void runCrossProfileCalendarTestsWhenNotWhitelisted() throws Exception {
+        try {
+            // Setup. Enable cross-profile calendar in settings and insert test data into calendar
+            // provider. But make sure that the test package is not whitelisted for cross-profile
+            // calendar. Thus cross-profile calendar Uris should not be accessible.
+            // All setups should be done in managed profile.
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testAddTestCalendarDataForWorkProfile", mProfileUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testEnableCrossProfileCalendarSettings", mProfileUserId);
+
+            // Testing.
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testPrimaryProfile_cannotAccessWorkCalendarsWhenDisabled", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testPrimaryProfile_cannotAccessWorkEventsWhenDisabled", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testPrimaryProfile_cannotAccessWorkInstancesWhenDisabled", mParentUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testViewEventCrossProfile_intentFailedWhenNotWhitelisted", mParentUserId);
+        } finally {
+            // Cleanup.
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testCleanupTestCalendarDataForWorkProfile", mProfileUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+                    "testDisableCrossProfileCalendarSettings", mProfileUserId);
+        }
+    }
+
+    private void setIdleWhitelist(String packageName, boolean enabled)
+            throws DeviceNotAvailableException {
+        String command = "cmd deviceidle whitelist " + (enabled ? "+" : "-") + packageName;
+        LogUtil.CLog.d("Output for command " + command + ": "
+                + getDevice().executeShellCommand(command));
+    }
+
+    private String changeCrossProfileWidgetForUser(String packageName, String command, int userId)
+            throws DeviceNotAvailableException {
+        String adbCommand = "am start -W --user " + userId
+                + " -c android.intent.category.DEFAULT "
+                + " --es extra-command " + command
+                + " --es extra-package-name " + packageName
+                + " " + MANAGED_PROFILE_PKG + "/.SetPolicyActivity";
+        String commandOutput = getDevice().executeShellCommand(adbCommand);
+        LogUtil.CLog.d("Output for command " + adbCommand + ": " + commandOutput);
+        return commandOutput;
+    }
+
+    private void startWidgetHostService() throws Exception {
+        String command = "am startservice --user " + mParentUserId
+                + " -a " + WIDGET_PROVIDER_PKG + ".REGISTER_CALLBACK "
+                + "--ei user-extra " + getUserSerialNumber(mProfileUserId)
+                + " " + WIDGET_PROVIDER_PKG + "/.SimpleAppWidgetHostService";
+        LogUtil.CLog.d("Output for command " + command + ": "
+                + getDevice().executeShellCommand(command));
+    }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfilePasswordTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfilePasswordTest.java
new file mode 100644
index 0000000..44c1a72
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfilePasswordTest.java
@@ -0,0 +1,247 @@
+/*
+ * 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.devicepolicy;
+
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+
+import android.platform.test.annotations.FlakyTest;
+import android.platform.test.annotations.LargeTest;
+import android.stats.devicepolicy.EventId;
+
+import com.android.cts.devicepolicy.annotations.LockSettingsTest;
+import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
+import com.android.tradefed.device.DeviceNotAvailableException;
+
+import java.util.concurrent.TimeUnit;
+
+public class ManagedProfilePasswordTest extends BaseManagedProfileTest {
+    private static final String USER_STATE_LOCKED = "RUNNING_LOCKED";
+    private static final long TIMEOUT_USER_LOCKED_MILLIS = TimeUnit.MINUTES.toMillis(2);
+    // Password needs to be in sync with ResetPasswordWithTokenTest.PASSWORD1
+    private static final String RESET_PASSWORD_TEST_DEFAULT_PASSWORD = "123456";
+
+    @FlakyTest
+    public void testLockNowWithKeyEviction() throws Exception {
+        if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
+            return;
+        }
+        changeUserCredential("1234", null, mProfileUserId);
+        lockProfile();
+    }
+
+    public void testPasswordMinimumRestrictions() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".PasswordMinimumRestrictionsTest",
+                mProfileUserId);
+    }
+
+    @FlakyTest
+    public void testResetPasswordWithTokenBeforeUnlock() throws Exception {
+        if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
+            return;
+        }
+
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
+                "testSetupWorkProfile", mProfileUserId);
+        lockProfile();
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
+                "testResetPasswordBeforeUnlock", mProfileUserId);
+        // Password needs to be in sync with ResetPasswordWithTokenTest.PASSWORD1
+        verifyUserCredential(RESET_PASSWORD_TEST_DEFAULT_PASSWORD, mProfileUserId);
+    }
+
+    @FlakyTest
+    public void testClearPasswordWithTokenBeforeUnlock() throws Exception {
+        if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
+            return;
+        }
+
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
+                "testSetupWorkProfile", mProfileUserId);
+        lockProfile();
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
+                "testClearPasswordBeforeUnlock", mProfileUserId);
+        // Make sure profile has no password
+        verifyUserCredential("", mProfileUserId);
+    }
+
+    /**
+     * Test password reset token is still functional after the primary user clears and
+     * re-adds back its device lock. This is to detect a regression where the work profile
+     * undergoes an untrusted credential reset (causing synthetic password to change, invalidating
+     * existing password reset token) if it has unified work challenge and the primary user clears
+     * the device lock.
+     */
+    @FlakyTest
+    public void testResetPasswordTokenUsableAfterClearingLock() throws Exception {
+        if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
+            return;
+        }
+        final String devicePassword = "1234";
+
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
+                "testSetResetPasswordToken", mProfileUserId);
+        try {
+            changeUserCredential(devicePassword, null, mParentUserId);
+            changeUserCredential(null, devicePassword, mParentUserId);
+            changeUserCredential(devicePassword, null, mParentUserId);
+            lockProfile();
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
+                    "testResetPasswordBeforeUnlock", mProfileUserId);
+            verifyUserCredential(RESET_PASSWORD_TEST_DEFAULT_PASSWORD, mProfileUserId);
+        } finally {
+            changeUserCredential(null, devicePassword, mParentUserId);
+            // Cycle the device screen to flush stale password information from keyguard,
+            // otherwise it will still ask for the non-existent password.
+            // return screen to be on for cts test runs
+            executeShellCommand("input keyevent KEYCODE_WAKEUP");
+            executeShellCommand("input keyevent KEYCODE_SLEEP");
+            executeShellCommand("input keyevent KEYCODE_WAKEUP");
+        }
+    }
+
+    @LockSettingsTest
+    public void testIsUsingUnifiedPassword() throws Exception {
+        if (!mHasFeature || !mHasSecureLockScreen) {
+            return;
+        }
+
+        // Freshly created profile has no separate challenge.
+        verifyUnifiedPassword(true);
+
+        // Set separate challenge and verify that the API reports it correctly.
+        changeUserCredential("1234" /* newCredential */, null /* oldCredential */, mProfileUserId);
+        verifyUnifiedPassword(false);
+    }
+
+    @FlakyTest
+    @LargeTest
+    @LockSettingsTest
+    public void testUnlockWorkProfile_deviceWidePassword() throws Exception {
+        if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
+            return;
+        }
+        String password = "0000";
+        try {
+            // Add a device password after the work profile has been created.
+            changeUserCredential(password, /* oldCredential= */ null, mPrimaryUserId);
+            // Lock the profile with key eviction.
+            lockProfile();
+            // Turn on work profile, by unlocking the profile with the device password.
+            verifyUserCredential(password, mPrimaryUserId);
+
+            // Verify profile user is running unlocked by running a sanity test on the work profile.
+            installAppAsUser(SIMPLE_APP_APK, mProfileUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".SanityTest", mProfileUserId);
+        } finally {
+            // Clean up
+            changeUserCredential(/* newCredential= */ null, password, mPrimaryUserId);
+        }
+    }
+
+    @FlakyTest
+    @LargeTest
+    @LockSettingsTest
+    public void testRebootDevice_unifiedPassword() throws Exception {
+        if (!mHasFeature || !mHasSecureLockScreen) {
+            return;
+        }
+        // Waiting before rebooting prevents flakiness.
+        waitForBroadcastIdle();
+        String password = "0000";
+        changeUserCredential(password, /* oldCredential= */ null, mPrimaryUserId);
+        try {
+            rebootAndWaitUntilReady();
+            verifyUserCredential(password, mPrimaryUserId);
+            installAppAsUser(SIMPLE_APP_APK, mProfileUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".SanityTest", mProfileUserId);
+        } finally {
+            changeUserCredential(/* newCredential= */ null, password, mPrimaryUserId);
+            // Work-around for http://b/113866275 - password prompt being erroneously shown at the
+            // end.
+            pressPowerButton();
+        }
+    }
+
+    @LargeTest
+    @LockSettingsTest
+    public void testRebootDevice_separatePasswords() throws Exception {
+        if (!mHasFeature || !mHasSecureLockScreen) {
+            return;
+        }
+        // Waiting before rebooting prevents flakiness.
+        waitForBroadcastIdle();
+        String profilePassword = "profile";
+        String primaryPassword = "primary";
+        int managedProfileUserId = getFirstManagedProfileUserId();
+        changeUserCredential(
+                profilePassword, /* oldCredential= */ null, managedProfileUserId);
+        changeUserCredential(primaryPassword, /* oldCredential= */ null, mPrimaryUserId);
+        try {
+            rebootAndWaitUntilReady();
+            verifyUserCredential(profilePassword, managedProfileUserId);
+            verifyUserCredential(primaryPassword, mPrimaryUserId);
+            installAppAsUser(SIMPLE_APP_APK, mProfileUserId);
+            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".SanityTest", mProfileUserId);
+        } finally {
+            changeUserCredential(
+                    /* newCredential= */ null, profilePassword, managedProfileUserId);
+            changeUserCredential(/* newCredential= */ null, primaryPassword, mPrimaryUserId);
+            // Work-around for http://b/113866275 - password prompt being erroneously shown at the
+            // end.
+            pressPowerButton();
+        }
+    }
+
+    public void testCreateSeparateChallengeChangedLogged() throws Exception {
+        if (!mHasFeature || !mHasSecureLockScreen) {
+            return;
+        }
+        assertMetricsLogged(getDevice(), () -> {
+            changeUserCredential(
+                    "1234" /* newCredential */, null /* oldCredential */, mProfileUserId);
+        }, new DevicePolicyEventWrapper.Builder(EventId.SEPARATE_PROFILE_CHALLENGE_CHANGED_VALUE)
+                .setBoolean(true)
+                .build());
+    }
+
+    private void verifyUnifiedPassword(boolean unified) throws DeviceNotAvailableException {
+        final String testMethod =
+                unified ? "testUsingUnifiedPassword" : "testNotUsingUnifiedPassword";
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".IsUsingUnifiedPasswordTest",
+                testMethod, mProfileUserId);
+    }
+
+    private void lockProfile() throws Exception {
+        final String cmd = "am broadcast --receiver-foreground --user " + mProfileUserId
+                + " -a com.android.cts.managedprofile.LOCK_PROFILE"
+                + " com.android.cts.managedprofile/.LockProfileReceiver";
+        getDevice().executeShellCommand(cmd);
+        waitUntilProfileLocked();
+    }
+
+    private void waitUntilProfileLocked() throws Exception {
+        final String cmd = String.format("am get-started-user-state %d", mProfileUserId);
+        tryWaitForSuccess(
+                () -> getDevice().executeShellCommand(cmd).startsWith(USER_STATE_LOCKED),
+                "The managed profile has not been locked after calling "
+                        + "lockNow(FLAG_SECURE_USER_DATA)",
+                TIMEOUT_USER_LOCKED_MILLIS);
+    }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningSingleAdminTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningSingleAdminTest.java
index b71c4c87..eafb72b 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningSingleAdminTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningSingleAdminTest.java
@@ -15,6 +15,8 @@
  */
 package com.android.cts.devicepolicy;
 
+import android.platform.test.annotations.FlakyTest;
+
 /**
  * This class tests the provisioning flow with an APK that declares a single receiver with
  * BIND_DEVICE_ADMIN permissions, which was a requirement for the app sending the
@@ -52,6 +54,7 @@
         super.tearDown();
     }
 
+    @FlakyTest
     public void testEXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME() throws Exception {
         if (!mHasFeature) {
             return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileRingtoneTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileRingtoneTest.java
new file mode 100644
index 0000000..03af77a
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileRingtoneTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.devicepolicy;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil.CLog;
+
+public class ManagedProfileRingtoneTest extends BaseManagedProfileTest {
+    public void testRingtoneSync() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        givePackageWriteSettingsPermission(mProfileUserId);
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".RingtoneSyncTest",
+                "testRingtoneSync", mProfileUserId);
+    }
+
+    // Test if setting RINGTONE disables sync
+    public void testRingtoneSyncAutoDisableRingtone() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        givePackageWriteSettingsPermission(mProfileUserId);
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".RingtoneSyncTest",
+                "testRingtoneDisableSync", mProfileUserId);
+    }
+
+    // Test if setting NOTIFICATION disables sync
+    public void testRingtoneSyncAutoDisableNotification() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        givePackageWriteSettingsPermission(mProfileUserId);
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".RingtoneSyncTest",
+                "testNotificationDisableSync", mProfileUserId);
+    }
+
+    // Test if setting ALARM disables sync
+    public void testRingtoneSyncAutoDisableAlarm() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        givePackageWriteSettingsPermission(mProfileUserId);
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".RingtoneSyncTest",
+                "testAlarmDisableSync", mProfileUserId);
+    }
+
+    private void givePackageWriteSettingsPermission(int userId) throws DeviceNotAvailableException {
+        // Allow app to write to settings (for RingtoneManager.setActualDefaultUri to work)
+        String command = "appops set --user " + userId + " " + MANAGED_PROFILE_PKG
+                + " android:write_settings allow";
+        CLog.d("Output for command " + command + ": " + getDevice().executeShellCommand(command));
+    }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 3f7bd9d..1dcaa62 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -13,12 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.cts.devicepolicy;
 
 import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
 
-
+import android.platform.test.annotations.LargeTest;
 import android.stats.devicepolicy.EventId;
 
 import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
@@ -28,130 +27,21 @@
 
 import junit.framework.AssertionFailedError;
 
-import java.util.Collections;
-import java.util.concurrent.Callable;
-import java.util.concurrent.TimeUnit;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
 /**
  * Set of tests for Managed Profile use cases.
  */
-public class ManagedProfileTest extends BaseDevicePolicyTest {
+public class ManagedProfileTest extends BaseManagedProfileTest {
 
-    private static final String MANAGED_PROFILE_PKG = "com.android.cts.managedprofile";
-    private static final String MANAGED_PROFILE_APK = "CtsManagedProfileApp.apk";
-
+    private static final String FEATURE_TELEPHONY = "android.hardware.telephony";
+    private static final String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
+    private static final String FEATURE_CAMERA = "android.hardware.camera";
+    private static final String FEATURE_WIFI = "android.hardware.wifi";
+    private static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
     private static final String DEVICE_OWNER_PKG = "com.android.cts.deviceowner";
     private static final String DEVICE_OWNER_APK = "CtsDeviceOwnerApp.apk";
     private static final String DEVICE_OWNER_ADMIN =
             DEVICE_OWNER_PKG + ".BaseDeviceOwnerTest$BasicAdminReceiver";
 
-    private static final String INTENT_SENDER_PKG = "com.android.cts.intent.sender";
-    private static final String INTENT_SENDER_APK = "CtsIntentSenderApp.apk";
-
-    private static final String INTENT_RECEIVER_PKG = "com.android.cts.intent.receiver";
-    private static final String INTENT_RECEIVER_APK = "CtsIntentReceiverApp.apk";
-
-    private static final String WIDGET_PROVIDER_APK = "CtsWidgetProviderApp.apk";
-    private static final String WIDGET_PROVIDER_PKG = "com.android.cts.widgetprovider";
-
-    private static final String DIRECTORY_PROVIDER_APK = "CtsContactDirectoryProvider.apk";
-    private static final String DIRECTORY_PROVIDER_PKG
-            = "com.android.cts.contactdirectoryprovider";
-    private static final String PRIMARY_DIRECTORY_PREFIX = "Primary";
-    private static final String MANAGED_DIRECTORY_PREFIX = "Managed";
-    private static final String DIRECTORY_PRIVOIDER_URI
-            = "content://com.android.cts.contact.directory.provider/";
-    private static final String SET_CUSTOM_DIRECTORY_PREFIX_METHOD = "set_prefix";
-
-    private static final String NOTIFICATION_APK = "CtsNotificationSenderApp.apk";
-    private static final String NOTIFICATION_PKG =
-            "com.android.cts.managedprofiletests.notificationsender";
-
-    private static final String ADMIN_RECEIVER_TEST_CLASS =
-            MANAGED_PROFILE_PKG + ".BaseManagedProfileTest$BasicAdminReceiver";
-
-    private static final String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
-    private static final String FEATURE_CAMERA = "android.hardware.camera";
-    private static final String FEATURE_WIFI = "android.hardware.wifi";
-    private static final String FEATURE_TELEPHONY = "android.hardware.telephony";
-    private static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
-
-    private static final String SIMPLE_APP_APK = "CtsSimpleApp.apk";
-
-    private static final long TIMEOUT_USER_LOCKED_MILLIS = TimeUnit.MINUTES.toMillis(2);
-
-    private static final String PARAM_PROFILE_ID = "profile-id";
-
-    // Password needs to be in sync with ResetPasswordWithTokenTest.PASSWORD1
-    private static final String RESET_PASSWORD_TEST_DEFAULT_PASSWORD = "123456";
-
-    private static final String PROFILE_CREDENTIAL = "1234";
-    // This should be sufficiently larger than ProfileTimeoutTestHelper.TIMEOUT_MS
-    private static final int PROFILE_TIMEOUT_DELAY_MS = 40_000;
-
-    //The maximum time to wait for user to be unlocked.
-    private static final long USER_UNLOCK_TIMEOUT_NANO = 30_000_000_000L;
-
-    private static final String USER_UNLOCKED_SHELL_OUTPUT = "RUNNING_UNLOCKED";
-
-    private int mParentUserId;
-
-    // ID of the profile we'll create. This will always be a profile of the parent.
-    private int mProfileUserId;
-
-    private boolean mHasNfcFeature;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        // We need multi user to be supported in order to create a profile of the user owner.
-        mHasFeature = mHasFeature && hasDeviceFeature("android.software.managed_users");
-        mHasNfcFeature = hasDeviceFeature("android.hardware.nfc")
-                && hasDeviceFeature("android.sofware.nfc.beam");
-
-        if (mHasFeature) {
-            removeTestUsers();
-            mParentUserId = mPrimaryUserId;
-            mProfileUserId = createManagedProfile(mParentUserId);
-            startUser(mProfileUserId);
-
-            // Install the APK on both primary and profile user in one single transaction.
-            // If they were installed separately, the second installation would become an app
-            // update and result in the current running test process being killed.
-            installAppAsUser(MANAGED_PROFILE_APK, USER_ALL);
-            setProfileOwnerOrFail(MANAGED_PROFILE_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
-                    mProfileUserId);
-            waitForUserUnlock();
-        }
-    }
-
-    private void waitForUserUnlock() throws Exception {
-        final String command = String.format("am get-started-user-state %d", mProfileUserId);
-        final long deadline = System.nanoTime() + USER_UNLOCK_TIMEOUT_NANO;
-        while (System.nanoTime() <= deadline) {
-            if (getDevice().executeShellCommand(command).startsWith(USER_UNLOCKED_SHELL_OUTPUT)) {
-                return;
-            }
-            Thread.sleep(100);
-        }
-        fail("Profile user is not unlocked.");
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        if (mHasFeature) {
-            removeUser(mProfileUserId);
-            getDevice().uninstallPackage(MANAGED_PROFILE_PKG);
-            getDevice().uninstallPackage(INTENT_SENDER_PKG);
-            getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
-            getDevice().uninstallPackage(NOTIFICATION_PKG);
-        }
-        super.tearDown();
-    }
-
     public void testManagedProfilesSupportedWithLockScreenOnly() throws Exception {
         if (mHasFeature) {
             // Managed profiles should be only supported if the device supports the secure lock
@@ -169,210 +59,6 @@
                 mProfileUserId);
     }
 
-    public void testWipeDataWithReason() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        assertTrue(listUsers().contains(mProfileUserId));
-        sendWipeProfileBroadcast("com.android.cts.managedprofile.WIPE_DATA_WITH_REASON");
-        // Note: the managed profile is removed by this test, which will make removeUserCommand in
-        // tearDown() to complain, but that should be OK since its result is not asserted.
-        assertUserGetsRemoved(mProfileUserId);
-        // testWipeDataWithReason() removes the managed profile,
-        // so it needs to separated from other tests.
-        // Check and clear the notification is presented after work profile got removed, so profile
-        // user no longer exists, verification should be run in primary user.
-        runDeviceTestsAsUser(
-                MANAGED_PROFILE_PKG,
-                ".WipeDataNotificationTest",
-                "testWipeDataWithReasonVerification",
-                mParentUserId);
-    }
-
-    public void testWipeDataLogged() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        assertTrue(listUsers().contains(mProfileUserId));
-        assertMetricsLogged(getDevice(), () -> {
-            sendWipeProfileBroadcast("com.android.cts.managedprofile.WIPE_DATA_WITH_REASON");
-        }, new DevicePolicyEventWrapper.Builder(EventId.WIPE_DATA_WITH_REASON_VALUE)
-                .setAdminPackageName(MANAGED_PROFILE_PKG)
-                .setInt(0)
-                .build());
-        // Check and clear the notification is presented after work profile got removed, so profile
-        // user no longer exists, verification should be run in primary user.
-        runDeviceTestsAsUser(
-                MANAGED_PROFILE_PKG,
-                ".WipeDataNotificationTest",
-                "testWipeDataWithReasonVerification",
-                mParentUserId);
-    }
-
-    public void testWipeDataWithoutReason() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        assertTrue(listUsers().contains(mProfileUserId));
-        sendWipeProfileBroadcast("com.android.cts.managedprofile.WIPE_DATA_WITHOUT_REASON");
-        // Note: the managed profile is removed by this test, which will make removeUserCommand in
-        // tearDown() to complain, but that should be OK since its result is not asserted.
-        assertUserGetsRemoved(mProfileUserId);
-        // testWipeDataWithoutReason() removes the managed profile,
-        // so it needs to separated from other tests.
-        // Check the notification is not presented after work profile got removed, so profile user
-        // no longer exists, verification should be run in primary user.
-        runDeviceTestsAsUser(
-                MANAGED_PROFILE_PKG,
-                ".WipeDataNotificationTest",
-                "testWipeDataWithoutReasonVerification",
-                mParentUserId);
-    }
-
-    /**
-     * wipeData() test removes the managed profile, so it needs to be separated from other tests.
-     */
-    public void testWipeData() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        assertTrue(listUsers().contains(mProfileUserId));
-        sendWipeProfileBroadcast("com.android.cts.managedprofile.WIPE_DATA");
-        // Note: the managed profile is removed by this test, which will make removeUserCommand in
-        // tearDown() to complain, but that should be OK since its result is not asserted.
-        assertUserGetsRemoved(mProfileUserId);
-    }
-
-    private void sendWipeProfileBroadcast(String action) throws Exception {
-        final String cmd = "am broadcast --receiver-foreground --user " + mProfileUserId
-                + " -a " + action
-                + " com.android.cts.managedprofile/.WipeDataReceiver";
-        getDevice().executeShellCommand(cmd);
-    }
-
-    public void testLockNowWithKeyEviction() throws Exception {
-        if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
-            return;
-        }
-        changeUserCredential("1234", null, mProfileUserId);
-        lockProfile();
-    }
-
-    private void lockProfile() throws Exception {
-        final String cmd = "am broadcast --receiver-foreground --user " + mProfileUserId
-                + " -a com.android.cts.managedprofile.LOCK_PROFILE"
-                + " com.android.cts.managedprofile/.LockProfileReceiver";
-        getDevice().executeShellCommand(cmd);
-        waitUntilProfileLocked();
-    }
-
-    private void waitUntilProfileLocked() throws Exception {
-        final String cmd = "dumpsys activity | grep 'User #" + mProfileUserId + ": state='";
-        final Pattern p = Pattern.compile("state=([\\p{Upper}_]+)$");
-        SuccessCondition userLocked = () -> {
-            final String activityDump = getDevice().executeShellCommand(cmd);
-            final Matcher m = p.matcher(activityDump);
-            return m.find() && m.group(1).equals("RUNNING_LOCKED");
-        };
-        tryWaitForSuccess(
-                userLocked,
-                "The managed profile has not been locked after calling "
-                        + "lockNow(FLAG_SECURE_USER_DATA)",
-                TIMEOUT_USER_LOCKED_MILLIS);
-    }
-
-    /** Profile should get locked if it is not in foreground no matter what. */
-    public void testWorkProfileTimeoutBackground() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        setUpWorkProfileTimeout();
-
-        startDummyActivity(mPrimaryUserId, true);
-        simulateUserInteraction(PROFILE_TIMEOUT_DELAY_MS);
-
-        verifyOnlyProfileLocked(true);
-    }
-
-    /** Profile should get locked if it is in foreground but with no user activity. */
-    public void testWorkProfileTimeoutIdleActivity() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        setUpWorkProfileTimeout();
-
-        startDummyActivity(mProfileUserId, false);
-        Thread.sleep(PROFILE_TIMEOUT_DELAY_MS);
-
-        verifyOnlyProfileLocked(true);
-    }
-
-    /** User activity in profile should prevent it from locking. */
-    public void testWorkProfileTimeoutUserActivity() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        setUpWorkProfileTimeout();
-
-        startDummyActivity(mProfileUserId, false);
-        simulateUserInteraction(PROFILE_TIMEOUT_DELAY_MS);
-
-        verifyOnlyProfileLocked(false);
-    }
-
-    /** Keep screen on window flag in the profile should prevent it from locking. */
-    public void testWorkProfileTimeoutKeepScreenOnWindow() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        setUpWorkProfileTimeout();
-
-        startDummyActivity(mProfileUserId, true);
-        Thread.sleep(PROFILE_TIMEOUT_DELAY_MS);
-
-        verifyOnlyProfileLocked(false);
-    }
-
-    private void setUpWorkProfileTimeout() throws DeviceNotAvailableException {
-        // Set separate challenge.
-        changeUserCredential(PROFILE_CREDENTIAL, null, mProfileUserId);
-
-        // Make sure the profile is not prematurely locked.
-        verifyUserCredential(PROFILE_CREDENTIAL, mProfileUserId);
-        verifyOnlyProfileLocked(false);
-        // Set profile timeout to 5 seconds.
-        runProfileTimeoutTest("testSetWorkProfileTimeout", mProfileUserId);
-    }
-
-    private void verifyOnlyProfileLocked(boolean locked) throws DeviceNotAvailableException {
-        final String expectedResultTest = locked ? "testDeviceLocked" : "testDeviceNotLocked";
-        runProfileTimeoutTest(expectedResultTest, mProfileUserId);
-        // Primary profile shouldn't be locked.
-        runProfileTimeoutTest("testDeviceNotLocked", mPrimaryUserId);
-    }
-
-    private void simulateUserInteraction(int timeMs) throws Exception {
-        final long endTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeMs);
-        final UserActivityEmulator helper = new UserActivityEmulator(getDevice());
-        while (System.nanoTime() < endTime) {
-            helper.tapScreenCenter();
-            // Just in case to prevent busy loop.
-            Thread.sleep(100);
-        }
-    }
-
-    private void runProfileTimeoutTest(String method, int userId)
-            throws DeviceNotAvailableException {
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, MANAGED_PROFILE_PKG + ".ProfileTimeoutTestHelper",
-                method, userId);
-    }
-
-    private void startDummyActivity(int profileUserId, boolean keepScreenOn) throws Exception {
-        getDevice().executeShellCommand(String.format(
-                "am start-activity -W --user %d --ez keep_screen_on %s %s/.TimeoutActivity",
-                profileUserId, keepScreenOn, MANAGED_PROFILE_PKG));
-    }
-
     public void testMaxOneManagedProfile() throws Exception {
         int newUserId = -1;
         try {
@@ -414,74 +100,7 @@
                 MANAGED_PROFILE_PKG, ".WifiTest", "testCannotGetWifiMacAddress", mProfileUserId);
     }
 
-    public void testCrossProfileIntentFilters() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        // Set up activities: ManagedProfileActivity will only be enabled in the managed profile and
-        // PrimaryUserActivity only in the primary one
-        disableActivityForUser("ManagedProfileActivity", mParentUserId);
-        disableActivityForUser("PrimaryUserActivity", mProfileUserId);
-
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
-                MANAGED_PROFILE_PKG + ".ManagedProfileTest", mProfileUserId);
-
-        assertMetricsLogged(getDevice(), () -> {
-            runDeviceTestsAsUser(
-                    MANAGED_PROFILE_PKG, MANAGED_PROFILE_PKG + ".ManagedProfileTest",
-                    "testAddCrossProfileIntentFilter_all", mProfileUserId);
-        }, new DevicePolicyEventWrapper.Builder(EventId.ADD_CROSS_PROFILE_INTENT_FILTER_VALUE)
-                .setAdminPackageName(MANAGED_PROFILE_PKG)
-                .setInt(1)
-                .setStrings("com.android.cts.managedprofile.ACTION_TEST_ALL_ACTIVITY")
-                .build());
-
-        // Set up filters from primary to managed profile
-        String command = "am start -W --user " + mProfileUserId + " " + MANAGED_PROFILE_PKG
-                + "/.PrimaryUserFilterSetterActivity";
-        CLog.d("Output for command " + command + ": "
-                + getDevice().executeShellCommand(command));
-        runDeviceTestsAsUser(
-                MANAGED_PROFILE_PKG, MANAGED_PROFILE_PKG + ".PrimaryUserTest", mParentUserId);
-        // TODO: Test with startActivity
-    }
-
-    public void testDisallowSharingIntoProfileFromProfile() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        // Set up activities: PrimaryUserActivity will only be enabled in the personal user
-        // This activity is used to find out the ground truth about the system's cross profile
-        // intent forwarding activity.
-        disableActivityForUser("PrimaryUserActivity", mProfileUserId);
-
-        // Tests from the profile side
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
-                ".DisallowSharingIntoProfileTest", "testSharingFromProfile", mProfileUserId);
-    }
-
-    public void testDisallowSharingIntoProfileFromPersonal() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        // Set up activities: ManagedProfileActivity will only be enabled in the managed profile
-        // This activity is used to find out the ground truth about the system's cross profile
-        // intent forwarding activity.
-        disableActivityForUser("ManagedProfileActivity", mParentUserId);
-
-        // Tests from the personal side, which is mostly driven from host side.
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".DisallowSharingIntoProfileTest",
-                "testSetUp", mProfileUserId);
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".DisallowSharingIntoProfileTest",
-                "testDisableSharingIntoProfile", mProfileUserId);
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".DisallowSharingIntoProfileTest",
-                "testSharingFromPersonalFails", mParentUserId);
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".DisallowSharingIntoProfileTest",
-                "testEnableSharingIntoProfile", mProfileUserId);
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".DisallowSharingIntoProfileTest",
-                "testSharingFromPersonalSucceeds", mParentUserId);
-    }
-
+    @LargeTest
     public void testAppLinks_verificationStatus() throws Exception {
         if (!mHasFeature) {
             return;
@@ -518,6 +137,7 @@
         assertAppLinkResult("testReceivedByAppLinkActivityInManaged");
     }
 
+    @LargeTest
     public void testAppLinks_enabledStatus() throws Exception {
         if (!mHasFeature) {
             return;
@@ -580,134 +200,6 @@
                 mProfileUserId);
     }
 
-    public void testCrossProfileContent() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-
-        // Storage permission shouldn't be granted, we check if missing permissions are respected
-        // in ContentTest#testSecurity.
-        installAppAsUser(INTENT_SENDER_APK, false /* grantPermissions */, USER_ALL);
-        installAppAsUser(INTENT_RECEIVER_APK, USER_ALL);
-
-        // Test from parent to managed
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
-                "testRemoveAllFilters", mProfileUserId);
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
-                "testAddManagedCanAccessParentFilters", mProfileUserId);
-        runDeviceTestsAsUser(INTENT_SENDER_PKG, ".ContentTest", mParentUserId);
-
-        // Test from managed to parent
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
-                "testRemoveAllFilters", mProfileUserId);
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
-                "testAddParentCanAccessManagedFilters", mProfileUserId);
-        runDeviceTestsAsUser(INTENT_SENDER_PKG, ".ContentTest", mProfileUserId);
-
-    }
-
-    public void testCrossProfileNotificationListeners_EmptyWhitelist() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-
-        installAppAsUser(NOTIFICATION_APK, USER_ALL);
-
-        // Profile owner in the profile sets an empty whitelist
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
-                "testSetEmptyWhitelist", mProfileUserId,
-                Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
-        // Listener outside the profile can only see personal notifications.
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
-                "testCannotReceiveProfileNotifications", mParentUserId,
-                Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
-    }
-
-    public void testCrossProfileNotificationListeners_NullWhitelist() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-
-        installAppAsUser(NOTIFICATION_APK, USER_ALL);
-
-        // Profile owner in the profile sets a null whitelist
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
-                "testSetNullWhitelist", mProfileUserId,
-                Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
-        // Listener outside the profile can see profile and personal notifications
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
-                "testCanReceiveNotifications", mParentUserId,
-                Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
-    }
-
-    public void testCrossProfileNotificationListeners_InWhitelist() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-
-        installAppAsUser(NOTIFICATION_APK, USER_ALL);
-
-        // Profile owner in the profile adds listener to the whitelist
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
-                "testAddListenerToWhitelist", mProfileUserId,
-                Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
-        // Listener outside the profile can see profile and personal notifications
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
-                "testCanReceiveNotifications", mParentUserId,
-                Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
-    }
-
-    public void testCrossProfileNotificationListeners_setAndGet() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        installAppAsUser(NOTIFICATION_APK, USER_ALL);
-
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
-                "testSetAndGetPermittedCrossProfileNotificationListeners", mProfileUserId,
-                Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
-    }
-
-    public void testCrossProfileCopyPaste() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        installAppAsUser(INTENT_RECEIVER_APK, USER_ALL);
-        installAppAsUser(INTENT_SENDER_APK, USER_ALL);
-
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
-                "testAllowCrossProfileCopyPaste", mProfileUserId);
-        // Test that managed can see what is copied in the parent.
-        testCrossProfileCopyPasteInternal(mProfileUserId, true);
-        // Test that the parent can see what is copied in managed.
-        testCrossProfileCopyPasteInternal(mParentUserId, true);
-
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
-                "testDisallowCrossProfileCopyPaste", mProfileUserId);
-        // Test that managed can still see what is copied in the parent.
-        testCrossProfileCopyPasteInternal(mProfileUserId, true);
-        // Test that the parent cannot see what is copied in managed.
-        testCrossProfileCopyPasteInternal(mParentUserId, false);
-    }
-
-    private void testCrossProfileCopyPasteInternal(int userId, boolean shouldSucceed)
-            throws DeviceNotAvailableException {
-        final String direction = (userId == mParentUserId)
-                ? "testAddManagedCanAccessParentFilters"
-                : "testAddParentCanAccessManagedFilters";
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
-                "testRemoveAllFilters", mProfileUserId);
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
-                direction, mProfileUserId);
-        if (shouldSucceed) {
-            runDeviceTestsAsUser(INTENT_SENDER_PKG, ".CopyPasteTest",
-                    "testCanReadAcrossProfiles", userId);
-        } else {
-            runDeviceTestsAsUser(INTENT_SENDER_PKG, ".CopyPasteTest",
-                    "testCannotReadAcrossProfiles", userId);
-        }
-    }
-
     /** Tests for the API helper class. */
     public void testCurrentApiHelper() throws Exception {
         if (!mHasFeature) {
@@ -793,94 +285,6 @@
         }
     }
 
-
-    public void testManagedContactsUris() throws Exception {
-        runManagedContactsTest(new Callable<Void>() {
-            @Override
-            public Void call() throws Exception {
-                ContactsTestSet contactsTestSet = new ContactsTestSet(ManagedProfileTest.this,
-                        MANAGED_PROFILE_PKG, mParentUserId, mProfileUserId);
-
-                contactsTestSet.setCallerIdEnabled(true);
-                contactsTestSet.setContactsSearchEnabled(true);
-                contactsTestSet.checkIfCanLookupEnterpriseContacts(true);
-                contactsTestSet.checkIfCanFilterEnterpriseContacts(true);
-                contactsTestSet.checkIfCanFilterSelfContacts();
-                return null;
-            }
-        });
-    }
-
-    public void testManagedQuickContacts() throws Exception {
-        runManagedContactsTest(new Callable<Void>() {
-            @Override
-            public Void call() throws Exception {
-                runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
-                        "testQuickContact", mParentUserId);
-                return null;
-            }
-        });
-    }
-
-    public void testManagedContactsPolicies() throws Exception {
-        runManagedContactsTest(new Callable<Void>() {
-            @Override
-            public Void call() throws Exception {
-                ContactsTestSet contactsTestSet = new ContactsTestSet(ManagedProfileTest.this,
-                        MANAGED_PROFILE_PKG, mParentUserId, mProfileUserId);
-                try {
-                    contactsTestSet.setCallerIdEnabled(true);
-                    contactsTestSet.setContactsSearchEnabled(false);
-                    contactsTestSet.checkIfCanLookupEnterpriseContacts(true);
-                    contactsTestSet.checkIfCanFilterEnterpriseContacts(false);
-                    contactsTestSet.checkIfCanFilterSelfContacts();
-                    contactsTestSet.setCallerIdEnabled(false);
-                    contactsTestSet.setContactsSearchEnabled(true);
-                    contactsTestSet.checkIfCanLookupEnterpriseContacts(false);
-                    contactsTestSet.checkIfCanFilterEnterpriseContacts(true);
-                    contactsTestSet.checkIfCanFilterSelfContacts();
-                    contactsTestSet.setCallerIdEnabled(false);
-                    contactsTestSet.setContactsSearchEnabled(false);
-                    contactsTestSet.checkIfCanLookupEnterpriseContacts(false);
-                    contactsTestSet.checkIfCanFilterEnterpriseContacts(false);
-                    contactsTestSet.checkIfCanFilterSelfContacts();
-                    contactsTestSet.checkIfNoEnterpriseDirectoryFound();
-                    assertMetricsLogged(getDevice(), () -> {
-                        contactsTestSet.setCallerIdEnabled(true);
-                        contactsTestSet.setCallerIdEnabled(false);
-                    }, new DevicePolicyEventWrapper
-                                .Builder(EventId.SET_CROSS_PROFILE_CALLER_ID_DISABLED_VALUE)
-                                .setAdminPackageName(MANAGED_PROFILE_PKG)
-                                .setBoolean(false)
-                                .build(),
-                        new DevicePolicyEventWrapper
-                                .Builder(EventId.SET_CROSS_PROFILE_CALLER_ID_DISABLED_VALUE)
-                                .setAdminPackageName(MANAGED_PROFILE_PKG)
-                                .setBoolean(true)
-                                .build());
-                    assertMetricsLogged(getDevice(), () -> {
-                        contactsTestSet.setContactsSearchEnabled(true);
-                        contactsTestSet.setContactsSearchEnabled(false);
-                    }, new DevicePolicyEventWrapper
-                                .Builder(EventId.SET_CROSS_PROFILE_CONTACTS_SEARCH_DISABLED_VALUE)
-                                .setAdminPackageName(MANAGED_PROFILE_PKG)
-                                .setBoolean(false)
-                                .build(),
-                        new DevicePolicyEventWrapper
-                                .Builder(EventId.SET_CROSS_PROFILE_CONTACTS_SEARCH_DISABLED_VALUE)
-                                .setAdminPackageName(MANAGED_PROFILE_PKG)
-                                .setBoolean(true)
-                                .build());
-                    return null;
-                } finally {
-                    // reset policies
-                    contactsTestSet.setCallerIdEnabled(true);
-                    contactsTestSet.setContactsSearchEnabled(true);
-                }
-            }
-        });
-    }
-
     public void testOrganizationInfo() throws Exception {
         if (!mHasFeature) {
             return;
@@ -900,14 +304,6 @@
                 .build());
     }
 
-    public void testPasswordMinimumRestrictions() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".PasswordMinimumRestrictionsTest",
-                mProfileUserId);
-    }
-
     public void testDevicePolicyManagerParentSupport() throws Exception {
         if (!mHasFeature) {
             return;
@@ -950,6 +346,7 @@
                 /*expectFailure*/ true));
     }
 
+    @LargeTest
     public void testCannotSetDeviceOwnerWhenProfilePresent() throws Exception {
         if (!mHasFeature) {
             return;
@@ -985,84 +382,6 @@
                 "testNfcShareEnabled", mParentUserId);
     }
 
-    public void testCrossProfileWidgets() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-
-        try {
-            installAppAsUser(WIDGET_PROVIDER_APK, USER_ALL);
-            getDevice().executeShellCommand("appwidget grantbind --user " + mParentUserId
-                    + " --package " + WIDGET_PROVIDER_PKG);
-            setIdleWhitelist(WIDGET_PROVIDER_PKG, true);
-            startWidgetHostService();
-
-            String commandOutput = changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG,
-                    "add-cross-profile-widget", mProfileUserId);
-            assertTrue("Command was expected to succeed " + commandOutput,
-                    commandOutput.contains("Status: ok"));
-
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileWidgetTest",
-                    "testCrossProfileWidgetProviderAdded", mProfileUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
-                    ".CrossProfileWidgetPrimaryUserTest",
-                    "testHasCrossProfileWidgetProvider_true", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
-                    ".CrossProfileWidgetPrimaryUserTest",
-                    "testHostReceivesWidgetUpdates_true", mParentUserId);
-
-            commandOutput = changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG,
-                    "remove-cross-profile-widget", mProfileUserId);
-            assertTrue("Command was expected to succeed " + commandOutput,
-                    commandOutput.contains("Status: ok"));
-
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileWidgetTest",
-                    "testCrossProfileWidgetProviderRemoved", mProfileUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
-                    ".CrossProfileWidgetPrimaryUserTest",
-                    "testHasCrossProfileWidgetProvider_false", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
-                    ".CrossProfileWidgetPrimaryUserTest",
-                    "testHostReceivesWidgetUpdates_false", mParentUserId);
-        } finally {
-            changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG, "remove-cross-profile-widget",
-                    mProfileUserId);
-            getDevice().uninstallPackage(WIDGET_PROVIDER_PKG);
-        }
-    }
-
-    public void testCrossProfileWidgetsLogged() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-
-        try {
-            installAppAsUser(WIDGET_PROVIDER_APK, USER_ALL);
-            getDevice().executeShellCommand("appwidget grantbind --user " + mParentUserId
-                    + " --package " + WIDGET_PROVIDER_PKG);
-            setIdleWhitelist(WIDGET_PROVIDER_PKG, true);
-            startWidgetHostService();
-
-            assertMetricsLogged(getDevice(), () -> {
-                changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG,
-                        "add-cross-profile-widget", mProfileUserId);
-                changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG,
-                        "remove-cross-profile-widget", mProfileUserId);
-            }, new DevicePolicyEventWrapper
-                        .Builder(EventId.ADD_CROSS_PROFILE_WIDGET_PROVIDER_VALUE)
-                        .setAdminPackageName(MANAGED_PROFILE_PKG)
-                        .build(),
-                new DevicePolicyEventWrapper
-                        .Builder(EventId.REMOVE_CROSS_PROFILE_WIDGET_PROVIDER_VALUE)
-                        .setAdminPackageName(MANAGED_PROFILE_PKG)
-                        .build());
-        } finally {
-            changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG, "remove-cross-profile-widget",
-                    mProfileUserId);
-            getDevice().uninstallPackage(WIDGET_PROVIDER_PKG);
-        }
-    }
-
     public void testIsProvisioningAllowed() throws DeviceNotAvailableException {
         if (!mHasFeature) {
             return;
@@ -1077,16 +396,6 @@
                 "testIsProvisioningAllowedTrue", mParentUserId);
     }
 
-    private void setDirectoryPrefix(String directoryName, int userId)
-            throws DeviceNotAvailableException {
-        String command = "content call --uri " + DIRECTORY_PRIVOIDER_URI
-                + " --user " + userId
-                + " --method " + SET_CUSTOM_DIRECTORY_PREFIX_METHOD
-                + " --arg " + directoryName;
-        CLog.d("Output for command " + command + ": "
-                + getDevice().executeShellCommand(command));
-    }
-
     public void testPhoneAccountVisibility() throws Exception {
         if (!mHasFeature) {
             return;
@@ -1127,6 +436,7 @@
         }
     }
 
+    @LargeTest
     public void testManagedCall() throws Exception {
         if (!mHasFeature) {
             return;
@@ -1177,52 +487,6 @@
                 mParentUserId);
     }
 
-    private void givePackageWriteSettingsPermission(int userId, String pkg) throws Exception {
-        // Allow app to write to settings (for RingtoneManager.setActualDefaultUri to work)
-        String command = "appops set --user " + userId + " " + pkg
-                + " android:write_settings allow";
-        CLog.d("Output for command " + command + ": " + getDevice().executeShellCommand(command));
-    }
-
-    public void testRingtoneSync() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        givePackageWriteSettingsPermission(mProfileUserId, MANAGED_PROFILE_PKG);
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".RingtoneSyncTest",
-                "testRingtoneSync", mProfileUserId);
-    }
-
-    // Test if setting RINGTONE disables sync
-    public void testRingtoneSyncAutoDisableRingtone() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        givePackageWriteSettingsPermission(mProfileUserId, MANAGED_PROFILE_PKG);
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".RingtoneSyncTest",
-                "testRingtoneDisableSync", mProfileUserId);
-    }
-
-    // Test if setting NOTIFICATION disables sync
-    public void testRingtoneSyncAutoDisableNotification() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        givePackageWriteSettingsPermission(mProfileUserId, MANAGED_PROFILE_PKG);
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".RingtoneSyncTest",
-                "testNotificationDisableSync", mProfileUserId);
-    }
-
-    // Test if setting ALARM disables sync
-    public void testRingtoneSyncAutoDisableAlarm() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        givePackageWriteSettingsPermission(mProfileUserId, MANAGED_PROFILE_PKG);
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".RingtoneSyncTest",
-                "testAlarmDisableSync", mProfileUserId);
-    }
-
     public void testTrustAgentInfo() throws Exception {
         if (!mHasFeature || !mHasSecureLockScreen) {
             return;
@@ -1298,330 +562,6 @@
                 "testProfileOwnerCannotGetDeviceIdentifiersWithoutPermission", mProfileUserId);
     }
 
-    public void testResetPasswordWithTokenBeforeUnlock() throws Exception {
-        if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
-            return;
-        }
-
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
-                "testSetupWorkProfile", mProfileUserId);
-        lockProfile();
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
-                "testResetPasswordBeforeUnlock", mProfileUserId);
-        // Password needs to be in sync with ResetPasswordWithTokenTest.PASSWORD1
-        verifyUserCredential(RESET_PASSWORD_TEST_DEFAULT_PASSWORD, mProfileUserId);
-    }
-
-    public void testClearPasswordWithTokenBeforeUnlock() throws Exception {
-        if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
-            return;
-        }
-
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
-                "testSetupWorkProfile", mProfileUserId);
-        lockProfile();
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
-                "testClearPasswordBeforeUnlock", mProfileUserId);
-        // Make sure profile has no password
-        verifyUserCredential("", mProfileUserId);
-    }
-
-    /**
-     * Test password reset token is still functional after the primary user clears and
-     * re-adds back its device lock. This is to detect a regression where the work profile
-     * undergoes an untrusted credential reset (causing synthetic password to change, invalidating
-     * existing password reset token) if it has unified work challenge and the primary user clears
-     * the device lock.
-     */
-    public void testResetPasswordTokenUsableAfterClearingLock() throws Exception {
-        if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
-            return;
-        }
-        final String devicePassword = "1234";
-
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
-                "testSetResetPasswordToken", mProfileUserId);
-        try {
-            changeUserCredential(devicePassword, null, mParentUserId);
-            changeUserCredential(null, devicePassword, mParentUserId);
-            changeUserCredential(devicePassword, null, mParentUserId);
-            lockProfile();
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
-                    "testResetPasswordBeforeUnlock", mProfileUserId);
-            verifyUserCredential(RESET_PASSWORD_TEST_DEFAULT_PASSWORD, mProfileUserId);
-        } finally {
-            changeUserCredential(null, devicePassword, mParentUserId);
-            // Cycle the device screen to flush stale password information from keyguard,
-            // otherwise it will still ask for the non-existent password.
-            // return screen to be on for cts test runs
-            executeShellCommand("input keyevent KEYCODE_WAKEUP");
-            executeShellCommand("input keyevent KEYCODE_SLEEP");
-            executeShellCommand("input keyevent KEYCODE_WAKEUP");
-        }
-    }
-
-    public void testIsUsingUnifiedPassword() throws Exception {
-        if (!mHasFeature || !mHasSecureLockScreen) {
-            return;
-        }
-
-        // Freshly created profile has no separate challenge.
-        verifyUnifiedPassword(true);
-
-        // Set separate challenge and verify that the API reports it correctly.
-        changeUserCredential("1234" /* newCredential */, null /* oldCredential */, mProfileUserId);
-        verifyUnifiedPassword(false);
-    }
-
-    public void testUnlockWorkProfile_deviceWidePassword() throws Exception {
-        if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
-            return;
-        }
-        String password = "0000";
-        try {
-            // Add a device password after the work profile has been created.
-            changeUserCredential(password, /* oldCredential= */ null, mPrimaryUserId);
-            // Lock the profile with key eviction.
-            lockProfile();
-            // Turn on work profile, by unlocking the profile with the device password.
-            verifyUserCredential(password, mPrimaryUserId);
-
-            // Verify profile user is running unlocked by running a sanity test on the work profile.
-            installAppAsUser(SIMPLE_APP_APK, mProfileUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".SanityTest", mProfileUserId);
-        } finally {
-            // Clean up
-            changeUserCredential(/* newCredential= */ null, password, mPrimaryUserId);
-        }
-    }
-
-    public void testRebootDevice_unifiedPassword() throws Exception {
-        if (!mHasFeature || !mHasSecureLockScreen) {
-            return;
-        }
-        // Waiting before rebooting prevents flakiness.
-        waitForBroadcastIdle();
-        String password = "0000";
-        changeUserCredential(password, /* oldCredential= */ null, mPrimaryUserId);
-        try {
-            rebootAndWaitUntilReady();
-            verifyUserCredential(password, mPrimaryUserId);
-            installAppAsUser(SIMPLE_APP_APK, mProfileUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".SanityTest", mProfileUserId);
-        } finally {
-            changeUserCredential(/* newCredential= */ null, password, mPrimaryUserId);
-            // Work-around for http://b/113866275 - password prompt being erroneously shown at the
-            // end.
-            pressPowerButton();
-        }
-    }
-
-    public void testRebootDevice_separatePasswords() throws Exception {
-        if (!mHasFeature || !mHasSecureLockScreen) {
-            return;
-        }
-        // Waiting before rebooting prevents flakiness.
-        waitForBroadcastIdle();
-        String profilePassword = "profile";
-        String primaryPassword = "primary";
-        int managedProfileUserId = getFirstManagedProfileUserId();
-        changeUserCredential(
-                profilePassword, /* oldCredential= */ null, managedProfileUserId);
-        changeUserCredential(primaryPassword, /* oldCredential= */ null, mPrimaryUserId);
-        try {
-            rebootAndWaitUntilReady();
-            verifyUserCredential(profilePassword, managedProfileUserId);
-            verifyUserCredential(primaryPassword, mPrimaryUserId);
-            installAppAsUser(SIMPLE_APP_APK, mProfileUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".SanityTest", mProfileUserId);
-        } finally {
-            changeUserCredential(
-                    /* newCredential= */ null, profilePassword, managedProfileUserId);
-            changeUserCredential(/* newCredential= */ null, primaryPassword, mPrimaryUserId);
-            // Work-around for http://b/113866275 - password prompt being erroneously shown at the
-            // end.
-            pressPowerButton();
-        }
-    }
-
-    public void testCrossProfileCalendarPackage() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        assertMetricsLogged(getDevice(), () -> {
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testCrossProfileCalendarPackage", mProfileUserId);
-        }, new DevicePolicyEventWrapper.Builder(EventId.SET_CROSS_PROFILE_CALENDAR_PACKAGES_VALUE)
-                    .setAdminPackageName(MANAGED_PROFILE_PKG)
-                    .setStrings(MANAGED_PROFILE_PKG)
-                    .build());
-    }
-
-    public void testCrossProfileCalendar() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        runCrossProfileCalendarTestsWhenWhitelistedAndEnabled();
-        runCrossProfileCalendarTestsWhenAllPackagesWhitelisted();
-        runCrossProfileCalendarTestsWhenDisabled();
-        runCrossProfileCalendarTestsWhenNotWhitelisted();
-    }
-
-    private void runCrossProfileCalendarTestsWhenWhitelistedAndEnabled() throws Exception {
-        try {
-            // Setup. Add the test package into cross-profile calendar whitelist, enable
-            // cross-profile calendar in settings, and insert test data into calendar provider.
-            // All setups should be done in managed profile.
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testWhitelistManagedProfilePackage", mProfileUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testAddTestCalendarDataForWorkProfile", mProfileUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testEnableCrossProfileCalendarSettings", mProfileUserId);
-
-            // Testing.
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testPrimaryProfile_getCorrectWorkCalendarsWhenEnabled", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testPrimaryProfile_getCorrectWorkEventsWhenEnabled", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testPrimaryProfile_getCorrectWorkInstancesWhenEnabled", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testPrimaryProfile_getCorrectWorkInstancesByDayWhenEnabled", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testPrimaryProfile_canAccessWorkInstancesSearch1", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testPrimaryProfile_canAccessWorkInstancesSearch2", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testPrimaryProfile_canAccessWorkInstancesSearchByDay", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testPrimaryProfile_getExceptionWhenQueryNonWhitelistedColumns", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testViewEventCrossProfile_intentReceivedWhenWhitelisted", mParentUserId);
-        } finally {
-            // Cleanup.
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testCleanupWhitelist", mProfileUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testCleanupTestCalendarDataForWorkProfile", mProfileUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testDisableCrossProfileCalendarSettings", mProfileUserId);
-        }
-    }
-
-    private void runCrossProfileCalendarTestsWhenAllPackagesWhitelisted() throws Exception {
-        try {
-            // Setup. Allow all packages to access cross-profile calendar APIs by setting
-            // the whitelist to null, enable cross-profile calendar in settings,
-            // and insert test data into calendar provider.
-            // All setups should be done in managed profile.
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testWhitelistAllPackages", mProfileUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testAddTestCalendarDataForWorkProfile", mProfileUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testEnableCrossProfileCalendarSettings", mProfileUserId);
-
-            // Testing.
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testPrimaryProfile_getCorrectWorkCalendarsWhenEnabled", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testPrimaryProfile_getCorrectWorkEventsWhenEnabled", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testPrimaryProfile_getCorrectWorkInstancesWhenEnabled", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testPrimaryProfile_getCorrectWorkInstancesByDayWhenEnabled", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testPrimaryProfile_canAccessWorkInstancesSearch1", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testPrimaryProfile_canAccessWorkInstancesSearch2", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testPrimaryProfile_canAccessWorkInstancesSearchByDay", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testPrimaryProfile_getExceptionWhenQueryNonWhitelistedColumns", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testViewEventCrossProfile_intentReceivedWhenWhitelisted", mParentUserId);
-        } finally {
-            // Cleanup.
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testCleanupWhitelist", mProfileUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testCleanupTestCalendarDataForWorkProfile", mProfileUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testDisableCrossProfileCalendarSettings", mProfileUserId);
-        }
-    }
-
-    private void runCrossProfileCalendarTestsWhenDisabled() throws Exception {
-        try {
-            // Setup. Add the test package into cross-profile calendar whitelist,
-            // and insert test data into calendar provider. But disable cross-profile calendar
-            // in settings. Thus cross-profile calendar Uris should not be accessible.
-            // All setups should be done in managed profile.
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testWhitelistManagedProfilePackage", mProfileUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testAddTestCalendarDataForWorkProfile", mProfileUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testDisableCrossProfileCalendarSettings", mProfileUserId);
-
-            // Testing.
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testPrimaryProfile_cannotAccessWorkCalendarsWhenDisabled", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testPrimaryProfile_cannotAccessWorkEventsWhenDisabled", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testPrimaryProfile_cannotAccessWorkInstancesWhenDisabled", mParentUserId);
-        } finally {
-            // Cleanup.
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testCleanupWhitelist", mProfileUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testCleanupTestCalendarDataForWorkProfile", mProfileUserId);
-        }
-    }
-
-    private void runCrossProfileCalendarTestsWhenNotWhitelisted() throws Exception {
-        try {
-            // Setup. Enable cross-profile calendar in settings and insert test data into calendar
-            // provider. But make sure that the test package is not whitelisted for cross-profile
-            // calendar. Thus cross-profile calendar Uris should not be accessible.
-            // All setups should be done in managed profile.
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testAddTestCalendarDataForWorkProfile", mProfileUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testEnableCrossProfileCalendarSettings", mProfileUserId);
-
-            // Testing.
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testPrimaryProfile_cannotAccessWorkCalendarsWhenDisabled", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testPrimaryProfile_cannotAccessWorkEventsWhenDisabled", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testPrimaryProfile_cannotAccessWorkInstancesWhenDisabled", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testViewEventCrossProfile_intentFailedWhenNotWhitelisted", mParentUserId);
-        } finally {
-            // Cleanup.
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testCleanupTestCalendarDataForWorkProfile", mProfileUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
-                    "testDisableCrossProfileCalendarSettings", mProfileUserId);
-        }
-    }
-
-    public void testCreateSeparateChallengeChangedLogged() throws Exception {
-        if (!mHasFeature || !mHasSecureLockScreen) {
-            return;
-        }
-        assertMetricsLogged(getDevice(), () -> {
-            changeUserCredential(
-                    "1234" /* newCredential */, null /* oldCredential */, mProfileUserId);
-        }, new DevicePolicyEventWrapper.Builder(EventId.SEPARATE_PROFILE_CHALLENGE_CHANGED_VALUE)
-                .setBoolean(true)
-                .build());
-    }
-
     public void testSetProfileNameLogged() throws Exception {
         if (!mHasFeature) {
             return;
@@ -1635,23 +575,6 @@
                 .build());
     }
 
-    private void verifyUnifiedPassword(boolean unified) throws DeviceNotAvailableException {
-        final String testMethod =
-                unified ? "testUsingUnifiedPassword" : "testNotUsingUnifiedPassword";
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".IsUsingUnifiedPasswordTest",
-                testMethod, mProfileUserId);
-    }
-
-    private void disableActivityForUser(String activityName, int userId)
-            throws DeviceNotAvailableException {
-        String command = "am start -W --user " + userId
-                + " --es extra-package " + MANAGED_PROFILE_PKG
-                + " --es extra-class-name " + MANAGED_PROFILE_PKG + "." + activityName
-                + " " + MANAGED_PROFILE_PKG + "/.ComponentDisablingActivity ";
-        CLog.d("Output for command " + command + ": "
-                + getDevice().executeShellCommand(command));
-    }
-
     private void changeUserRestrictionOrFail(String key, boolean value, int userId)
             throws DeviceNotAvailableException {
         changeUserRestrictionOrFail(key, value, userId, MANAGED_PROFILE_PKG);
@@ -1662,25 +585,6 @@
         return changeUserRestriction(key, value, userId, MANAGED_PROFILE_PKG);
     }
 
-    private void setIdleWhitelist(String packageName, boolean enabled)
-            throws DeviceNotAvailableException {
-        String command = "cmd deviceidle whitelist " + (enabled ? "+" : "-") + packageName;
-        CLog.d("Output for command " + command + ": "
-                + getDevice().executeShellCommand(command));
-    }
-
-    private String changeCrossProfileWidgetForUser(String packageName, String command, int userId)
-            throws DeviceNotAvailableException {
-        String adbCommand = "am start -W --user " + userId
-                + " -c android.intent.category.DEFAULT "
-                + " --es extra-command " + command
-                + " --es extra-package-name " + packageName
-                + " " + MANAGED_PROFILE_PKG + "/.SetPolicyActivity";
-        String commandOutput = getDevice().executeShellCommand(adbCommand);
-        CLog.d("Output for command " + adbCommand + ": " + commandOutput);
-        return commandOutput;
-    }
-
     // status should be one of never, undefined, ask, always
     private void changeVerificationStatus(int userId, String packageName, String status)
             throws DeviceNotAvailableException {
@@ -1689,15 +593,6 @@
                 + getDevice().executeShellCommand(command));
     }
 
-    protected void startWidgetHostService() throws Exception {
-        String command = "am startservice --user " + mParentUserId
-                + " -a " + WIDGET_PROVIDER_PKG + ".REGISTER_CALLBACK "
-                + "--ei user-extra " + getUserSerialNumber(mProfileUserId)
-                + " " + WIDGET_PROVIDER_PKG + "/.SimpleAppWidgetHostService";
-        CLog.d("Output for command " + command + ": "
-                + getDevice().executeShellCommand(command));
-    }
-
     private void assertAppLinkResult(String methodName) throws DeviceNotAvailableException {
         runDeviceTestsAsUser(INTENT_SENDER_PKG, ".AppLinkTest", methodName,
                 mProfileUserId);
@@ -1706,290 +601,4 @@
     private boolean shouldRunTelecomTest() throws DeviceNotAvailableException {
         return hasDeviceFeature(FEATURE_TELEPHONY) && hasDeviceFeature(FEATURE_CONNECTION_SERVICE);
     }
-
-    private void runManagedContactsTest(Callable<Void> callable) throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-
-        try {
-            // Allow cross profile contacts search.
-            // TODO test both on and off.
-            getDevice().executeShellCommand(
-                    "settings put --user " + mProfileUserId
-                    + " secure managed_profile_contact_remote_search 1");
-
-            // Add test account
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
-                    "testAddTestAccount", mParentUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
-                    "testAddTestAccount", mProfileUserId);
-
-            // Install directory provider to both primary and managed profile
-            installAppAsUser(DIRECTORY_PROVIDER_APK, USER_ALL);
-            setDirectoryPrefix(PRIMARY_DIRECTORY_PREFIX, mParentUserId);
-            setDirectoryPrefix(MANAGED_DIRECTORY_PREFIX, mProfileUserId);
-
-            // Check enterprise directory API works
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
-                    "testGetDirectoryListInPrimaryProfile", mParentUserId);
-
-            // Insert Primary profile Contacts
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
-                    "testPrimaryProfilePhoneAndEmailLookup_insertedAndfound", mParentUserId);
-            // Insert Managed profile Contacts
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
-                    "testManagedProfilePhoneAndEmailLookup_insertedAndfound", mProfileUserId);
-            // Insert a primary contact with same phone & email as other
-            // enterprise contacts
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
-                    "testPrimaryProfileDuplicatedPhoneEmailContact_insertedAndfound",
-                    mParentUserId);
-            // Insert a enterprise contact with same phone & email as other
-            // primary contacts
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
-                    "testManagedProfileDuplicatedPhoneEmailContact_insertedAndfound",
-                    mProfileUserId);
-
-            callable.call();
-
-        } finally {
-            // Clean up in managed profile and primary profile
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
-                    "testCurrentProfileContacts_removeContacts", mProfileUserId);
-            runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
-                    "testCurrentProfileContacts_removeContacts", mParentUserId);
-            getDevice().uninstallPackage(DIRECTORY_PROVIDER_PKG);
-        }
-    }
-
-
-    /*
-     * Container for running ContactsTest under multi-user environment
-     */
-    private static class ContactsTestSet {
-
-        private ManagedProfileTest mManagedProfileTest;
-        private String mManagedProfilePackage;
-        private int mParentUserId;
-        private int mProfileUserId;
-
-        public ContactsTestSet(ManagedProfileTest managedProfileTest, String managedProfilePackage,
-                int parentUserId, int profileUserId) {
-            mManagedProfileTest = managedProfileTest;
-            mManagedProfilePackage = managedProfilePackage;
-            mParentUserId = parentUserId;
-            mProfileUserId = profileUserId;
-        }
-
-        private void runDeviceTestsAsUser(String pkgName, String testClassName,
-                String testMethodName, Integer userId) throws DeviceNotAvailableException {
-            mManagedProfileTest.runDeviceTestsAsUser(pkgName, testClassName, testMethodName,
-                    userId);
-        }
-
-        // Enable / Disable
-        public void setCallerIdEnabled(boolean enabled) throws DeviceNotAvailableException {
-            if (enabled) {
-                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                        "testSetCrossProfileCallerIdDisabled_false", mProfileUserId);
-            } else {
-                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                        "testSetCrossProfileCallerIdDisabled_true", mProfileUserId);
-            }
-        }
-
-        // Enable / Disable cross profile contacts search
-        public void setContactsSearchEnabled(boolean enabled) throws DeviceNotAvailableException {
-            if (enabled) {
-                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                        "testSetCrossProfileContactsSearchDisabled_false", mProfileUserId);
-            } else {
-                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                        "testSetCrossProfileContactsSearchDisabled_true", mProfileUserId);
-            }
-        }
-
-        public void checkIfCanLookupEnterpriseContacts(boolean expected)
-                throws DeviceNotAvailableException {
-            // Primary user cannot use ordinary phone/email lookup api to access
-            // managed contacts
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testPrimaryProfilePhoneLookup_canNotAccessEnterpriseContact", mParentUserId);
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testPrimaryProfileEmailLookup_canNotAccessEnterpriseContact", mParentUserId);
-            // Primary user can use ENTERPRISE_CONTENT_FILTER_URI to access
-            // primary contacts
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testPrimaryProfileEnterprisePhoneLookup_canAccessPrimaryContact",
-                    mParentUserId);
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testPrimaryProfileEnterpriseEmailLookup_canAccessPrimaryContact",
-                    mParentUserId);
-            // When there exist contacts with the same phone/email in primary &
-            // enterprise,
-            // primary user can use ENTERPRISE_CONTENT_FILTER_URI to access the
-            // primary contact.
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testPrimaryProfileEnterpriseEmailLookupDuplicated_canAccessPrimaryContact",
-                    mParentUserId);
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testPrimaryProfileEnterprisePhoneLookupDuplicated_canAccessPrimaryContact",
-                    mParentUserId);
-
-            // Managed user cannot use ordinary phone/email lookup api to access
-            // primary contacts
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testManagedProfilePhoneLookup_canNotAccessPrimaryContact", mProfileUserId);
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testManagedProfileEmailLookup_canNotAccessPrimaryContact", mProfileUserId);
-            // Managed user can use ENTERPRISE_CONTENT_FILTER_URI to access
-            // enterprise contacts
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testManagedProfileEnterprisePhoneLookup_canAccessEnterpriseContact",
-                    mProfileUserId);
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testManagedProfileEnterpriseEmailLookup_canAccessEnterpriseContact",
-                    mProfileUserId);
-            // Managed user cannot use ENTERPRISE_CONTENT_FILTER_URI to access
-            // primary contacts
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testManagedProfileEnterprisePhoneLookup_canNotAccessPrimaryContact",
-                    mProfileUserId);
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testManagedProfileEnterpriseEmailLookup_canNotAccessPrimaryContact",
-                    mProfileUserId);
-            // When there exist contacts with the same phone/email in primary &
-            // enterprise,
-            // managed user can use ENTERPRISE_CONTENT_FILTER_URI to access the
-            // enterprise contact.
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testManagedProfileEnterpriseEmailLookupDuplicated_canAccessEnterpriseContact",
-                    mProfileUserId);
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testManagedProfileEnterprisePhoneLookupDuplicated_canAccessEnterpriseContact",
-                    mProfileUserId);
-
-            // Check if phone lookup can access primary directories
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testPrimaryProfileEnterprisePhoneLookup_canAccessPrimaryDirectories",
-                    mParentUserId);
-
-            // Check if email lookup can access primary directories
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testPrimaryProfileEnterpriseEmailLookup_canAccessPrimaryDirectories",
-                    mParentUserId);
-
-            if (expected) {
-                // Primary user can use ENTERPRISE_CONTENT_FILTER_URI to access
-                // managed profile contacts
-                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                        "testPrimaryProfileEnterprisePhoneLookup_canAccessEnterpriseContact",
-                        mParentUserId);
-                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                        "testPrimaryProfileEnterpriseEmailLookup_canAccessEnterpriseContact",
-                        mParentUserId);
-
-                // Make sure SIP enterprise lookup works too.
-                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                        "testPrimaryProfileEnterpriseSipLookup_canAccessEnterpriseContact",
-                        mParentUserId);
-
-                // Check if phone lookup can access enterprise directories
-                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                        "testPrimaryProfileEnterprisePhoneLookup_canAccessManagedDirectories",
-                        mParentUserId);
-
-                // Check if email lookup can access enterprise directories
-                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                        "testPrimaryProfileEnterpriseEmailLookup_canAccessManagedDirectories",
-                        mParentUserId);
-            } else {
-                // Primary user cannot use ENTERPRISE_CONTENT_FILTER_URI to
-                // access managed contacts
-                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                        "testPrimaryProfileEnterprisePhoneLookup_canNotAccessEnterpriseContact",
-                        mParentUserId);
-                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                        "testPrimaryProfileEnterprisePhoneLookup_canNotAccessManagedDirectories",
-                        mParentUserId);
-
-                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                        "testPrimaryProfileEnterpriseEmailLookup_canNotAccessManagedDirectories",
-                        mParentUserId);
-                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                        "testPrimaryProfileEnterprisePhoneLookup_canNotAccessManagedDirectories",
-                        mParentUserId);
-            }
-        }
-
-        public void checkIfCanFilterSelfContacts() throws DeviceNotAvailableException {
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testPrimaryProfileEnterpriseCallableFilter_canAccessPrimaryDirectories",
-                    mParentUserId);
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testManagedProfileEnterpriseCallableFilter_canAccessManagedDirectories",
-                    mProfileUserId);
-
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testPrimaryProfileEnterpriseEmailFilter_canAccessPrimaryDirectories",
-                    mParentUserId);
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testEnterpriseProfileEnterpriseEmailFilter_canAccessManagedDirectories",
-                    mProfileUserId);
-
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testPrimaryProfileEnterpriseContactFilter_canAccessPrimaryDirectories",
-                    mParentUserId);
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testManagedProfileEnterpriseContactFilter_canAccessManagedDirectories",
-                    mProfileUserId);
-
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testPrimaryProfileEnterprisePhoneFilter_canAccessPrimaryDirectories",
-                    mParentUserId);
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testManagedProfileEnterprisePhoneFilter_canAccessManagedDirectories",
-                    mProfileUserId);
-        }
-
-        public void checkIfCanFilterEnterpriseContacts(boolean expected)
-                throws DeviceNotAvailableException {
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testFilterUriWhenDirectoryParamMissing", mParentUserId);
-            if (expected) {
-                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                        "testPrimaryProfileEnterpriseCallableFilter_canAccessManagedDirectories",
-                        mParentUserId);
-                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                        "testPrimaryProfileEnterpriseEmailFilter_canAccessManagedDirectories",
-                        mParentUserId);
-                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                        "testPrimaryProfileEnterpriseContactFilter_canAccessManagedDirectories",
-                        mParentUserId);
-                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                        "testPrimaryProfileEnterprisePhoneFilter_canAccessManagedDirectories",
-                        mParentUserId);
-            } else {
-                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                        "testPrimaryProfileEnterpriseCallableFilter_canNotAccessManagedDirectories",
-                        mParentUserId);
-                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                        "testPrimaryProfileEnterpriseEmailFilter_canNotAccessManagedDirectories",
-                        mParentUserId);
-                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                        "testPrimaryProfileEnterpriseContactFilter_canNotAccessManagedDirectories",
-                        mParentUserId);
-                runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                        "testPrimaryProfileEnterprisePhoneFilter_canNotAccessManagedDirectories",
-                        mParentUserId);
-            }
-        }
-
-        public void checkIfNoEnterpriseDirectoryFound() throws DeviceNotAvailableException {
-            runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
-                    "testPrimaryProfileEnterpriseDirectories_canNotAccessManagedDirectories",
-                    mParentUserId);
-        }
-    }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTimeoutTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTimeoutTest.java
new file mode 100644
index 0000000..341912c
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTimeoutTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.devicepolicy;
+
+import android.platform.test.annotations.FlakyTest;
+import android.platform.test.annotations.LargeTest;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+
+import java.util.concurrent.TimeUnit;
+
+public class ManagedProfileTimeoutTest extends BaseManagedProfileTest {
+    private static final String PROFILE_CREDENTIAL = "1234";
+    // This should be sufficiently larger than ProfileTimeoutTestHelper.TIMEOUT_MS
+    private static final int PROFILE_TIMEOUT_DELAY_MS = 40_000;
+
+    /** Profile should get locked if it is not in foreground no matter what. */
+    @FlakyTest
+    public void testWorkProfileTimeoutBackground() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        setUpWorkProfileTimeout();
+
+        startDummyActivity(mPrimaryUserId, true);
+        simulateUserInteraction(PROFILE_TIMEOUT_DELAY_MS);
+
+        verifyOnlyProfileLocked(true);
+    }
+
+    /** Profile should get locked if it is in foreground but with no user activity. */
+    @LargeTest
+    public void testWorkProfileTimeoutIdleActivity() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        setUpWorkProfileTimeout();
+
+        startDummyActivity(mProfileUserId, false);
+        Thread.sleep(PROFILE_TIMEOUT_DELAY_MS);
+
+        verifyOnlyProfileLocked(true);
+    }
+
+    /** User activity in profile should prevent it from locking. */
+    @FlakyTest
+    public void testWorkProfileTimeoutUserActivity() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        setUpWorkProfileTimeout();
+
+        startDummyActivity(mProfileUserId, false);
+        simulateUserInteraction(PROFILE_TIMEOUT_DELAY_MS);
+
+        verifyOnlyProfileLocked(false);
+    }
+
+    /** Keep screen on window flag in the profile should prevent it from locking. */
+    @FlakyTest
+    public void testWorkProfileTimeoutKeepScreenOnWindow() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        setUpWorkProfileTimeout();
+
+        startDummyActivity(mProfileUserId, true);
+        Thread.sleep(PROFILE_TIMEOUT_DELAY_MS);
+
+        verifyOnlyProfileLocked(false);
+    }
+
+    private void setUpWorkProfileTimeout() throws DeviceNotAvailableException {
+        // Set separate challenge.
+        changeUserCredential(PROFILE_CREDENTIAL, null, mProfileUserId);
+
+        // Make sure the profile is not prematurely locked.
+        verifyUserCredential(PROFILE_CREDENTIAL, mProfileUserId);
+        verifyOnlyProfileLocked(false);
+        // Set profile timeout to 5 seconds.
+        runProfileTimeoutTest("testSetWorkProfileTimeout", mProfileUserId);
+    }
+
+    private void verifyOnlyProfileLocked(boolean locked) throws DeviceNotAvailableException {
+        final String expectedResultTest = locked ? "testDeviceLocked" : "testDeviceNotLocked";
+        runProfileTimeoutTest(expectedResultTest, mProfileUserId);
+        // Primary profile shouldn't be locked.
+        runProfileTimeoutTest("testDeviceNotLocked", mPrimaryUserId);
+    }
+
+    private void simulateUserInteraction(int timeMs) throws Exception {
+        final long endTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeMs);
+        final UserActivityEmulator helper = new UserActivityEmulator(getDevice());
+        while (System.nanoTime() < endTime) {
+            helper.tapScreenCenter();
+            // Just in case to prevent busy loop.
+            Thread.sleep(100);
+        }
+    }
+
+    private void runProfileTimeoutTest(String method, int userId)
+            throws DeviceNotAvailableException {
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, MANAGED_PROFILE_PKG + ".ProfileTimeoutTestHelper",
+                method, userId);
+    }
+
+    private void startDummyActivity(int profileUserId, boolean keepScreenOn) throws Exception {
+        getDevice().executeShellCommand(String.format(
+                "am start-activity -W --user %d --ez keep_screen_on %s %s/.TimeoutActivity",
+                profileUserId, keepScreenOn, MANAGED_PROFILE_PKG));
+    }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileWipeTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileWipeTest.java
new file mode 100644
index 0000000..0445897
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileWipeTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.devicepolicy;
+
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+
+import android.platform.test.annotations.FlakyTest;
+import android.stats.devicepolicy.EventId;
+
+import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
+
+public class ManagedProfileWipeTest extends BaseManagedProfileTest {
+    @FlakyTest
+    public void testWipeDataWithReason() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        assertTrue(listUsers().contains(mProfileUserId));
+        sendWipeProfileBroadcast("com.android.cts.managedprofile.WIPE_DATA_WITH_REASON");
+        // Note: the managed profile is removed by this test, which will make removeUserCommand in
+        // tearDown() to complain, but that should be OK since its result is not asserted.
+        assertUserGetsRemoved(mProfileUserId);
+        // testWipeDataWithReason() removes the managed profile,
+        // so it needs to separated from other tests.
+        // Check and clear the notification is presented after work profile got removed, so profile
+        // user no longer exists, verification should be run in primary user.
+        runDeviceTestsAsUser(
+                MANAGED_PROFILE_PKG,
+                ".WipeDataNotificationTest",
+                "testWipeDataWithReasonVerification",
+                mParentUserId);
+    }
+
+    @FlakyTest
+    public void testWipeDataLogged() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        assertTrue(listUsers().contains(mProfileUserId));
+        assertMetricsLogged(getDevice(), () -> {
+            sendWipeProfileBroadcast("com.android.cts.managedprofile.WIPE_DATA_WITH_REASON");
+        }, new DevicePolicyEventWrapper.Builder(EventId.WIPE_DATA_WITH_REASON_VALUE)
+                .setAdminPackageName(MANAGED_PROFILE_PKG)
+                .setInt(0)
+                .build());
+        // Check and clear the notification is presented after work profile got removed, so profile
+        // user no longer exists, verification should be run in primary user.
+        runDeviceTestsAsUser(
+                MANAGED_PROFILE_PKG,
+                ".WipeDataNotificationTest",
+                "testWipeDataWithReasonVerification",
+                mParentUserId);
+    }
+
+    @FlakyTest
+    public void testWipeDataWithoutReason() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        assertTrue(listUsers().contains(mProfileUserId));
+        sendWipeProfileBroadcast("com.android.cts.managedprofile.WIPE_DATA_WITHOUT_REASON");
+        // Note: the managed profile is removed by this test, which will make removeUserCommand in
+        // tearDown() to complain, but that should be OK since its result is not asserted.
+        assertUserGetsRemoved(mProfileUserId);
+        // testWipeDataWithoutReason() removes the managed profile,
+        // so it needs to separated from other tests.
+        // Check the notification is not presented after work profile got removed, so profile user
+        // no longer exists, verification should be run in primary user.
+        runDeviceTestsAsUser(
+                MANAGED_PROFILE_PKG,
+                ".WipeDataNotificationTest",
+                "testWipeDataWithoutReasonVerification",
+                mParentUserId);
+    }
+
+    /**
+     * wipeData() test removes the managed profile, so it needs to be separated from other tests.
+     */
+    public void testWipeData() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        assertTrue(listUsers().contains(mProfileUserId));
+        sendWipeProfileBroadcast("com.android.cts.managedprofile.WIPE_DATA");
+        // Note: the managed profile is removed by this test, which will make removeUserCommand in
+        // tearDown() to complain, but that should be OK since its result is not asserted.
+        assertUserGetsRemoved(mProfileUserId);
+    }
+
+    private void sendWipeProfileBroadcast(String action) throws Exception {
+        final String cmd = "am broadcast --receiver-foreground --user " + mProfileUserId
+                + " -a " + action
+                + " com.android.cts.managedprofile/.WipeDataReceiver";
+        getDevice().executeShellCommand(cmd);
+    }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerHostSideTransferTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerHostSideTransferTest.java
index fa0e60b..dbe8e52 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerHostSideTransferTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerHostSideTransferTest.java
@@ -15,6 +15,8 @@
  */
 package com.android.cts.devicepolicy;
 
+import android.platform.test.annotations.LargeTest;
+
 /**
  * Tests the DPC transfer functionality for device owner. Testing is done by having two dummy DPCs,
  * CtsTransferOwnerOutgoingApp and CtsTransferOwnerIncomingApp. The former is the current DPC
@@ -49,6 +51,7 @@
         }
     }
 
+    @LargeTest
     public void testTransferAffiliatedProfileOwnershipCompleteCallback() throws Exception {
         if (!mHasFeature || !hasDeviceFeature("android.software.managed_users")) {
             return;
@@ -72,6 +75,7 @@
                 mUserId);
     }
 
+    @LargeTest
     public void testTransferAffiliatedProfileOwnershipInComp() throws Exception {
         if (!mHasFeature || !hasDeviceFeature("android.software.managed_users")) {
             return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
index 6f3c07c..0b837de 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.cts.devicepolicy;
 
+import android.platform.test.annotations.FlakyTest;
 import android.stats.devicepolicy.EventId;
 
 import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
@@ -66,8 +67,10 @@
 
         final int userId = createSecondaryUserAsProfileOwner();
         runDeviceTestsAsUser(
-                DEVICE_ADMIN_PKG, ".AffiliationTest",
-                "testLockTaskMethodsThrowExceptionIfUnaffiliated", userId);
+                DEVICE_ADMIN_PKG,
+                ".AffiliationTest",
+                "testLockTaskMethodsThrowExceptionIfUnaffiliated",
+                userId);
 
         setUserAsAffiliatedUserToPrimary(userId);
         runDeviceTestsAsUser(
@@ -77,6 +80,7 @@
                 userId);
     }
 
+    @FlakyTest(bugId = 127270520)
     public void testLockTask_affiliatedSecondaryUser() throws Exception {
         if (!mHasFeature || !canCreateAdditionalUsers(1)) {
             return;
@@ -98,6 +102,30 @@
                         "testGenerateKeyPairWithDeviceIdAttestationExpectingSuccess", mUserId));
     }
 
+    @FlakyTest
+    @Override
+    public void testCaCertManagement() throws Exception {
+        super.testCaCertManagement();
+    }
+
+    @FlakyTest
+    @Override
+    public void testCannotRemoveUserIfRestrictionSet() throws Exception {
+        super.testCannotRemoveUserIfRestrictionSet();
+    }
+
+    @FlakyTest
+    @Override
+    public void testInstallCaCertLogged() throws Exception {
+        super.testInstallCaCertLogged();
+    }
+
+    @Override
+    @FlakyTest(bugId = 138721077)
+    public void testLockTask_defaultDialer() throws Exception {
+        super.testLockTask_defaultDialer();
+    }
+
     @Override
     Map<String, DevicePolicyEventWrapper[]> getAdditionalDelegationTests() {
         final Map<String, DevicePolicyEventWrapper[]> result = new HashMap<>();
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
index b3b48b4..ca0fb4a 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
@@ -16,6 +16,11 @@
 
 package com.android.cts.devicepolicy;
 
+import android.platform.test.annotations.FlakyTest;
+import android.platform.test.annotations.LargeTest;
+
+import com.android.cts.devicepolicy.annotations.LockSettingsTest;
+import com.android.cts.devicepolicy.annotations.PermissionsTest;
 import com.android.tradefed.device.DeviceNotAvailableException;
 
 /**
@@ -67,6 +72,7 @@
      * Verify that screenshots are still possible for activities in the primary user when the policy
      * is set on the profile owner.
      */
+    @LargeTest
     public void testScreenCaptureDisabled_allowedPrimaryUser() throws Exception {
         if (!mHasFeature) {
             return;
@@ -80,6 +86,7 @@
         executeDeviceTestMethod(".ScreenCaptureDisabledTest", "testScreenCapturePossible");
     }
 
+    @FlakyTest
     public void testScreenCaptureDisabled_assist_allowedPrimaryUser() throws Exception {
         if (!mHasFeature) {
             return;
@@ -122,6 +129,7 @@
     }
 
     /** VPN tests don't require physical device for managed profile, thus overriding. */
+    @FlakyTest
     @Override
     public void testAlwaysOnVpn() throws Exception {
         super.testAlwaysOnVpn();
@@ -135,6 +143,7 @@
 
     /** VPN tests don't require physical device for managed profile, thus overriding. */
     @Override
+    @LargeTest
     public void testAlwaysOnVpnAcrossReboot() throws Exception {
         super.testAlwaysOnVpnAcrossReboot();
     }
@@ -158,6 +167,7 @@
     }
 
     @Override
+    @LockSettingsTest
     public void testResetPasswordWithToken() throws Exception {
         if (!mHasFeature || !mHasSecureLockScreen) {
             return;
@@ -220,6 +230,75 @@
                 "testSucceedsWithProfileOwnerIdsGrant", mUserId);
     }
 
+    @FlakyTest
+    @Override
+    public void testCaCertManagement() throws Exception {
+        super.testCaCertManagement();
+    }
+
+    @FlakyTest
+    @Override
+    public void testDelegatedCertInstaller() throws Exception {
+        super.testDelegatedCertInstaller();
+    }
+
+    @FlakyTest
+    @Override
+    public void testPackageInstallUserRestrictions() throws Exception {
+        super.testPackageInstallUserRestrictions();
+    }
+
+    @Override
+    @FlakyTest
+    @PermissionsTest
+    public void testPermissionGrant() throws Exception {
+        super.testPermissionGrant();
+    }
+
+    @Override
+    @FlakyTest
+    @PermissionsTest
+    public void testPermissionMixedPolicies() throws Exception {
+        super.testPermissionMixedPolicies();
+    }
+
+    @FlakyTest
+    @Override
+    public void testScreenCaptureDisabled_assist() throws Exception {
+        super.testScreenCaptureDisabled_assist();
+    }
+
+    @Override
+    @PermissionsTest
+    public void testPermissionPolicy() throws Exception {
+        super.testPermissionPolicy();
+    }
+
+    @FlakyTest
+    @Override
+    public void testSetMeteredDataDisabledPackages() throws Exception {
+        super.testSetMeteredDataDisabledPackages();
+    }
+
+    @Override
+    @PermissionsTest
+    public void testPermissionAppUpdate() throws Exception {
+        super.testPermissionAppUpdate();
+    }
+
+    @Override
+    @PermissionsTest
+    public void testPermissionGrantPreMApp() throws Exception {
+        super.testPermissionGrantPreMApp();
+    }
+
+    @Override
+    @PermissionsTest
+    public void testPermissionGrantOfDisallowedPermissionWhileOtherPermIsGranted()
+            throws Exception {
+        super.testPermissionGrantOfDisallowedPermissionWhileOtherPermIsGranted();
+    }
+
     @Override
     public void testLockTask() {
         // Managed profiles are not allowed to use lock task
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTestApi25.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTestApi25.java
index 7623b48..74f9c21 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTestApi25.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTestApi25.java
@@ -16,6 +16,10 @@
 
 package com.android.cts.devicepolicy;
 
+import android.platform.test.annotations.LargeTest;
+
+import com.android.cts.devicepolicy.annotations.PermissionsTest;
+
 /**
  * Set of tests for managed profile owner use cases that also apply to device owners.
  * Tests that should be run identically in both cases are added in DeviceAndProfileOwnerTestApi25.
@@ -74,6 +78,7 @@
      *  unlocked, and cannot remove the password even when FBE is unlocked.
      */
     @Override
+    @LargeTest
     public void testResetPasswordFbe() throws Exception {
         if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
             return;
@@ -94,4 +99,10 @@
         executeDeviceTestMethod(FBE_HELPER_CLASS, "testUnlockFbe");
         executeDeviceTestMethod(RESET_PASSWORD_TEST_CLASS, "testResetPasswordManagedProfile");
     }
+
+    @Override
+    @PermissionsTest
+    public void testPermissionGrantPreMApp() throws Exception {
+        super.testPermissionGrantPreMApp();
+    }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java
index e1d50bd..be1a414 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java
@@ -16,6 +16,9 @@
 
 package com.android.cts.devicepolicy;
 
+import android.platform.test.annotations.FlakyTest;
+import android.platform.test.annotations.LargeTest;
+
 /**
  * Set of tests for pure (non-managed) profile owner use cases that also apply to device owners.
  * Tests that should be run identically in both cases are added in DeviceAndProfileOwnerTest.
@@ -48,4 +51,28 @@
         }
         super.tearDown();
     }
+
+    @Override
+    @FlakyTest
+    public void testCaCertManagement() throws Exception {
+        super.testCaCertManagement();
+    }
+
+    @Override
+    @FlakyTest
+    public void testInstallCaCertLogged() throws Exception {
+        super.testInstallCaCertLogged();
+    }
+
+    @Override
+    @LargeTest
+    public void testPackageInstallUserRestrictions() throws Exception {
+        super.testPackageInstallUserRestrictions();
+    }
+
+    @Override
+    @FlakyTest(bugId = 138721077)
+    public void testLockTask_defaultDialer() throws Exception {
+        super.testLockTask_defaultDialer();
+    }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java
index fd9a0d6..a9b7a9c 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java
@@ -1,5 +1,7 @@
 package com.android.cts.devicepolicy;
 
+import android.platform.test.annotations.LargeTest;
+
 import java.util.HashMap;
 import java.util.Map;
 
@@ -49,6 +51,7 @@
         super.tearDown();
     }
 
+    @LargeTest
     public void testQuietMode_defaultForegroundLauncher() throws Exception {
         if (!mHasFeature) {
           return;
@@ -61,6 +64,7 @@
                 createParams(mProfileId));
     }
 
+    @LargeTest
     public void testQuietMode_notForegroundLauncher() throws Exception {
         if (!mHasFeature) {
             return;
@@ -73,6 +77,7 @@
             createParams(mProfileId));
     }
 
+    @LargeTest
     public void testQuietMode_notDefaultLauncher() throws Exception {
         if (!mHasFeature) {
             return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java
index fdf063ea..7130e3b 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java
@@ -15,6 +15,8 @@
  */
 package com.android.cts.devicepolicy;
 
+import android.platform.test.annotations.FlakyTest;
+
 import com.android.tradefed.device.DeviceNotAvailableException;
 
 import javax.annotation.Nonnull;
@@ -128,6 +130,7 @@
     }
 
     // Checks restrictions for managed profile.
+    @FlakyTest
     public void testUserRestrictions_managedProfileOwnerOnly() throws Exception {
         if (!mHasFeature || !mSupportsMultiUser || !mHasManagedUserFeature) {
             return;
@@ -150,6 +153,7 @@
     /**
      * DO + PO combination.  Make sure global DO restrictions are visible on secondary users.
      */
+    @FlakyTest
     public void testUserRestrictions_layering() throws Exception {
         if (!mHasFeature || !mSupportsMultiUser) {
             return;
@@ -216,6 +220,7 @@
      * DO sets profile global restrictions (only ENSURE_VERIFY_APPS), should affect all
      * users (not a particularly special case but to be sure).
      */
+    @FlakyTest
     public void testUserRestrictions_profileGlobalRestrictionsAsDo() throws Exception {
         if (!mHasFeature || !mSupportsMultiUser) {
             return;
@@ -236,6 +241,7 @@
      * Managed profile owner sets profile global restrictions (only ENSURE_VERIFY_APPS), should
      * affect all users.
      */
+    @FlakyTest
     public void testUserRestrictions_ProfileGlobalRestrictionsAsPo() throws Exception {
         if (!mHasFeature || !mSupportsMultiUser || !mHasManagedUserFeature) {
             return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/annotations/LockSettingsTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/annotations/LockSettingsTest.java
new file mode 100644
index 0000000..413df07
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/annotations/LockSettingsTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.devicepolicy.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that a test depends on screenlock functionality (e.g. setting user password, unlocking
+ * the user, etc.) and should be run in presubmit when changes are made to the relevant code.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface LockSettingsTest {}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/annotations/PermissionsTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/annotations/PermissionsTest.java
new file mode 100644
index 0000000..04a8351
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/annotations/PermissionsTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.devicepolicy.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that a test depends on permissions functionality and should be run in presubmit when
+ * permissions code changes are made.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface PermissionsTest {}
diff --git a/hostsidetests/dumpsys/Android.bp b/hostsidetests/dumpsys/Android.bp
index cdcd366..d26c8c4 100644
--- a/hostsidetests/dumpsys/Android.bp
+++ b/hostsidetests/dumpsys/Android.bp
@@ -20,6 +20,7 @@
     libs: [
         "cts-tradefed",
         "tradefed",
+        "truth-prebuilt",
     ],
     // tag this module as a cts test artifact
     test_suites: [
diff --git a/hostsidetests/dumpsys/OWNERS b/hostsidetests/dumpsys/OWNERS
index 8bbfc48..b5deebe 100644
--- a/hostsidetests/dumpsys/OWNERS
+++ b/hostsidetests/dumpsys/OWNERS
@@ -1,2 +1,4 @@
 # Bug component: 153446
-felipeal@google.com
+nandana@google.com
+omakoto@google.com
+kwekua@google.com
diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
index 5aaef193..6c651e2 100644
--- a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
@@ -16,6 +16,8 @@
 
 package android.dumpsys.cts;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.tradefed.log.LogUtil.CLog;
 
@@ -360,13 +362,16 @@
     }
 
     private void checkJobCompletion(String[] parts) {
-        assertEquals(10, parts.length);
+        // This line contains a number for each job cancel reason.
+        // (See JobParameters.JOB_STOP_REASON_CODES), and future mainline updates may introudce
+        // more codes, so we have no upper bound for the number of columns.
+        assertThat(parts.length).isAtLeast(11);
         assertNotNull(parts[4]); // job
-        assertInteger(parts[5]); // reason_canceled
-        assertInteger(parts[6]); // reason_constraints_not_satisfied
-        assertInteger(parts[7]); // reason_preempt
-        assertInteger(parts[8]); // reason_timeout
-        assertInteger(parts[9]); // reason_device_idle
+
+        // Values for each of JOB_STOP_REASON_CODES.
+        for (int i = 5; i < parts.length; i++) {
+            assertInteger(parts[i]);
+        }
     }
 
     private void checkJobsDeferred(String[] parts) {
@@ -575,7 +580,7 @@
     }
 
     private void checkDataConnection(String[] parts) {
-        assertEquals(26, parts.length);
+        assertEquals(27, parts.length);
         assertInteger(parts[4]);  // none
         assertInteger(parts[5]);  // gprs
         assertInteger(parts[6]);  // edge
@@ -597,7 +602,8 @@
         assertInteger(parts[22]); // iwlan
         assertInteger(parts[23]); // lte_ca
         assertInteger(parts[24]); // nr
-        assertInteger(parts[25]); // other
+        assertInteger(parts[25]); // emngcy
+        assertInteger(parts[26]); // other
     }
 
     private void checkWifiState(String[] parts) {
diff --git a/hostsidetests/incident/OWNERS b/hostsidetests/incident/OWNERS
index 2d6787a..1165b42 100644
--- a/hostsidetests/incident/OWNERS
+++ b/hostsidetests/incident/OWNERS
@@ -1,2 +1,5 @@
 # Bug component: 329246
-joeo@google.com
\ No newline at end of file
+joeo@google.com
+jreck@google.com
+kwekua@google.com
+yamasani@google.com
diff --git a/hostsidetests/incident/src/com/android/server/cts/ActivityManagerIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/ActivityManagerIncidentTest.java
index 342dc7f..acf1bd4 100644
--- a/hostsidetests/incident/src/com/android/server/cts/ActivityManagerIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/ActivityManagerIncidentTest.java
@@ -97,7 +97,7 @@
         assertTrue(activeServices.getServicesByUsersCount() > 0);
 
         for (ServicesByUser perUserServices : activeServices.getServicesByUsersList()) {
-            assertTrue(perUserServices.getServiceRecordsCount() > 0);
+            assertTrue(perUserServices.getServiceRecordsCount() >= 0);
             for (ServiceRecordProto service : perUserServices.getServiceRecordsList()) {
                 assertFalse(service.getShortName().isEmpty());
                 assertFalse(service.getPackageName().isEmpty());
diff --git a/hostsidetests/incident/src/com/android/server/cts/AlarmManagerIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/AlarmManagerIncidentTest.java
index 35d1bb6..8093dc3 100644
--- a/hostsidetests/incident/src/com/android/server/cts/AlarmManagerIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/AlarmManagerIncidentTest.java
@@ -23,8 +23,8 @@
 import com.android.server.BroadcastStatsProto;
 import com.android.server.ConstantsProto;
 import com.android.server.FilterStatsProto;
-import com.android.server.ForceAppStandbyTrackerProto;
-import com.android.server.ForceAppStandbyTrackerProto.RunAnyInBackgroundRestrictedPackages;
+import com.android.server.AppStateTrackerProto;
+import com.android.server.AppStateTrackerProto.RunAnyInBackgroundRestrictedPackages;
 import com.android.server.IdleDispatchEntryProto;
 import com.android.server.InFlightProto;
 import com.android.server.WakeupEventProto;
@@ -58,19 +58,19 @@
         assertTrue(0 < settings.getAllowWhileIdleLongDurationMs());
         assertTrue(0 < settings.getAllowWhileIdleWhitelistDurationMs());
 
-        // ForceAppStandbyTrackerProto
-        ForceAppStandbyTrackerProto forceAppStandbyTracker = dump.getForceAppStandbyTracker();
-        for (int uid : forceAppStandbyTracker.getForegroundUidsList()) {
+        // AppStateTrackerProto
+        AppStateTrackerProto appStateTracker = dump.getAppStateTracker();
+        for (int uid : appStateTracker.getForegroundUidsList()) {
             // 0 is technically a valid UID.
             assertTrue(0 <= uid);
         }
-        for (int aid : forceAppStandbyTracker.getPowerSaveWhitelistAppIdsList()) {
+        for (int aid : appStateTracker.getPowerSaveWhitelistAppIdsList()) {
             assertTrue(0 <= aid);
         }
-        for (int aid : forceAppStandbyTracker.getTempPowerSaveWhitelistAppIdsList()) {
+        for (int aid : appStateTracker.getTempPowerSaveWhitelistAppIdsList()) {
             assertTrue(0 <= aid);
         }
-        for (RunAnyInBackgroundRestrictedPackages r : forceAppStandbyTracker.getRunAnyInBackgroundRestrictedPackagesList()) {
+        for (RunAnyInBackgroundRestrictedPackages r : appStateTracker.getRunAnyInBackgroundRestrictedPackagesList()) {
             assertTrue(0 <= r.getUid());
         }
 
diff --git a/hostsidetests/incident/src/com/android/server/cts/JobSchedulerIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/JobSchedulerIncidentTest.java
index 9c8363c..1c2a1fe 100644
--- a/hostsidetests/incident/src/com/android/server/cts/JobSchedulerIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/JobSchedulerIncidentTest.java
@@ -69,7 +69,7 @@
         for (JobSchedulerServiceDumpProto.PendingJob pj : dump.getPendingJobsList()) {
             testJobStatusShortInfoProto(pj.getInfo(), filterLevel);
             testJobStatusDumpProto(pj.getDump());
-            assertTrue(0 <= pj.getEnqueuedDurationMs());
+            assertTrue(0 <= pj.getPendingDurationMs());
         }
 
         for (JobSchedulerServiceDumpProto.ActiveJob aj : dump.getActiveJobsList()) {
@@ -109,10 +109,6 @@
         assertTrue(0 <= c.getMaxWorkRescheduleCount());
         assertTrue(0 <= c.getMinLinearBackoffTimeMs());
         assertTrue(0 <= c.getMinExpBackoffTimeMs());
-        assertTrue(0 <= c.getStandbyHeartbeatTimeMs());
-        for (int sb : c.getStandbyBeatsList()) {
-            assertTrue(0 <= sb);
-        }
     }
 
     private static void testDataSetProto(DataSetProto ds) throws Exception {
@@ -186,7 +182,8 @@
         assertTrue(0 <= ji.getTriggerContentMaxDelayMs());
         testNetworkRequestProto(ji.getRequiredNetwork());
         // JobInfo.NETWORK_BYTES_UNKNOWN (= -1) is a valid value.
-        assertTrue(-1 <= ji.getTotalNetworkBytes());
+        assertTrue(-1 <= ji.getTotalNetworkDownloadBytes());
+        assertTrue(-1 <= ji.getTotalNetworkUploadBytes());
         assertTrue(0 <= ji.getMinLatencyMs());
         assertTrue(0 <= ji.getMaxExecutionDelayMs());
         JobStatusDumpProto.JobInfo.Backoff bp = ji.getBackoffPolicy();
diff --git a/hostsidetests/media/OWNERS b/hostsidetests/media/OWNERS
index 9884b18..8d6b291e 100644
--- a/hostsidetests/media/OWNERS
+++ b/hostsidetests/media/OWNERS
@@ -1,4 +1,4 @@
-rachad@google.com
+# Bug component: 1344
 elaurent@google.com
 lajos@google.com
 marcone@google.com
diff --git a/hostsidetests/multiuser/OWNERS b/hostsidetests/multiuser/OWNERS
new file mode 100644
index 0000000..2b344eb
--- /dev/null
+++ b/hostsidetests/multiuser/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 71510
+include /tests/app/OWNERS
+
+bookatz@google.com
diff --git a/hostsidetests/net/AndroidTest.xml b/hostsidetests/net/AndroidTest.xml
index dbff179..5479c51 100644
--- a/hostsidetests/net/AndroidTest.xml
+++ b/hostsidetests/net/AndroidTest.xml
@@ -31,4 +31,9 @@
         <option name="jar" value="CtsHostsideNetworkTests.jar" />
         <option name="runtime-hint" value="3m56s" />
     </test>
+
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="directory-keys" value="/sdcard/CtsHostsideNetworkTests" />
+        <option name="collect-on-run-ended-only" value="true" />
+    </metrics_collector>
 </configuration>
diff --git a/hostsidetests/net/app/Android.bp b/hostsidetests/net/app/Android.bp
index d66b71b..e988ea4 100644
--- a/hostsidetests/net/app/Android.bp
+++ b/hostsidetests/net/app/Android.bp
@@ -20,6 +20,8 @@
     //sdk_version: "current",
     platform_apis: true,
     static_libs: [
+        "androidx.test.rules",
+        "androidx.test.ext.junit",
         "compatibility-device-util-axt",
         "ctstestrunner-axt",
         "ub-uiautomator",
diff --git a/hostsidetests/net/app/AndroidManifest.xml b/hostsidetests/net/app/AndroidManifest.xml
index f54b5c1..3b00713 100644
--- a/hostsidetests/net/app/AndroidManifest.xml
+++ b/hostsidetests/net/app/AndroidManifest.xml
@@ -26,8 +26,9 @@
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 
-    <application>
+    <application android:requestLegacyExternalStorage="true" >
         <uses-library android:name="android.test.runner" />
         <activity android:name=".MyActivity" />
         <service android:name=".MyVpnService"
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
index 55bd406..d06ab13 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
@@ -16,20 +16,27 @@
 
 package com.android.cts.net.hostside;
 
+import static com.android.cts.net.hostside.Property.APP_STANDBY_MODE;
+import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+
+import static org.junit.Assert.assertEquals;
+
 import android.os.SystemClock;
-import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
 
 /**
  * Base class for metered and non-metered tests on idle apps.
  */
+@RequiredProperties({APP_STANDBY_MODE})
 abstract class AbstractAppIdleTestCase extends AbstractRestrictBackgroundNetworkTestCase {
 
-    @Override
-    protected final void setUp() throws Exception {
+    @Before
+    public final void setUp() throws Exception {
         super.setUp();
 
-        if (!isSupported()) return;
-
         // Set initial state.
         removePowerSaveModeWhitelist(TEST_APP2_PKG);
         removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
@@ -39,41 +46,16 @@
         registerBroadcastReceiver();
     }
 
-    @Override
-    protected final void tearDown() throws Exception {
+    @After
+    public final void tearDown() throws Exception {
         super.tearDown();
 
-        if (!isSupported()) return;
-
-        try {
-            tearDownMeteredNetwork();
-        } finally {
-            turnBatteryOff();
-            setAppIdle(false);
-        }
+        turnBatteryOff();
+        setAppIdle(false);
     }
 
-    @Override
-    protected boolean isSupported() throws Exception {
-        boolean supported = isDozeModeEnabled();
-        if (!supported) {
-            Log.i(TAG, "Skipping " + getClass() + "." + getName()
-                    + "() because device does not support Doze Mode");
-        }
-        return supported;
-    }
-
-    /**
-     * Resets the (non) metered network state.
-     *
-     * <p>By default is empty - it's up to subclasses to override.
-     */
-    protected void tearDownMeteredNetwork() throws Exception {
-    }
-
+    @Test
     public void testBackgroundNetworkAccess_enabled() throws Exception {
-        if (!isSupported()) return;
-
         setAppIdle(true);
         assertBackgroundNetworkAccess(false);
 
@@ -98,9 +80,8 @@
         assertBackgroundNetworkAccess(false);
     }
 
+    @Test
     public void testBackgroundNetworkAccess_whitelisted() throws Exception {
-        if (!isSupported()) return;
-
         setAppIdle(true);
         assertBackgroundNetworkAccess(false);
 
@@ -127,9 +108,8 @@
         assertBackgroundNetworkAccess(false);
     }
 
+    @Test
     public void testBackgroundNetworkAccess_tempWhitelisted() throws Exception {
-        if (!isSupported()) return;
-
         setAppIdle(true);
         assertBackgroundNetworkAccess(false);
 
@@ -140,23 +120,17 @@
         assertBackgroundNetworkAccess(false);
     }
 
+    @Test
     public void testBackgroundNetworkAccess_disabled() throws Exception {
-        if (!isSupported()) return;
-
         assertBackgroundNetworkAccess(true);
 
         assertsForegroundAlwaysHasNetworkAccess();
         assertBackgroundNetworkAccess(true);
     }
 
+    @RequiredProperties({BATTERY_SAVER_MODE})
+    @Test
     public void testAppIdleNetworkAccess_whenCharging() throws Exception {
-        if (!isBatterySaverSupported()) {
-            Log.i(TAG, "Skipping " + getClass() + "." + getName()
-                    + "() because device does not support Battery saver mode");
-            return;
-        }
-        if (!isSupported()) return;
-
         // Check that app is paroled when charging
         setAppIdle(true);
         assertBackgroundNetworkAccess(false);
@@ -180,9 +154,8 @@
         assertBackgroundNetworkAccess(true);
     }
 
+    @Test
     public void testAppIdleNetworkAccess_idleWhitelisted() throws Exception {
-        if (!isSupported()) return;
-
         setAppIdle(true);
         assertAppIdle(true);
         assertBackgroundNetworkAccess(false);
@@ -199,9 +172,8 @@
         removeAppIdleWhitelist(mUid + 1);
     }
 
+    @Test
     public void testAppIdle_toast() throws Exception {
-        if (!isSupported()) return;
-
         setAppIdle(true);
         assertAppIdle(true);
         assertEquals("Shown", showToast());
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
index 931376b..04d054d 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
@@ -16,20 +16,22 @@
 
 package com.android.cts.net.hostside;
 
-import android.text.TextUtils;
-import android.util.Log;
+import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
 
 /**
  * Base class for metered and non-metered Battery Saver Mode tests.
  */
+@RequiredProperties({BATTERY_SAVER_MODE})
 abstract class AbstractBatterySaverModeTestCase extends AbstractRestrictBackgroundNetworkTestCase {
 
-    @Override
-    protected final void setUp() throws Exception {
+    @Before
+    public final void setUp() throws Exception {
         super.setUp();
 
-        if (!isSupported()) return;
-
         // Set initial state.
         removePowerSaveModeWhitelist(TEST_APP2_PKG);
         removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
@@ -38,55 +40,15 @@
         registerBroadcastReceiver();
     }
 
-    @Override
-    protected final void tearDown() throws Exception {
+    @After
+    public final void tearDown() throws Exception {
         super.tearDown();
 
-        if (!isSupported()) return;
-
-        try {
-            tearDownMeteredNetwork();
-        } finally {
-            setBatterySaverMode(false);
-        }
+        setBatterySaverMode(false);
     }
 
-    @Override
-    protected boolean isSupported() throws Exception {
-        String unSupported = "";
-        if (!isDozeModeEnabled()) {
-            unSupported += "Doze mode,";
-        }
-        if (!isBatterySaverSupported()) {
-            unSupported += "Battery saver mode,";
-        }
-        if (!TextUtils.isEmpty(unSupported)) {
-            Log.i(TAG, "Skipping " + getClass() + "." + getName()
-                    + "() because device does not support " + unSupported);
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Sets the initial (non) metered network state.
-     *
-     * <p>By default is empty - it's up to subclasses to override.
-     */
-    protected void setUpMeteredNetwork() throws Exception {
-    }
-
-    /**
-     * Resets the (non) metered network state.
-     *
-     * <p>By default is empty - it's up to subclasses to override.
-     */
-    protected void tearDownMeteredNetwork() throws Exception {
-    }
-
+    @Test
     public void testBackgroundNetworkAccess_enabled() throws Exception {
-        if (!isSupported()) return;
-
         setBatterySaverMode(true);
         assertBackgroundNetworkAccess(false);
 
@@ -118,9 +80,8 @@
         assertBackgroundNetworkAccess(false);
     }
 
+    @Test
     public void testBackgroundNetworkAccess_whitelisted() throws Exception {
-        if (!isSupported()) return;
-
         setBatterySaverMode(true);
         assertBackgroundNetworkAccess(false);
 
@@ -140,9 +101,8 @@
         assertBackgroundNetworkAccess(false);
     }
 
+    @Test
     public void testBackgroundNetworkAccess_disabled() throws Exception {
-        if (!isSupported()) return;
-
         assertBackgroundNetworkAccess(true);
 
         assertsForegroundAlwaysHasNetworkAccess();
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
index f20f1d1..6f32c56 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
@@ -16,20 +16,25 @@
 
 package com.android.cts.net.hostside;
 
+import static com.android.cts.net.hostside.Property.DOZE_MODE;
+import static com.android.cts.net.hostside.Property.NOT_LOW_RAM_DEVICE;
+
 import android.os.SystemClock;
-import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
 
 /**
  * Base class for metered and non-metered Doze Mode tests.
  */
+@RequiredProperties({DOZE_MODE})
 abstract class AbstractDozeModeTestCase extends AbstractRestrictBackgroundNetworkTestCase {
 
-    @Override
-    protected final void setUp() throws Exception {
+    @Before
+    public final void setUp() throws Exception {
         super.setUp();
 
-        if (!isSupported()) return;
-
         // Set initial state.
         removePowerSaveModeWhitelist(TEST_APP2_PKG);
         removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
@@ -38,48 +43,15 @@
         registerBroadcastReceiver();
     }
 
-    @Override
-    protected final void tearDown() throws Exception {
+    @After
+    public final void tearDown() throws Exception {
         super.tearDown();
 
-        if (!isSupported()) return;
-
-        try {
-            tearDownMeteredNetwork();
-        } finally {
-            setDozeMode(false);
-        }
+        setDozeMode(false);
     }
 
-    @Override
-    protected boolean isSupported() throws Exception {
-        boolean supported = isDozeModeEnabled();
-        if (!supported) {
-            Log.i(TAG, "Skipping " + getClass() + "." + getName()
-                    + "() because device does not support Doze Mode");
-        }
-        return supported;
-    }
-
-    /**
-     * Sets the initial (non) metered network state.
-     *
-     * <p>By default is empty - it's up to subclasses to override.
-     */
-    protected void setUpMeteredNetwork() throws Exception {
-    }
-
-    /**
-     * Resets the (non) metered network state.
-     *
-     * <p>By default is empty - it's up to subclasses to override.
-     */
-    protected void tearDownMeteredNetwork() throws Exception {
-    }
-
+    @Test
     public void testBackgroundNetworkAccess_enabled() throws Exception {
-        if (!isSupported()) return;
-
         setDozeMode(true);
         assertBackgroundNetworkAccess(false);
 
@@ -96,9 +68,8 @@
         assertBackgroundNetworkAccess(false);
     }
 
+    @Test
     public void testBackgroundNetworkAccess_whitelisted() throws Exception {
-        if (!isSupported()) return;
-
         setDozeMode(true);
         assertBackgroundNetworkAccess(false);
 
@@ -118,19 +89,18 @@
         assertBackgroundNetworkAccess(false);
     }
 
+    @Test
     public void testBackgroundNetworkAccess_disabled() throws Exception {
-        if (!isSupported()) return;
-
         assertBackgroundNetworkAccess(true);
 
         assertsForegroundAlwaysHasNetworkAccess();
         assertBackgroundNetworkAccess(true);
     }
 
+    @RequiredProperties({NOT_LOW_RAM_DEVICE})
+    @Test
     public void testBackgroundNetworkAccess_enabledButWhitelistedOnNotificationAction()
             throws Exception {
-        if (!isSupported() || isLowRamDevice()) return;
-
         setPendingIntentWhitelistDuration(NETWORK_TIMEOUT_MS);
         try {
             registerNotificationListenerService();
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index 40d7e34..57b7bb4 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -17,14 +17,22 @@
 package com.android.cts.net.hostside;
 
 import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
-import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
-import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
-import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
 import static android.os.BatteryManager.BATTERY_PLUGGED_AC;
 import static android.os.BatteryManager.BATTERY_PLUGGED_USB;
 import static android.os.BatteryManager.BATTERY_PLUGGED_WIRELESS;
 
-import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.executeShellCommand;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getConnectivityManager;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getContext;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getInstrumentation;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getWifiManager;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isDozeModeSupported;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.restrictBackgroundValueToString;
+
+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.app.ActivityManager;
 import android.app.Instrumentation;
@@ -34,9 +42,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkInfo.State;
 import android.net.wifi.WifiManager;
@@ -44,24 +50,27 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.provider.Settings;
 import android.service.notification.NotificationListenerService;
-import android.test.InstrumentationTestCase;
-import android.text.TextUtils;
 import android.util.Log;
 
-import com.android.compatibility.common.util.BatteryUtils;
+import org.junit.Rule;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
 /**
  * Superclass for tests related to background network restrictions.
  */
-abstract class AbstractRestrictBackgroundNetworkTestCase extends InstrumentationTestCase {
-    protected static final String TAG = "RestrictBackgroundNetworkTests";
+@RunWith(AndroidJUnit4.class)
+public abstract class AbstractRestrictBackgroundNetworkTestCase {
+    public static final String TAG = "RestrictBackgroundNetworkTests";
 
     protected static final String TEST_PKG = "com.android.cts.net.hostside";
     protected static final String TEST_APP2_PKG = "com.android.cts.net.hostside.app2";
@@ -98,8 +107,6 @@
     static final int NETWORK_TIMEOUT_MS = 15 * SECOND_IN_MS;
     private static int PROCESS_STATE_FOREGROUND_SERVICE;
 
-    private static final int PROCESS_STATE_TOP = 2;
-
     private static final String KEY_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
 
     protected static final int TYPE_COMPONENT_ACTIVTIY = 0;
@@ -126,22 +133,23 @@
     protected WifiManager mWfm;
     protected int mUid;
     private int mMyUid;
-    private String mMeteredWifi;
     private MyServiceClient mServiceClient;
     private String mDeviceIdleConstantsSetting;
-    private boolean mSupported;
     private boolean mIsLocationOn;
 
-    @Override
+    @Rule
+    public final RuleChain mRuleChain = RuleChain.outerRule(new DumpOnFailureRule())
+            .around(new RequiredPropertiesRule())
+            .around(new MeterednessConfigurationRule());
+
     protected void setUp() throws Exception {
-        super.setUp();
 
         PROCESS_STATE_FOREGROUND_SERVICE = (Integer) ActivityManager.class
                 .getDeclaredField("PROCESS_STATE_FOREGROUND_SERVICE").get(null);
         mInstrumentation = getInstrumentation();
-        mContext = mInstrumentation.getContext();
-        mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-        mWfm = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+        mContext = getContext();
+        mCm = getConnectivityManager();
+        mWfm = getWifiManager();
         mUid = getUid(TEST_APP2_PKG);
         mMyUid = getUid(mContext.getPackageName());
         mServiceClient = new MyServiceClient(mContext);
@@ -151,10 +159,9 @@
         if (!mIsLocationOn) {
             enableLocation();
         }
-        mSupported = setUpActiveNetworkMeteringState();
         setAppIdle(false);
 
-        Log.i(TAG, "Apps status on " + getName() + ":\n"
+        Log.i(TAG, "Apps status:\n"
                 + "\ttest app: uid=" + mMyUid + ", state=" + getProcessStateByUid(mMyUid) + "\n"
                 + "\tapp2: uid=" + mUid + ", state=" + getProcessStateByUid(mUid));
 
@@ -165,16 +172,13 @@
         final String currentConstants =
                 executeShellCommand("settings get global app_idle_constants");
         assertEquals(appIdleConstants, currentConstants);
-   }
+    }
 
-    @Override
     protected void tearDown() throws Exception {
         if (!mIsLocationOn) {
             disableLocation();
         }
         mServiceClient.unbind();
-
-        super.tearDown();
     }
 
     private void enableLocation() throws Exception {
@@ -259,23 +263,8 @@
     protected void assertRestrictBackgroundStatus(int expectedStatus) throws Exception {
         final String status = mServiceClient.getRestrictBackgroundStatus();
         assertNotNull("didn't get API status from app2", status);
-        final String actualStatus = toString(Integer.parseInt(status));
-        assertEquals("wrong status", toString(expectedStatus), actualStatus);
-    }
-
-    protected void assertMyRestrictBackgroundStatus(int expectedStatus) throws Exception {
-        final int actualStatus = mCm.getRestrictBackgroundStatus();
-        assertEquals("Wrong status", toString(expectedStatus), toString(actualStatus));
-    }
-
-    protected boolean isMyRestrictBackgroundStatus(int expectedStatus) throws Exception {
-        final int actualStatus = mCm.getRestrictBackgroundStatus();
-        if (expectedStatus != actualStatus) {
-            Log.d(TAG, "Expected: " + toString(expectedStatus)
-                    + " but actual: " + toString(actualStatus));
-            return false;
-        }
-        return true;
+        assertEquals(restrictBackgroundValueToString(expectedStatus),
+                restrictBackgroundValueToString(Integer.parseInt(status)));
     }
 
     protected void assertBackgroundNetworkAccess(boolean expectAllowed) throws Exception {
@@ -298,28 +287,6 @@
     }
 
     /**
-     * Whether this device suport this type of test.
-     *
-     * <p>Should be overridden when necessary (but always calling
-     * {@code super.isSupported()} first), and explicitly used before each test
-     * Example:
-     *
-     * <pre><code>
-     * public void testSomething() {
-     *    if (!isSupported()) return;
-     * </code></pre>
-     *
-     * @return {@code true} by default.
-     */
-    protected boolean isSupported() throws Exception {
-        return mSupported;
-    }
-
-    protected boolean isBatterySaverSupported() {
-        return BatteryUtils.isBatterySaverSupported();
-    }
-
-    /**
      * Asserts that an app always have access while on foreground or running a foreground service.
      *
      * <p>This method will launch an activity and a foreground service to make the assertion, but
@@ -388,23 +355,6 @@
     }
 
     /**
-     * As per CDD requirements, if the device doesn't support data saver mode then
-     * ConnectivityManager.getRestrictBackgroundStatus() will always return
-     * RESTRICT_BACKGROUND_STATUS_DISABLED. So, enable the data saver mode and check if
-     * ConnectivityManager.getRestrictBackgroundStatus() for an app in background returns
-     * RESTRICT_BACKGROUND_STATUS_DISABLED or not.
-     */
-    protected boolean isDataSaverSupported() throws Exception {
-        assertMyRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
-        try {
-            setRestrictBackground(true);
-            return !isMyRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
-        } finally {
-            setRestrictBackground(false);
-        }
-    }
-
-    /**
      * Returns whether an app state should be considered "background" for restriction purposes.
      */
     protected boolean isBackground(int state) {
@@ -443,40 +393,10 @@
             // Exponential back-off.
             timeoutMs = Math.min(timeoutMs*2, NETWORK_TIMEOUT_MS);
         }
-        dumpOnFailure();
         fail("Invalid state for expectAvailable=" + expectAvailable + " after " + maxTries
                 + " attempts.\nLast error: " + error);
     }
 
-    private void dumpOnFailure() throws Exception {
-        dumpAllNetworkRules();
-        Log.d(TAG, "Usagestats dump: " + getUsageStatsDump());
-        executeShellCommand("settings get global app_idle_constants");
-    }
-
-    private void dumpAllNetworkRules() throws Exception {
-        final String networkManagementDump = runShellCommand(mInstrumentation,
-                "dumpsys network_management").trim();
-        final String networkPolicyDump = runShellCommand(mInstrumentation,
-                "dumpsys netpolicy").trim();
-        TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter('\n');
-        splitter.setString(networkManagementDump);
-        String next;
-        Log.d(TAG, ">>> Begin network_management dump");
-        while (splitter.hasNext()) {
-            next = splitter.next();
-            Log.d(TAG, next);
-        }
-        Log.d(TAG, "<<< End network_management dump");
-        splitter.setString(networkPolicyDump);
-        Log.d(TAG, ">>> Begin netpolicy dump");
-        while (splitter.hasNext()) {
-            next = splitter.next();
-            Log.d(TAG, next);
-        }
-        Log.d(TAG, "<<< End netpolicy dump");
-    }
-
     /**
      * Checks whether the network is available as expected.
      *
@@ -528,22 +448,10 @@
         return errors.toString();
     }
 
-    protected boolean isLowRamDevice() {
-        final ActivityManager am = (ActivityManager) mContext.getSystemService(
-            Context.ACTIVITY_SERVICE);
-        return am.isLowRamDevice();
-    }
-
-    protected String executeShellCommand(String command) throws Exception {
-        final String result = runShellCommand(mInstrumentation, command).trim();
-        if (DEBUG) Log.d(TAG, "Command '" + command + "' returned '" + result + "'");
-        return result;
-    }
-
     /**
      * Runs a Shell command which is not expected to generate output.
      */
-    protected void executeSilentShellCommand(String command) throws Exception {
+    protected void executeSilentShellCommand(String command) {
         final String result = executeShellCommand(command);
         assertTrue("Command '" + command + "' failed: " + result, result.trim().isEmpty());
     }
@@ -572,10 +480,6 @@
         });
     }
 
-    protected void assertDelayedShellCommand(String command, ExpectResultChecker checker)
-            throws Exception {
-        assertDelayedShellCommand(command, 5, 1, checker);
-    }
     protected void assertDelayedShellCommand(String command, int maxTries, int napTimeSeconds,
             ExpectResultChecker checker) throws Exception {
         String result = "";
@@ -592,159 +496,6 @@
                 + " attempts. Last result: '" + result + "'");
     }
 
-    /**
-     * Sets the initial metering state for the active network.
-     *
-     * <p>It's called on setup and by default does nothing - it's up to the
-     * subclasses to override.
-     *
-     * @return whether the tests in the subclass are supported on this device.
-     */
-    protected boolean setUpActiveNetworkMeteringState() throws Exception {
-        return true;
-    }
-
-    /**
-     * Makes sure the active network is not metered.
-     *
-     * <p>If the device does not supoprt un-metered networks (for example if it
-     * only has cellular data but not wi-fi), it should return {@code false};
-     * otherwise, it should return {@code true} (or fail if the un-metered
-     * network could not be set).
-     *
-     * @return {@code true} if the network is now unmetered.
-     */
-    protected boolean setUnmeteredNetwork() throws Exception {
-        final NetworkInfo info = mCm.getActiveNetworkInfo();
-        assertNotNull("Could not get active network", info);
-        if (!mCm.isActiveNetworkMetered()) {
-            Log.d(TAG, "Active network is not metered: " + info);
-        } else if (info.getType() == ConnectivityManager.TYPE_WIFI) {
-            Log.i(TAG, "Setting active WI-FI network as not metered: " + info );
-            setWifiMeteredStatus(false);
-        } else {
-            Log.d(TAG, "Active network cannot be set to un-metered: " + info);
-            return false;
-        }
-        assertActiveNetworkMetered(false); // Sanity check.
-        return true;
-    }
-
-    /**
-     * Enables metering on the active network if supported.
-     *
-     * <p>If the device does not support metered networks it should return
-     * {@code false}; otherwise, it should return {@code true} (or fail if the
-     * metered network could not be set).
-     *
-     * @return {@code true} if the network is now metered.
-     */
-    protected boolean setMeteredNetwork() throws Exception {
-        final NetworkInfo info = mCm.getActiveNetworkInfo();
-        final boolean metered = mCm.isActiveNetworkMetered();
-        if (metered) {
-            Log.d(TAG, "Active network already metered: " + info);
-            return true;
-        } else if (info.getType() != ConnectivityManager.TYPE_WIFI) {
-            Log.w(TAG, "Active network does not support metering: " + info);
-            return false;
-        } else {
-            Log.w(TAG, "Active network not metered: " + info);
-        }
-        final String netId = setWifiMeteredStatus(true);
-
-        // Set flag so status is reverted on resetMeteredNetwork();
-        mMeteredWifi = netId;
-        // Sanity check.
-        assertWifiMeteredStatus(netId, true);
-        assertActiveNetworkMetered(true);
-        return true;
-    }
-
-    /**
-     * Resets the device metering state to what it was before the test started.
-     *
-     * <p>This reverts any metering changes made by {@code setMeteredNetwork}.
-     */
-    protected void resetMeteredNetwork() throws Exception {
-        if (mMeteredWifi != null) {
-            Log.i(TAG, "resetMeteredNetwork(): SID '" + mMeteredWifi
-                    + "' was set as metered by test case; resetting it");
-            setWifiMeteredStatus(mMeteredWifi, false);
-            assertActiveNetworkMetered(false); // Sanity check.
-        }
-    }
-
-    private void assertActiveNetworkMetered(boolean expected) throws Exception {
-        final int maxTries = 5;
-        NetworkInfo info = null;
-        for (int i = 1; i <= maxTries; i++) {
-            info = mCm.getActiveNetworkInfo();
-            if (info == null) {
-                Log.v(TAG, "No active network info on attempt #" + i
-                        + "; sleeping 1s before polling again");
-            } else if (mCm.isActiveNetworkMetered() != expected) {
-                Log.v(TAG, "Wrong metered status for active network " + info + "; expected="
-                        + expected + "; sleeping 1s before polling again");
-            } else {
-                break;
-            }
-            Thread.sleep(SECOND_IN_MS);
-        }
-        assertNotNull("No active network after " + maxTries + " attempts", info);
-        assertEquals("Wrong metered status for active network " + info, expected,
-                mCm.isActiveNetworkMetered());
-    }
-
-    private String setWifiMeteredStatus(boolean metered) throws Exception {
-        // We could call setWifiEnabled() here, but it might take sometime to be in a consistent
-        // state (for example, if one of the saved network is not properly authenticated), so it's
-        // better to let the hostside test take care of that.
-        assertTrue("wi-fi is disabled", mWfm.isWifiEnabled());
-        // TODO: if it's not guaranteed the device has wi-fi, we need to change the tests
-        // to make the actual verification of restrictions optional.
-        final String ssid = mWfm.getConnectionInfo().getSSID();
-        return setWifiMeteredStatus(ssid, metered);
-    }
-
-    private String setWifiMeteredStatus(String ssid, boolean metered) throws Exception {
-        assertNotNull("null SSID", ssid);
-        final String netId = ssid.trim().replaceAll("\"", ""); // remove quotes, if any.
-        assertFalse("empty SSID", ssid.isEmpty());
-
-        Log.i(TAG, "Setting wi-fi network " + netId + " metered status to " + metered);
-        final String setCommand = "cmd netpolicy set metered-network " + netId + " " + metered;
-        assertDelayedShellCommand(setCommand, "");
-
-        return netId;
-    }
-
-    private void assertWifiMeteredStatus(String netId, boolean status) throws Exception {
-        final String command = "cmd netpolicy list wifi-networks";
-        final String expectedLine = netId + ";" + status;
-        assertDelayedShellCommand(command, new ExpectResultChecker() {
-
-            @Override
-            public boolean isExpected(String result) {
-                return result.contains(expectedLine);
-            }
-
-            @Override
-            public String getExpected() {
-                return "line containing " + expectedLine;
-            }
-        });
-    }
-
-    protected void setRestrictBackground(boolean enabled) throws Exception {
-        executeShellCommand("cmd netpolicy set restrict-background " + enabled);
-        final String output = executeShellCommand("cmd netpolicy get restrict-background ");
-        final String expectedSuffix = enabled ? "enabled" : "disabled";
-        // TODO: use MoreAsserts?
-        assertTrue("output '" + output + "' should end with '" + expectedSuffix + "'",
-                output.endsWith(expectedSuffix));
-      }
-
     protected void addRestrictBackgroundWhitelist(int uid) throws Exception {
         executeShellCommand("cmd netpolicy add restrict-background-whitelist " + uid);
         assertRestrictBackgroundWhitelist(uid, true);
@@ -924,7 +675,7 @@
 
     protected void setDozeMode(boolean enabled) throws Exception {
         // Sanity check, since tests should check beforehand....
-        assertTrue("Device does not support Doze Mode", isDozeModeEnabled());
+        assertTrue("Device does not support Doze Mode", isDozeModeSupported());
 
         Log.i(TAG, "Setting Doze Mode to " + enabled);
         if (enabled) {
@@ -944,43 +695,16 @@
         assertDelayedShellCommand("dumpsys deviceidle get deep", enabled ? "IDLE" : "ACTIVE");
     }
 
-    protected boolean isDozeModeEnabled() throws Exception {
-        final String result = executeShellCommand("cmd deviceidle enabled deep").trim();
-        return result.equals("1");
-    }
-
     protected void setAppIdle(boolean enabled) throws Exception {
         Log.i(TAG, "Setting app idle to " + enabled);
         executeSilentShellCommand("am set-inactive " + TEST_APP2_PKG + " " + enabled );
         assertAppIdle(enabled); // Sanity check
     }
 
-    private String getUsageStatsDump() throws Exception {
-        final String output = runShellCommand(mInstrumentation, "dumpsys usagestats").trim();
-        final StringBuilder sb = new StringBuilder();
-        final TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter('\n');
-        splitter.setString(output);
-        String str;
-        while (splitter.hasNext()) {
-            str = splitter.next();
-            if (str.contains("package=")
-                    && !str.contains(TEST_PKG) && !str.contains(TEST_APP2_PKG)) {
-                continue;
-            }
-            if (str.trim().startsWith("config=") || str.trim().startsWith("time=")) {
-                continue;
-            }
-            sb.append(str).append('\n');
-        }
-        return sb.toString();
-    }
-
     protected void assertAppIdle(boolean enabled) throws Exception {
         try {
             assertDelayedShellCommand("am get-inactive " + TEST_APP2_PKG, 15, 2, "Idle=" + enabled);
         } catch (Throwable e) {
-            Log.d(TAG, "UsageStats dump:\n" + getUsageStatsDump());
-            executeShellCommand("settings get global app_idle_constants");
             throw e;
         }
     }
@@ -1061,12 +785,10 @@
                         // App didn't come to foreground when the activity is started, so try again.
                         assertForegroundNetworkAccess();
                     } else {
-                        dumpOnFailure();
                         fail("Network is not available for app2 (" + mUid + "): " + errors[0]);
                     }
                 }
             } else {
-                dumpOnFailure();
                 fail("Timed out waiting for network availability status from app2 (" + mUid + ")");
             }
         } else {
@@ -1150,19 +872,6 @@
         }
     }
 
-    private String toString(int status) {
-        switch (status) {
-            case RESTRICT_BACKGROUND_STATUS_DISABLED:
-                return "DISABLED";
-            case RESTRICT_BACKGROUND_STATUS_WHITELISTED:
-                return "WHITELISTED";
-            case RESTRICT_BACKGROUND_STATUS_ENABLED:
-                return "ENABLED";
-            default:
-                return "UNKNOWN_STATUS_" + status;
-        }
-    }
-
     private ProcessState getProcessStateByUid(int uid) throws Exception {
         return new ProcessState(executeShellCommand("cmd activity get-uid-state " + uid));
     }
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java
index 622d993..f1858d6 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java
@@ -16,15 +16,8 @@
 
 package com.android.cts.net.hostside;
 
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+
+@RequiredProperties({METERED_NETWORK})
 public class AppIdleMeteredTest extends AbstractAppIdleTestCase {
-
-    @Override
-    protected boolean setUpActiveNetworkMeteringState() throws Exception {
-        return setMeteredNetwork();
-    }
-
-    @Override
-    protected void tearDownMeteredNetwork() throws Exception {
-        resetMeteredNetwork();
-    }
 }
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java
index bde71f9..e737a6d 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java
@@ -16,9 +16,8 @@
 
 package com.android.cts.net.hostside;
 
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+
+@RequiredProperties({NON_METERED_NETWORK})
 public class AppIdleNonMeteredTest extends AbstractAppIdleTestCase {
-    @Override
-    protected boolean setUpActiveNetworkMeteringState() throws Exception {
-        return setUnmeteredNetwork();
-    }
 }
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java
index 3071cfe..c78ca2e 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java
@@ -16,15 +16,8 @@
 
 package com.android.cts.net.hostside;
 
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+
+@RequiredProperties({METERED_NETWORK})
 public class BatterySaverModeMeteredTest extends AbstractBatterySaverModeTestCase {
-
-    @Override
-    protected boolean setUpActiveNetworkMeteringState() throws Exception {
-        return setMeteredNetwork();
-    }
-
-    @Override
-    protected void tearDownMeteredNetwork() throws Exception {
-        resetMeteredNetwork();
-    }
 }
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java
index 6d3076f..fb52a54 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java
@@ -16,10 +16,9 @@
 
 package com.android.cts.net.hostside;
 
-public class BatterySaverModeNonMeteredTest extends AbstractBatterySaverModeTestCase {
 
-    @Override
-    protected boolean setUpActiveNetworkMeteringState() throws Exception {
-        return setUnmeteredNetwork();
-    }
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+
+@RequiredProperties({NON_METERED_NETWORK})
+public class BatterySaverModeNonMeteredTest extends AbstractBatterySaverModeTestCase {
 }
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/DataSaverModeTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
index cfe6a73..aa2c914 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
@@ -20,24 +20,33 @@
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
 
-import android.util.Log;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+import static com.android.cts.net.hostside.Property.NO_DATA_SAVER_MODE;
+
+import static org.junit.Assert.fail;
 
 import com.android.compatibility.common.util.CddTest;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import androidx.test.filters.LargeTest;
+
+@RequiredProperties({DATA_SAVER_MODE, METERED_NETWORK})
+@LargeTest
 public class DataSaverModeTest extends AbstractRestrictBackgroundNetworkTestCase {
 
     private static final String[] REQUIRED_WHITELISTED_PACKAGES = {
         "com.android.providers.downloads"
     };
 
-    private boolean mIsDataSaverSupported;
-
-    @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
 
-        mIsDataSaverSupported = isDataSaverSupported();
-
         // Set initial state.
         setRestrictBackground(false);
         removeRestrictBackgroundWhitelist(mUid);
@@ -47,36 +56,15 @@
         assertRestrictBackgroundChangedReceived(0);
    }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void tearDown() throws Exception {
         super.tearDown();
 
-        if (!isSupported()) return;
-
-        try {
-            resetMeteredNetwork();
-        } finally {
-            setRestrictBackground(false);
-        }
+        setRestrictBackground(false);
     }
 
-    @Override
-    protected boolean setUpActiveNetworkMeteringState() throws Exception {
-        return setMeteredNetwork();
-    }
-
-    @Override
-    protected boolean isSupported() throws Exception {
-        if (!mIsDataSaverSupported) {
-            Log.i(TAG, "Skipping " + getClass() + "." + getName()
-                    + "() because device does not support Data Saver Mode");
-        }
-        return mIsDataSaverSupported && super.isSupported();
-    }
-
+    @Test
     public void testGetRestrictBackgroundStatus_disabled() throws Exception {
-        if (!isSupported()) return;
-
         assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
 
         // Sanity check: make sure status is always disabled, never whitelisted
@@ -88,9 +76,8 @@
         assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
     }
 
+    @Test
     public void testGetRestrictBackgroundStatus_whitelisted() throws Exception {
-        if (!isSupported()) return;
-
         setRestrictBackground(true);
         assertRestrictBackgroundChangedReceived(1);
         assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
@@ -107,9 +94,8 @@
         assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
     }
 
+    @Test
     public void testGetRestrictBackgroundStatus_enabled() throws Exception {
-        if (!isSupported()) return;
-
         setRestrictBackground(true);
         assertRestrictBackgroundChangedReceived(1);
         assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
@@ -142,9 +128,8 @@
         assertBackgroundNetworkAccess(false);
     }
 
+    @Test
     public void testGetRestrictBackgroundStatus_blacklisted() throws Exception {
-        if (!isSupported()) return;
-
         addRestrictBackgroundBlacklist(mUid);
         assertRestrictBackgroundChangedReceived(1);
         assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
@@ -180,9 +165,8 @@
         assertsForegroundAlwaysHasNetworkAccess();
     }
 
+    @Test
     public void testGetRestrictBackgroundStatus_requiredWhitelistedPackages() throws Exception {
-        if (!isSupported()) return;
-
         final StringBuilder error = new StringBuilder();
         for (String packageName : REQUIRED_WHITELISTED_PACKAGES) {
             int uid = -1;
@@ -202,10 +186,10 @@
         }
     }
 
+    @RequiredProperties({NO_DATA_SAVER_MODE})
     @CddTest(requirement="7.4.7/C-2-2")
+    @Test
     public void testBroadcastNotSentOnUnsupportedDevices() throws Exception {
-        if (isSupported()) return;
-
         setRestrictBackground(true);
         assertRestrictBackgroundChangedReceived(0);
 
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java
index e4189af..4306c99 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java
@@ -16,15 +16,8 @@
 
 package com.android.cts.net.hostside;
 
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+
+@RequiredProperties({METERED_NETWORK})
 public class DozeModeMeteredTest extends AbstractDozeModeTestCase {
-
-    @Override
-    protected boolean setUpActiveNetworkMeteringState() throws Exception {
-        return setMeteredNetwork();
-    }
-
-    @Override
-    protected void tearDownMeteredNetwork() throws Exception {
-        resetMeteredNetwork();
-    }
 }
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java
index edbbb9e..1e89f15 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java
@@ -16,10 +16,8 @@
 
 package com.android.cts.net.hostside;
 
-public class DozeModeNonMeteredTest extends AbstractDozeModeTestCase {
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
 
-    @Override
-    protected boolean setUpActiveNetworkMeteringState() throws Exception {
-        return setUnmeteredNetwork();
-    }
+@RequiredProperties({NON_METERED_NETWORK})
+public class DozeModeNonMeteredTest extends AbstractDozeModeTestCase {
 }
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
new file mode 100644
index 0000000..cedd62a
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
@@ -0,0 +1,91 @@
+/*
+ * 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.net.hostside;
+
+import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
+import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TEST_PKG;
+
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import com.android.compatibility.common.util.OnFailureRule;
+
+import org.junit.AssumptionViolatedException;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+public class DumpOnFailureRule extends OnFailureRule {
+    private File mDumpDir = new File(Environment.getExternalStorageDirectory(),
+            "CtsHostsideNetworkTests");
+
+    @Override
+    public void onTestFailure(Statement base, Description description, Throwable throwable) {
+        final String testName = description.getClassName() + "_" + description.getMethodName();
+
+        if (throwable instanceof AssumptionViolatedException) {
+            Log.d(TAG, "Skipping test " + testName + ": " + throwable);
+            return;
+        }
+
+        prepareDumpRootDir();
+        final File dumpFile = new File(mDumpDir, "dump-" + testName);
+        Log.i(TAG, "Dumping debug info for " + description + ": " + dumpFile.getPath());
+        try (FileOutputStream out = new FileOutputStream(dumpFile)) {
+            for (String cmd : new String[] {
+                    "dumpsys netpolicy",
+                    "dumpsys network_management",
+                    "dumpsys usagestats " + TEST_PKG,
+                    "dumpsys usagestats appstandby",
+            }) {
+                dumpCommandOutput(out, cmd);
+            }
+        } catch (FileNotFoundException e) {
+            Log.e(TAG, "Error opening file: " + dumpFile, e);
+        } catch (IOException e) {
+            Log.e(TAG, "Error closing file: " + dumpFile, e);
+        }
+    }
+
+    void dumpCommandOutput(FileOutputStream out, String cmd) {
+        final ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation()
+                .getUiAutomation().executeShellCommand(cmd);
+        try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+            out.write(("Output of '" + cmd + "':\n").getBytes(StandardCharsets.UTF_8));
+            FileUtils.copy(in, out);
+            out.write("\n\n=================================================================\n\n"
+                    .getBytes(StandardCharsets.UTF_8));
+        } catch (IOException e) {
+            Log.e(TAG, "Error dumping '" + cmd + "'", e);
+        }
+    }
+
+    void prepareDumpRootDir() {
+        if (!mDumpDir.exists() && !mDumpDir.mkdir()) {
+            Log.e(TAG, "Error creating " + mDumpDir);
+        }
+    }
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/MeterednessConfigurationRule.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/MeterednessConfigurationRule.java
new file mode 100644
index 0000000..8fadf9e
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/MeterednessConfigurationRule.java
@@ -0,0 +1,60 @@
+/*
+ * 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.net.hostside;
+
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.resetMeteredNetwork;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setupMeteredNetwork;
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+
+import android.util.ArraySet;
+import android.util.Pair;
+
+import com.android.compatibility.common.util.BeforeAfterRule;
+
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+public class MeterednessConfigurationRule extends BeforeAfterRule {
+    private Pair<String, Boolean> mSsidAndInitialMeteredness;
+
+    @Override
+    public void onBefore(Statement base, Description description) throws Throwable {
+        final ArraySet<Property> requiredProperties
+                = RequiredPropertiesRule.getRequiredProperties();
+        if (requiredProperties.contains(METERED_NETWORK)) {
+            configureNetworkMeteredness(true);
+        } else if (requiredProperties.contains(NON_METERED_NETWORK)) {
+            configureNetworkMeteredness(false);
+        }
+    }
+
+    @Override
+    public void onAfter(Statement base, Description description) throws Throwable {
+        resetNetworkMeteredness();
+    }
+
+    public void configureNetworkMeteredness(boolean metered) throws Exception {
+        mSsidAndInitialMeteredness = setupMeteredNetwork(metered);
+    }
+
+    public void resetNetworkMeteredness() throws Exception {
+        if (mSsidAndInitialMeteredness != null) {
+            resetMeteredNetwork(mSsidAndInitialMeteredness.first,
+                    mSsidAndInitialMeteredness.second);
+        }
+    }
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java
index b1a2186..c9edda6 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java
@@ -15,9 +15,21 @@
  */
 package com.android.cts.net.hostside;
 
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.net.hostside.Property.APP_STANDBY_MODE;
+import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.DOZE_MODE;
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+
 import android.os.SystemClock;
 import android.util.Log;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 /**
  * Test cases for the more complex scenarios where multiple restrictions (like Battery Saver Mode
  * and Data Saver Mode) are applied simultaneously.
@@ -29,12 +41,10 @@
 public class MixedModesTest extends AbstractRestrictBackgroundNetworkTestCase {
     private static final String TAG = "MixedModesTest";
 
-    @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
 
-        if (!isSupported()) return;
-
         // Set initial state.
         removeRestrictBackgroundWhitelist(mUid);
         removeRestrictBackgroundBlacklist(mUid);
@@ -44,12 +54,10 @@
         registerBroadcastReceiver();
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void tearDown() throws Exception {
         super.tearDown();
 
-        if (!isSupported()) return;
-
         try {
             setRestrictBackground(false);
         } finally {
@@ -57,34 +65,15 @@
         }
     }
 
-    @Override
-    public boolean isSupported() throws Exception {
-        if (!isDozeModeEnabled()) {
-            Log.i(TAG, "Skipping " + getClass() + "." + getName()
-                    + "() because device does not support Doze Mode");
-            return false;
-        }
-        return true;
-    }
-
     /**
      * Tests all DS ON and BS ON scenarios from network-policy-restrictions.md on metered networks.
      */
+    @RequiredProperties({DATA_SAVER_MODE, BATTERY_SAVER_MODE, METERED_NETWORK})
+    @Test
     public void testDataAndBatterySaverModes_meteredNetwork() throws Exception {
-        if (!isBatterySaverSupported()) {
-            Log.i(TAG, "Skipping " + getClass() + "." + getName()
-                    + "() because device does not support Battery saver mode");
-            return;
-        }
-        if (!isSupported()) return;
-
-        Log.i(TAG, "testDataAndBatterySaverModes_meteredNetwork() tests");
-        if (!setMeteredNetwork()) {
-            Log.w(TAG, "testDataAndBatterySaverModes_meteredNetwork() skipped because "
-                    + "device cannot use a metered network");
-            return;
-        }
-
+        final MeterednessConfigurationRule meterednessConfiguration
+                = new MeterednessConfigurationRule();
+        meterednessConfiguration.configureNetworkMeteredness(true);
         try {
             setRestrictBackground(true);
             setBatterySaverMode(true);
@@ -137,7 +126,7 @@
             removeRestrictBackgroundBlacklist(mUid);
             removePowerSaveModeWhitelist(TEST_APP2_PKG);
         } finally {
-            resetMeteredNetwork();
+            meterednessConfiguration.resetNetworkMeteredness();
         }
     }
 
@@ -145,86 +134,75 @@
      * Tests all DS ON and BS ON scenarios from network-policy-restrictions.md on non-metered
      * networks.
      */
+    @RequiredProperties({DATA_SAVER_MODE, BATTERY_SAVER_MODE, NON_METERED_NETWORK})
+    @Test
     public void testDataAndBatterySaverModes_nonMeteredNetwork() throws Exception {
-        if (!isBatterySaverSupported()) {
-            Log.i(TAG, "Skipping " + getClass() + "." + getName()
-                    + "() because device does not support Battery saver mode");
-            return;
+        final MeterednessConfigurationRule meterednessConfiguration
+                = new MeterednessConfigurationRule();
+        meterednessConfiguration.configureNetworkMeteredness(false);
+        try {
+            setRestrictBackground(true);
+            setBatterySaverMode(true);
+
+            Log.v(TAG, "Not whitelisted for any.");
+            assertBackgroundNetworkAccess(false);
+            assertsForegroundAlwaysHasNetworkAccess();
+            assertBackgroundNetworkAccess(false);
+
+            Log.v(TAG, "Whitelisted for Data Saver but not for Battery Saver.");
+            addRestrictBackgroundWhitelist(mUid);
+            removePowerSaveModeWhitelist(TEST_APP2_PKG);
+            assertBackgroundNetworkAccess(false);
+            assertsForegroundAlwaysHasNetworkAccess();
+            assertBackgroundNetworkAccess(false);
+            removeRestrictBackgroundWhitelist(mUid);
+
+            Log.v(TAG, "Whitelisted for Battery Saver but not for Data Saver.");
+            addPowerSaveModeWhitelist(TEST_APP2_PKG);
+            removeRestrictBackgroundWhitelist(mUid);
+            assertBackgroundNetworkAccess(true);
+            assertsForegroundAlwaysHasNetworkAccess();
+            assertBackgroundNetworkAccess(true);
+            removePowerSaveModeWhitelist(TEST_APP2_PKG);
+
+            Log.v(TAG, "Whitelisted for both.");
+            addRestrictBackgroundWhitelist(mUid);
+            addPowerSaveModeWhitelist(TEST_APP2_PKG);
+            assertBackgroundNetworkAccess(true);
+            assertsForegroundAlwaysHasNetworkAccess();
+            assertBackgroundNetworkAccess(true);
+            removePowerSaveModeWhitelist(TEST_APP2_PKG);
+            assertBackgroundNetworkAccess(false);
+            removeRestrictBackgroundWhitelist(mUid);
+
+            Log.v(TAG, "Blacklisted for Data Saver, not whitelisted for Battery Saver.");
+            addRestrictBackgroundBlacklist(mUid);
+            removePowerSaveModeWhitelist(TEST_APP2_PKG);
+            assertBackgroundNetworkAccess(false);
+            assertsForegroundAlwaysHasNetworkAccess();
+            assertBackgroundNetworkAccess(false);
+            removeRestrictBackgroundBlacklist(mUid);
+
+            Log.v(TAG, "Blacklisted for Data Saver, whitelisted for Battery Saver.");
+            addRestrictBackgroundBlacklist(mUid);
+            addPowerSaveModeWhitelist(TEST_APP2_PKG);
+            assertBackgroundNetworkAccess(true);
+            assertsForegroundAlwaysHasNetworkAccess();
+            assertBackgroundNetworkAccess(true);
+            removeRestrictBackgroundBlacklist(mUid);
+            removePowerSaveModeWhitelist(TEST_APP2_PKG);
+        } finally {
+            meterednessConfiguration.resetNetworkMeteredness();
         }
-        if (!isSupported()) return;
-
-        if (!setUnmeteredNetwork()) {
-            Log.w(TAG, "testDataAndBatterySaverModes_nonMeteredNetwork() skipped because network"
-                    + " is metered");
-            return;
-        }
-        Log.i(TAG, "testDataAndBatterySaverModes_nonMeteredNetwork() tests");
-        setRestrictBackground(true);
-        setBatterySaverMode(true);
-
-        Log.v(TAG, "Not whitelisted for any.");
-        assertBackgroundNetworkAccess(false);
-        assertsForegroundAlwaysHasNetworkAccess();
-        assertBackgroundNetworkAccess(false);
-
-        Log.v(TAG, "Whitelisted for Data Saver but not for Battery Saver.");
-        addRestrictBackgroundWhitelist(mUid);
-        removePowerSaveModeWhitelist(TEST_APP2_PKG);
-        assertBackgroundNetworkAccess(false);
-        assertsForegroundAlwaysHasNetworkAccess();
-        assertBackgroundNetworkAccess(false);
-        removeRestrictBackgroundWhitelist(mUid);
-
-        Log.v(TAG, "Whitelisted for Battery Saver but not for Data Saver.");
-        addPowerSaveModeWhitelist(TEST_APP2_PKG);
-        removeRestrictBackgroundWhitelist(mUid);
-        assertBackgroundNetworkAccess(true);
-        assertsForegroundAlwaysHasNetworkAccess();
-        assertBackgroundNetworkAccess(true);
-        removePowerSaveModeWhitelist(TEST_APP2_PKG);
-
-        Log.v(TAG, "Whitelisted for both.");
-        addRestrictBackgroundWhitelist(mUid);
-        addPowerSaveModeWhitelist(TEST_APP2_PKG);
-        assertBackgroundNetworkAccess(true);
-        assertsForegroundAlwaysHasNetworkAccess();
-        assertBackgroundNetworkAccess(true);
-        removePowerSaveModeWhitelist(TEST_APP2_PKG);
-        assertBackgroundNetworkAccess(false);
-        removeRestrictBackgroundWhitelist(mUid);
-
-        Log.v(TAG, "Blacklisted for Data Saver, not whitelisted for Battery Saver.");
-        addRestrictBackgroundBlacklist(mUid);
-        removePowerSaveModeWhitelist(TEST_APP2_PKG);
-        assertBackgroundNetworkAccess(false);
-        assertsForegroundAlwaysHasNetworkAccess();
-        assertBackgroundNetworkAccess(false);
-        removeRestrictBackgroundBlacklist(mUid);
-
-        Log.v(TAG, "Blacklisted for Data Saver, whitelisted for Battery Saver.");
-        addRestrictBackgroundBlacklist(mUid);
-        addPowerSaveModeWhitelist(TEST_APP2_PKG);
-        assertBackgroundNetworkAccess(true);
-        assertsForegroundAlwaysHasNetworkAccess();
-        assertBackgroundNetworkAccess(true);
-        removeRestrictBackgroundBlacklist(mUid);
-        removePowerSaveModeWhitelist(TEST_APP2_PKG);
     }
 
     /**
      * Tests that powersave whitelists works as expected when doze and battery saver modes
      * are enabled.
      */
+    @RequiredProperties({DOZE_MODE, BATTERY_SAVER_MODE})
+    @Test
     public void testDozeAndBatterySaverMode_powerSaveWhitelists() throws Exception {
-        if (!isBatterySaverSupported()) {
-            Log.i(TAG, "Skipping " + getClass() + "." + getName()
-                    + "() because device does not support Battery saver mode");
-            return;
-        }
-        if (!isSupported()) {
-            return;
-        }
-
         setBatterySaverMode(true);
         setDozeMode(true);
 
@@ -250,11 +228,9 @@
      * Tests that powersave whitelists works as expected when doze and appIdle modes
      * are enabled.
      */
+    @RequiredProperties({DOZE_MODE, APP_STANDBY_MODE})
+    @Test
     public void testDozeAndAppIdle_powerSaveWhitelists() throws Exception {
-        if (!isSupported()) {
-            return;
-        }
-
         setDozeMode(true);
         setAppIdle(true);
 
@@ -276,11 +252,9 @@
         }
     }
 
+    @RequiredProperties({APP_STANDBY_MODE, DOZE_MODE})
+    @Test
     public void testAppIdleAndDoze_tempPowerSaveWhitelists() throws Exception {
-        if (!isSupported()) {
-            return;
-        }
-
         setDozeMode(true);
         setAppIdle(true);
 
@@ -299,16 +273,9 @@
         }
     }
 
+    @RequiredProperties({APP_STANDBY_MODE, BATTERY_SAVER_MODE})
+    @Test
     public void testAppIdleAndBatterySaver_tempPowerSaveWhitelists() throws Exception {
-        if (!isBatterySaverSupported()) {
-            Log.i(TAG, "Skipping " + getClass() + "." + getName()
-                    + "() because device does not support Battery saver mode");
-            return;
-        }
-        if (!isSupported()) {
-            return;
-        }
-
         setBatterySaverMode(true);
         setAppIdle(true);
 
@@ -330,11 +297,9 @@
     /**
      * Tests that the app idle whitelist works as expected when doze and appIdle mode are enabled.
      */
+    @RequiredProperties({DOZE_MODE, APP_STANDBY_MODE})
+    @Test
     public void testDozeAndAppIdle_appIdleWhitelist() throws Exception {
-        if (!isSupported()) {
-            return;
-        }
-
         setDozeMode(true);
         setAppIdle(true);
 
@@ -353,11 +318,9 @@
         }
     }
 
+    @RequiredProperties({APP_STANDBY_MODE, DOZE_MODE})
+    @Test
     public void testAppIdleAndDoze_tempPowerSaveAndAppIdleWhitelists() throws Exception {
-        if (!isSupported()) {
-            return;
-        }
-
         setDozeMode(true);
         setAppIdle(true);
 
@@ -380,16 +343,9 @@
         }
     }
 
+    @RequiredProperties({APP_STANDBY_MODE, BATTERY_SAVER_MODE})
+    @Test
     public void testAppIdleAndBatterySaver_tempPowerSaveAndAppIdleWhitelists() throws Exception {
-        if (!isBatterySaverSupported()) {
-            Log.i(TAG, "Skipping " + getClass() + "." + getName()
-                    + "() because device does not support Battery saver mode");
-            return;
-        }
-        if (!isSupported()) {
-            return;
-        }
-
         setBatterySaverMode(true);
         setAppIdle(true);
 
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
index 24dde9d..ed397b9 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
@@ -16,15 +16,26 @@
 
 package com.android.cts.net.hostside;
 
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
 import android.net.Network;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import java.util.Objects;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
 public class NetworkCallbackTest extends AbstractRestrictBackgroundNetworkTestCase {
 
-    private boolean mIsDataSaverSupported;
     private Network mNetwork;
     private final TestNetworkCallback mTestNetworkCallback = new TestNetworkCallback();
 
@@ -132,108 +143,122 @@
         }
     }
 
-    @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
 
-        mIsDataSaverSupported = isDataSaverSupported();
-
         mNetwork = mCm.getActiveNetwork();
 
-        // Set initial state.
-        setBatterySaverMode(false);
         registerBroadcastReceiver();
 
-        if (!mIsDataSaverSupported) return;
-        setRestrictBackground(false);
         removeRestrictBackgroundWhitelist(mUid);
         removeRestrictBackgroundBlacklist(mUid);
         assertRestrictBackgroundChangedReceived(0);
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void tearDown() throws Exception {
         super.tearDown();
 
-        if (!mIsDataSaverSupported) return;
+        setRestrictBackground(false);
+        setBatterySaverMode(false);
+    }
 
+    @RequiredProperties({DATA_SAVER_MODE})
+    @Test
+    public void testOnBlockedStatusChanged_dataSaver() throws Exception {
+        // Initial state
+        setBatterySaverMode(false);
+        setRestrictBackground(false);
+
+        final MeterednessConfigurationRule meterednessConfiguration
+                = new MeterednessConfigurationRule();
+        meterednessConfiguration.configureNetworkMeteredness(true);
         try {
-            resetMeteredNetwork();
+            // Register callback
+            registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
+            mTestNetworkCallback.expectAvailableCallback(mNetwork);
+            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+
+            // Enable restrict background
+            setRestrictBackground(true);
+            assertBackgroundNetworkAccess(false);
+            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+
+            // Add to whitelist
+            addRestrictBackgroundWhitelist(mUid);
+            assertBackgroundNetworkAccess(true);
+            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+
+            // Remove from whitelist
+            removeRestrictBackgroundWhitelist(mUid);
+            assertBackgroundNetworkAccess(false);
+            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
         } finally {
+            meterednessConfiguration.resetNetworkMeteredness();
+        }
+
+        // Set to non-metered network
+        meterednessConfiguration.configureNetworkMeteredness(false);
+        try {
+            assertBackgroundNetworkAccess(true);
+            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+
+            // Disable restrict background, should not trigger callback
             setRestrictBackground(false);
+            assertBackgroundNetworkAccess(true);
+            mTestNetworkCallback.assertNoCallback();
+        } finally {
+            meterednessConfiguration.resetNetworkMeteredness();
         }
     }
 
-    public void testOnBlockedStatusChanged_data_saver() throws Exception {
-        if (!mIsDataSaverSupported) return;
-
-        // Prepare metered wifi
-        if (!setMeteredNetwork()) return;
-
-        // Register callback
-        registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
-        mTestNetworkCallback.expectAvailableCallback(mNetwork);
-        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
-        // Enable restrict background
-        setRestrictBackground(true);
-        assertBackgroundNetworkAccess(false);
-        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
-
-        // Add to whitelist
-        addRestrictBackgroundWhitelist(mUid);
-        assertBackgroundNetworkAccess(true);
-        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
-        // Remove from whitelist
-        removeRestrictBackgroundWhitelist(mUid);
-        assertBackgroundNetworkAccess(false);
-        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
-
-        // Set to non-metered network
-        setUnmeteredNetwork();
-        assertBackgroundNetworkAccess(true);
-        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
-        // Disable restrict background, should not trigger callback
+    @RequiredProperties({BATTERY_SAVER_MODE})
+    @Test
+    public void testOnBlockedStatusChanged_powerSaver() throws Exception {
+        // Set initial state.
+        setBatterySaverMode(false);
         setRestrictBackground(false);
-        assertBackgroundNetworkAccess(true);
-        mTestNetworkCallback.assertNoCallback();
-    }
 
+        final MeterednessConfigurationRule meterednessConfiguration
+                = new MeterednessConfigurationRule();
+        meterednessConfiguration.configureNetworkMeteredness(true);
+        try {
+            // Register callback
+            registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
+            mTestNetworkCallback.expectAvailableCallback(mNetwork);
+            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
 
-    public void testOnBlockedStatusChanged_power_saver() throws Exception {
-        // Prepare metered wifi
-        if (!setMeteredNetwork()) return;
+            // Enable Power Saver
+            setBatterySaverMode(true);
+            assertBackgroundNetworkAccess(false);
+            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
 
-        // Register callback
-        registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
-        mTestNetworkCallback.expectAvailableCallback(mNetwork);
-        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
-        // Enable Power Saver
-        setBatterySaverMode(true);
-        assertBackgroundNetworkAccess(false);
-        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
-
-        // Disable Power Saver
-        setBatterySaverMode(false);
-        assertBackgroundNetworkAccess(true);
-        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+            // Disable Power Saver
+            setBatterySaverMode(false);
+            assertBackgroundNetworkAccess(true);
+            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+        } finally {
+            meterednessConfiguration.resetNetworkMeteredness();
+        }
 
         // Set to non-metered network
-        setUnmeteredNetwork();
-        mTestNetworkCallback.assertNoCallback();
+        meterednessConfiguration.configureNetworkMeteredness(false);
+        try {
+            mTestNetworkCallback.assertNoCallback();
 
-        // Enable Power Saver
-        setBatterySaverMode(true);
-        assertBackgroundNetworkAccess(false);
-        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+            // Enable Power Saver
+            setBatterySaverMode(true);
+            assertBackgroundNetworkAccess(false);
+            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
 
-        // Disable Power Saver
-        setBatterySaverMode(false);
-        assertBackgroundNetworkAccess(true);
-        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+            // Disable Power Saver
+            setBatterySaverMode(false);
+            assertBackgroundNetworkAccess(true);
+            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+        } finally {
+            meterednessConfiguration.resetNetworkMeteredness();
+        }
     }
 
     // TODO: 1. test against VPN lockdown.
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
new file mode 100644
index 0000000..ca2864c
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
@@ -0,0 +1,255 @@
+/*
+ * 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.net.hostside;
+
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.ActivityManager;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.wifi.WifiManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.compatibility.common.util.AppStandbyUtils;
+import com.android.compatibility.common.util.BatteryUtils;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+public class NetworkPolicyTestUtils {
+
+    private static final int TIMEOUT_CHANGE_METEREDNESS_MS = 5000;
+
+    private static ConnectivityManager mCm;
+    private static WifiManager mWm;
+
+    private static Boolean mBatterySaverSupported;
+    private static Boolean mDataSaverSupported;
+    private static Boolean mDozeModeSupported;
+    private static Boolean mAppStandbySupported;
+
+    private NetworkPolicyTestUtils() {}
+
+    public static boolean isBatterySaverSupported() {
+        if (mBatterySaverSupported == null) {
+            mBatterySaverSupported = BatteryUtils.isBatterySaverSupported();
+        }
+        return mBatterySaverSupported;
+    }
+
+    /**
+     * As per CDD requirements, if the device doesn't support data saver mode then
+     * ConnectivityManager.getRestrictBackgroundStatus() will always return
+     * RESTRICT_BACKGROUND_STATUS_DISABLED. So, enable the data saver mode and check if
+     * ConnectivityManager.getRestrictBackgroundStatus() for an app in background returns
+     * RESTRICT_BACKGROUND_STATUS_DISABLED or not.
+     */
+    public static boolean isDataSaverSupported() {
+        if (mDataSaverSupported == null) {
+            assertMyRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
+            try {
+                setRestrictBackground(true);
+                mDataSaverSupported = !isMyRestrictBackgroundStatus(
+                        RESTRICT_BACKGROUND_STATUS_DISABLED);
+            } finally {
+                setRestrictBackground(false);
+            }
+        }
+        return mDataSaverSupported;
+    }
+
+    public static boolean isDozeModeSupported() {
+        if (mDozeModeSupported == null) {
+            final String result = executeShellCommand("cmd deviceidle enabled deep");
+            mDozeModeSupported = result.equals("1");
+        }
+        return mDozeModeSupported;
+    }
+
+    public static boolean isAppStandbySupported() {
+        if (mAppStandbySupported == null) {
+            mAppStandbySupported = AppStandbyUtils.isAppStandbyEnabled();
+        }
+        return mAppStandbySupported;
+    }
+
+    public static boolean isLowRamDevice() {
+        final ActivityManager am = (ActivityManager) getContext().getSystemService(
+                Context.ACTIVITY_SERVICE);
+        return am.isLowRamDevice();
+    }
+
+    public static boolean isActiveNetworkMetered(boolean metered) {
+        return getConnectivityManager().isActiveNetworkMetered() == metered;
+    }
+
+    public static boolean canChangeActiveNetworkMeteredness() {
+        final Network activeNetwork = getConnectivityManager().getActiveNetwork();
+        final NetworkCapabilities networkCapabilities
+                = getConnectivityManager().getNetworkCapabilities(activeNetwork);
+        return networkCapabilities.hasTransport(TRANSPORT_WIFI);
+    }
+
+    public static Pair<String, Boolean> setupMeteredNetwork(boolean metered) throws Exception {
+        if (isActiveNetworkMetered(metered)) {
+            return null;
+        }
+        final String ssid = unquoteSSID(getWifiManager().getConnectionInfo().getSSID());
+        setWifiMeteredStatus(ssid, metered);
+        return Pair.create(ssid, !metered);
+    }
+
+    public static void resetMeteredNetwork(String ssid, boolean metered) throws Exception {
+        setWifiMeteredStatus(ssid, metered);
+    }
+
+    public static void setWifiMeteredStatus(String ssid, boolean metered) throws Exception {
+        assertFalse("SSID should not be empty", TextUtils.isEmpty(ssid));
+        final String cmd = "cmd netpolicy set metered-network " + ssid + " " + metered;
+        executeShellCommand(cmd);
+        assertWifiMeteredStatus(ssid, metered);
+        assertActiveNetworkMetered(metered);
+    }
+
+    public static void assertWifiMeteredStatus(String ssid, boolean expectedMeteredStatus) {
+        final String result = executeShellCommand("cmd netpolicy list wifi-networks");
+        final String expectedLine = ssid + ";" + expectedMeteredStatus;
+        assertTrue("Expected line: " + expectedLine + "; Actual result: " + result,
+                result.contains(expectedLine));
+    }
+
+    // Copied from cts/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
+    public static void assertActiveNetworkMetered(boolean expectedMeteredStatus) throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final NetworkCallback networkCallback = new NetworkCallback() {
+            @Override
+            public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
+                final boolean metered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED);
+                if (metered == expectedMeteredStatus) {
+                    latch.countDown();
+                }
+            }
+        };
+        // Registering a callback here guarantees onCapabilitiesChanged is called immediately
+        // with the current setting. Therefore, if the setting has already been changed,
+        // this method will return right away, and if not it will wait for the setting to change.
+        getConnectivityManager().registerDefaultNetworkCallback(networkCallback);
+        if (!latch.await(TIMEOUT_CHANGE_METEREDNESS_MS, TimeUnit.MILLISECONDS)) {
+            fail("Timed out waiting for active network metered status to change to "
+                    + expectedMeteredStatus + " ; network = "
+                    + getConnectivityManager().getActiveNetwork());
+        }
+        getConnectivityManager().unregisterNetworkCallback(networkCallback);
+    }
+
+    public static void setRestrictBackground(boolean enabled) {
+        executeShellCommand("cmd netpolicy set restrict-background " + enabled);
+        final String output = executeShellCommand("cmd netpolicy get restrict-background");
+        final String expectedSuffix = enabled ? "enabled" : "disabled";
+        assertTrue("output '" + output + "' should end with '" + expectedSuffix + "'",
+                output.endsWith(expectedSuffix));
+    }
+
+    public static boolean isMyRestrictBackgroundStatus(int expectedStatus) {
+        final int actualStatus = getConnectivityManager().getRestrictBackgroundStatus();
+        if (expectedStatus != actualStatus) {
+            Log.d(TAG, "MyRestrictBackgroundStatus: "
+                    + "Expected: " + restrictBackgroundValueToString(expectedStatus)
+                    + "; Actual: " + restrictBackgroundValueToString(actualStatus));
+            return false;
+        }
+        return true;
+    }
+
+    // Copied from cts/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
+    private static String unquoteSSID(String ssid) {
+        // SSID is returned surrounded by quotes if it can be decoded as UTF-8.
+        // Otherwise it's guaranteed not to start with a quote.
+        if (ssid.charAt(0) == '"') {
+            return ssid.substring(1, ssid.length() - 1);
+        } else {
+            return ssid;
+        }
+    }
+
+    public static String restrictBackgroundValueToString(int status) {
+        switch (status) {
+            case RESTRICT_BACKGROUND_STATUS_DISABLED:
+                return "DISABLED";
+            case RESTRICT_BACKGROUND_STATUS_WHITELISTED:
+                return "WHITELISTED";
+            case RESTRICT_BACKGROUND_STATUS_ENABLED:
+                return "ENABLED";
+            default:
+                return "UNKNOWN_STATUS_" + status;
+        }
+    }
+
+    public static String executeShellCommand(String command) {
+        final String result = runShellCommand(command).trim();
+        Log.d(TAG, "Output of '" + command + "': '" + result + "'");
+        return result;
+    }
+
+    public static void assertMyRestrictBackgroundStatus(int expectedStatus) {
+        final int actualStatus = getConnectivityManager().getRestrictBackgroundStatus();
+        assertEquals(restrictBackgroundValueToString(expectedStatus),
+                restrictBackgroundValueToString(actualStatus));
+    }
+
+    public static ConnectivityManager getConnectivityManager() {
+        if (mCm == null) {
+            mCm = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+        }
+        return mCm;
+    }
+
+    public static WifiManager getWifiManager() {
+        if (mWm == null) {
+            mWm = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
+        }
+        return mWm;
+    }
+
+    public static Context getContext() {
+        return getInstrumentation().getContext();
+    }
+
+    public static Instrumentation getInstrumentation() {
+        return InstrumentationRegistry.getInstrumentation();
+    }
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/Property.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/Property.java
new file mode 100644
index 0000000..18805f9
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/Property.java
@@ -0,0 +1,70 @@
+/*
+ * 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.net.hostside;
+
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.canChangeActiveNetworkMeteredness;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isActiveNetworkMetered;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isAppStandbySupported;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isBatterySaverSupported;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isDataSaverSupported;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isDozeModeSupported;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isLowRamDevice;
+
+public enum Property {
+    BATTERY_SAVER_MODE(1 << 0) {
+        public boolean isSupported() { return isBatterySaverSupported(); }
+    },
+
+    DATA_SAVER_MODE(1 << 1) {
+        public boolean isSupported() { return isDataSaverSupported(); }
+    },
+
+    NO_DATA_SAVER_MODE(~DATA_SAVER_MODE.getValue()) {
+        public boolean isSupported() { return !isDataSaverSupported(); }
+    },
+
+    DOZE_MODE(1 << 2) {
+        public boolean isSupported() { return isDozeModeSupported(); }
+    },
+
+    APP_STANDBY_MODE(1 << 3) {
+        public boolean isSupported() { return isAppStandbySupported(); }
+    },
+
+    NOT_LOW_RAM_DEVICE(1 << 4) {
+        public boolean isSupported() { return !isLowRamDevice(); }
+    },
+
+    METERED_NETWORK(1 << 5) {
+        public boolean isSupported() {
+            return isActiveNetworkMetered(true) || canChangeActiveNetworkMeteredness();
+        }
+    },
+
+    NON_METERED_NETWORK(~METERED_NETWORK.getValue()) {
+        public boolean isSupported() {
+            return isActiveNetworkMetered(false) || canChangeActiveNetworkMeteredness();
+        }
+    };
+
+    private int mValue;
+
+    Property(int value) { mValue = value; }
+
+    public int getValue() { return mValue; }
+
+    abstract boolean isSupported();
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/RequiredProperties.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/RequiredProperties.java
new file mode 100644
index 0000000..96838bb
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/RequiredProperties.java
@@ -0,0 +1,31 @@
+/*
+ * 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.net.hostside;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Retention(RUNTIME)
+@Target({METHOD, TYPE})
+@Inherited
+public @interface RequiredProperties {
+    Property[] value();
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/RequiredPropertiesRule.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/RequiredPropertiesRule.java
new file mode 100644
index 0000000..1e33320
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/RequiredPropertiesRule.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 com.android.cts.net.hostside;
+
+import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
+
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.compatibility.common.util.BeforeAfterRule;
+
+import org.junit.Assume;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class RequiredPropertiesRule extends BeforeAfterRule {
+
+    private static ArraySet<Property> mRequiredProperties;
+
+    @Override
+    public void onBefore(Statement base, Description description) {
+        mRequiredProperties = getAllRequiredProperties(description);
+
+        final String testName = description.getClassName() + "#" + description.getMethodName();
+        assertTestIsValid(testName, mRequiredProperties);
+        Log.i(TAG, "Running test " + testName + " with required properties: "
+                + propertiesToString(mRequiredProperties));
+    }
+
+    private ArraySet<Property> getAllRequiredProperties(Description description) {
+        final ArraySet<Property> allRequiredProperties = new ArraySet<>();
+        RequiredProperties requiredProperties = description.getAnnotation(RequiredProperties.class);
+        if (requiredProperties != null) {
+            Collections.addAll(allRequiredProperties, requiredProperties.value());
+        }
+
+        for (Class<?> clazz = description.getTestClass();
+                clazz != null; clazz = clazz.getSuperclass()) {
+            requiredProperties = clazz.getDeclaredAnnotation(RequiredProperties.class);
+            if (requiredProperties == null) {
+                continue;
+            }
+            for (Property requiredProperty : requiredProperties.value()) {
+                if (!allRequiredProperties.contains(~requiredProperty.getValue())) {
+                    allRequiredProperties.add(requiredProperty);
+                }
+            }
+        }
+        return allRequiredProperties;
+    }
+
+    private void assertTestIsValid(String testName, ArraySet<Property> requiredProperies) {
+        if (requiredProperies == null) {
+            return;
+        }
+        final ArrayList<Property> unsupportedProperties = new ArrayList<>();
+        for (Property property : requiredProperies) {
+            if (!property.isSupported()) {
+                unsupportedProperties.add(property);
+            }
+        }
+        Assume.assumeTrue("Unsupported properties: "
+                + propertiesToString(unsupportedProperties), unsupportedProperties.isEmpty());
+    }
+
+    public static ArraySet<Property> getRequiredProperties() {
+        return mRequiredProperties;
+    }
+
+    private static String propertiesToString(Iterable<Property> properties) {
+        return "[" + TextUtils.join(",", properties) + "]";
+    }
+}
diff --git a/hostsidetests/net/src/com/android/cts/net/HostsideNetworkCallbackTests.java b/hostsidetests/net/src/com/android/cts/net/HostsideNetworkCallbackTests.java
index 8d6c4ac..1312085 100644
--- a/hostsidetests/net/src/com/android/cts/net/HostsideNetworkCallbackTests.java
+++ b/hostsidetests/net/src/com/android/cts/net/HostsideNetworkCallbackTests.java
@@ -29,14 +29,14 @@
         uninstallPackage(TEST_APP2_PKG, true);
     }
 
-    public void testOnBlockedStatusChanged_data_saver() throws Exception {
+    public void testOnBlockedStatusChanged_dataSaver() throws Exception {
         runDeviceTests(TEST_PKG,
-                TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_data_saver");
+                TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_dataSaver");
     }
 
-    public void testOnBlockedStatusChanged_power_saver() throws Exception {
+    public void testOnBlockedStatusChanged_powerSaver() throws Exception {
         runDeviceTests(TEST_PKG,
-                TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_power_saver");
+                TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_powerSaver");
     }
 }
 
diff --git a/hostsidetests/net/src/com/android/cts/net/HostsideNetworkTestCase.java b/hostsidetests/net/src/com/android/cts/net/HostsideNetworkTestCase.java
index a2443b3..ce20379 100644
--- a/hostsidetests/net/src/com/android/cts/net/HostsideNetworkTestCase.java
+++ b/hostsidetests/net/src/com/android/cts/net/HostsideNetworkTestCase.java
@@ -79,7 +79,8 @@
     protected void installPackage(String apk) throws FileNotFoundException,
             DeviceNotAvailableException {
         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
-        assertNull(getDevice().installPackage(buildHelper.getTestFile(apk), false));
+        assertNull(getDevice().installPackage(buildHelper.getTestFile(apk),
+                false /* reinstall */, true /* grantPermissions */));
     }
 
     protected void uninstallPackage(String packageName, boolean shouldSucceed)
diff --git a/hostsidetests/numberblocking/OWNERS b/hostsidetests/numberblocking/OWNERS
new file mode 100644
index 0000000..0cfd686
--- /dev/null
+++ b/hostsidetests/numberblocking/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 151185
+tgunn@google.com
\ No newline at end of file
diff --git a/hostsidetests/numberblocking/src/com/android/cts/numberblocking/hostside/NumberBlockingTest.java b/hostsidetests/numberblocking/src/com/android/cts/numberblocking/hostside/NumberBlockingTest.java
index 1bf1aef..12b2dca 100644
--- a/hostsidetests/numberblocking/src/com/android/cts/numberblocking/hostside/NumberBlockingTest.java
+++ b/hostsidetests/numberblocking/src/com/android/cts/numberblocking/hostside/NumberBlockingTest.java
@@ -27,6 +27,8 @@
 import com.android.tradefed.testtype.IBuildReceiver;
 
 import java.io.File;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Multi-user tests for number blocking.
@@ -237,15 +239,15 @@
 
     // TODO: Replace this with API in ITestDevice once it is available.
     private int getUserSerialNumber(int userId) throws DeviceNotAvailableException {
+        Pattern pattern = Pattern.compile("^.*UserInfo\\{" + userId + "\\:.*serialNo=(\\d+).*$");
         // dumpsys user return lines like "UserInfo{0:Owner:13} serialNo=0"
         String commandOutput = getDevice().executeShellCommand("dumpsys user");
         String[] tokens = commandOutput.split("\\n");
         for (String token : tokens) {
             token = token.trim();
-            if (token.contains("UserInfo{" + userId + ":")) {
-                String[] split = token.split("serialNo=");
-                assertTrue(split.length == 2);
-                int serialNumber = Integer.parseInt(split[1]);
+            Matcher matcher = pattern.matcher(token);
+            if (matcher.matches()) {
+                int serialNumber = Integer.parseInt(matcher.group(1));
                 LogUtil.CLog.logAndDisplay(
                         Log.LogLevel.INFO,
                         String.format("Serial number of user %d : %d", userId, serialNumber));
diff --git a/hostsidetests/rollback/Android.bp b/hostsidetests/rollback/Android.bp
index c3dbe18..817a339 100644
--- a/hostsidetests/rollback/Android.bp
+++ b/hostsidetests/rollback/Android.bp
@@ -24,7 +24,7 @@
 android_test_helper_app {
     name: "CtsRollbackManagerHostTestHelperApp",
     srcs:  ["app/src/**/*.java"],
-    static_libs: ["androidx.test.rules", "cts-rollback-lib"],
+    static_libs: ["androidx.test.rules", "cts-rollback-lib", "cts-install-lib"],
     manifest : "app/AndroidManifest.xml",
     java_resources:  [
         ":StagedInstallTestApexV2",
diff --git a/hostsidetests/rollback/OWNERS b/hostsidetests/rollback/OWNERS
new file mode 100644
index 0000000..e84df8e
--- /dev/null
+++ b/hostsidetests/rollback/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 557916
+ruhler@google.com
+*
\ No newline at end of file
diff --git a/hostsidetests/rollback/app/AndroidManifest.xml b/hostsidetests/rollback/app/AndroidManifest.xml
index 2aac999..421ceff 100644
--- a/hostsidetests/rollback/app/AndroidManifest.xml
+++ b/hostsidetests/rollback/app/AndroidManifest.xml
@@ -18,7 +18,7 @@
           package="com.android.cts.rollback.host.app" >
 
     <application>
-        <receiver android:name="com.android.cts.rollback.lib.LocalIntentSender"
+        <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
                   android:exported="true" />
         <uses-library android:name="android.test.runner" />
     </application>
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 1e8040d..8baf21f 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
@@ -23,12 +23,11 @@
 import android.Manifest;
 import android.content.rollback.RollbackInfo;
 
-import androidx.test.InstrumentationRegistry;
-
-import com.android.cts.rollback.lib.Install;
+import com.android.cts.install.lib.Install;
+import com.android.cts.install.lib.InstallUtils;
+import com.android.cts.install.lib.TestApp;
 import com.android.cts.rollback.lib.Rollback;
-import com.android.cts.rollback.lib.TestApp;
-import com.android.cts.rollback.lib.Utils;
+import com.android.cts.rollback.lib.RollbackUtils;
 
 import org.junit.After;
 import org.junit.Before;
@@ -49,8 +48,7 @@
      */
     @Before
     public void setup() throws InterruptedException, IOException {
-        InstrumentationRegistry.getInstrumentation().getUiAutomation()
-                .adoptShellPermissionIdentity(
+        InstallUtils.adoptShellPermissionIdentity(
                     Manifest.permission.INSTALL_PACKAGES,
                     Manifest.permission.DELETE_PACKAGES,
                     Manifest.permission.TEST_MANAGE_ROLLBACKS);
@@ -61,8 +59,7 @@
      */
     @After
     public void teardown() throws InterruptedException, IOException {
-        InstrumentationRegistry.getInstrumentation().getUiAutomation()
-                .dropShellPermissionIdentity();
+        InstallUtils.dropShellPermissionIdentity();
     }
 
 
@@ -72,7 +69,7 @@
      */
     @Test
     public void testApkOnlyEnableRollback() throws Exception {
-        assertThat(Utils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
 
         Install.single(TestApp.A1).commit();
         Install.single(TestApp.A2).setStaged().setEnableRollback().commit();
@@ -88,15 +85,17 @@
      */
     @Test
     public void testApkOnlyCommitRollback() throws Exception {
-        assertThat(Utils.getInstalledVersion(TestApp.A)).isEqualTo(2);
-        RollbackInfo available = Utils.getAvailableRollback(TestApp.A);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+        InstallUtils.processUserData(TestApp.A);
+
+        RollbackInfo available = RollbackUtils.getAvailableRollback(TestApp.A);
         assertThat(available).isStaged();
         assertThat(available).packagesContainsExactly(
                 Rollback.from(TestApp.A2).to(TestApp.A1));
-        assertThat(Utils.getCommittedRollback(TestApp.A)).isNull();
+        assertThat(RollbackUtils.getCommittedRollback(TestApp.A)).isNull();
 
-        Utils.rollback(available.getRollbackId(), TestApp.A2);
-        RollbackInfo committed = Utils.getCommittedRollback(TestApp.A);
+        RollbackUtils.rollback(available.getRollbackId(), TestApp.A2);
+        RollbackInfo committed = RollbackUtils.getCommittedRollback(TestApp.A);
         assertThat(committed).hasRollbackId(available.getRollbackId());
         assertThat(committed).isStaged();
         assertThat(committed).packagesContainsExactly(
@@ -106,8 +105,8 @@
 
         // Note: The app is not rolled back until after the rollback is staged
         // and the device has been rebooted.
-        Utils.waitForSessionReady(committed.getCommittedSessionId());
-        assertThat(Utils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+        InstallUtils.waitForSessionReady(committed.getCommittedSessionId());
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
 
         // At this point, the host test driver will reboot the device and run
         // testApkOnlyConfirmRollback().
@@ -119,9 +118,10 @@
      */
     @Test
     public void testApkOnlyConfirmRollback() throws Exception {
-        assertThat(Utils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+        InstallUtils.processUserData(TestApp.A);
 
-        RollbackInfo committed = Utils.getCommittedRollback(TestApp.A);
+        RollbackInfo committed = RollbackUtils.getCommittedRollback(TestApp.A);
         assertThat(committed).isStaged();
         assertThat(committed).packagesContainsExactly(
                 Rollback.from(TestApp.A2).to(TestApp.A1));
@@ -133,12 +133,11 @@
      * Test rollbacks of staged installs involving only apex.
      * Install first version phase.
      *
-     * <p> We can't rollback to version 1, which is already installed, so we start by installing
-     * version 2. The test ultimately rolls back from 3 to 2.
+     * <p> We start by installing version 2. The test ultimately rolls back from 3 to 2.
      */
     @Test
     public void testApexOnlyInstallFirstVersion() throws Exception {
-        assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(1);
 
         Install.single(TestApp.Apex2).setStaged().commit();
 
@@ -152,7 +151,7 @@
      */
     @Test
     public void testApexOnlyEnableRollback() throws Exception {
-        assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
         Install.single(TestApp.Apex3).setStaged().setEnableRollback().commit();
 
         // At this point, the host test driver will reboot the device and run
@@ -165,14 +164,14 @@
      */
     @Test
     public void testApexOnlyCommitRollback() throws Exception {
-        assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(3);
-        RollbackInfo available = Utils.getAvailableRollback(TestApp.Apex);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(3);
+        RollbackInfo available = RollbackUtils.getAvailableRollback(TestApp.Apex);
         assertThat(available).isStaged();
         assertThat(available).packagesContainsExactly(
                 Rollback.from(TestApp.Apex3).to(TestApp.Apex2));
 
-        Utils.rollback(available.getRollbackId(), TestApp.Apex3);
-        RollbackInfo committed = Utils.getCommittedRollbackById(available.getRollbackId());
+        RollbackUtils.rollback(available.getRollbackId(), TestApp.Apex3);
+        RollbackInfo committed = RollbackUtils.getCommittedRollbackById(available.getRollbackId());
         assertThat(committed).isNotNull();
         assertThat(committed).isStaged();
         assertThat(committed).packagesContainsExactly(
@@ -182,8 +181,8 @@
 
         // Note: The app is not rolled back until after the rollback is staged
         // and the device has been rebooted.
-        Utils.waitForSessionReady(committed.getCommittedSessionId());
-        assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(3);
+        InstallUtils.waitForSessionReady(committed.getCommittedSessionId());
+        assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(3);
 
         // At this point, the host test driver will reboot the device and run
         // testApexOnlyConfirmRollback().
@@ -195,7 +194,7 @@
      */
     @Test
     public void testApexOnlyConfirmRollback() throws Exception {
-        assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
 
         // Rollback data for shim apex will remain in storage since the apex cannot be completely
         // removed and thus the rollback data won't be expired. Unfortunately, we can't also delete
@@ -204,6 +203,42 @@
         // At this point, the host test driver will reboot the device to complete the uninstall.
     }
 
+    /**
+     * Test rollback to system version involving apex only
+     */
+    @Test
+    public void testApexOnlySystemVersion_EnableRollback() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+        Install.single(TestApp.Apex2).setStaged().setEnableRollback().commit();
+    }
+
+    @Test
+    public void testApexOnlySystemVersion_CommitRollback() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+        RollbackInfo available = RollbackUtils.getAvailableRollback(TestApp.Apex);
+        assertThat(available).isStaged();
+        assertThat(available).packagesContainsExactly(
+                Rollback.from(TestApp.Apex2).to(TestApp.Apex1));
+
+        RollbackUtils.rollback(available.getRollbackId(), TestApp.Apex2);
+        RollbackInfo committed = RollbackUtils.getCommittedRollbackById(available.getRollbackId());
+        assertThat(committed).isNotNull();
+        assertThat(committed).isStaged();
+        assertThat(committed).packagesContainsExactly(
+                Rollback.from(TestApp.Apex2).to(TestApp.Apex1));
+        assertThat(committed).causePackagesContainsExactly(TestApp.Apex2);
+        assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1);
+
+        // Note: The app is not rolled back until after the rollback is staged
+        // and the device has been rebooted.
+        InstallUtils.waitForSessionReady(committed.getCommittedSessionId());
+        assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+    }
+
+    @Test
+    public void testApexOnlySystemVersion_ConfirmRollback() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+    }
 
     /**
      * Test rollbacks of staged installs involving apex and apk.
@@ -213,8 +248,8 @@
      */
     @Test
     public void testApexAndApkInstallFirstVersion() throws Exception {
-        assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(1);
-        assertThat(Utils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
 
         Install.multi(TestApp.Apex2, TestApp.A1).setStaged().commit();
 
@@ -228,8 +263,8 @@
      */
     @Test
     public void testApexAndApkEnableRollback() throws Exception {
-        assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
-        assertThat(Utils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
         Install.multi(TestApp.Apex3, TestApp.A2).setStaged().setEnableRollback().commit();
 
         // At this point, the host test driver will reboot the device and run
@@ -242,16 +277,18 @@
      */
     @Test
     public void testApexAndApkCommitRollback() throws Exception {
-        assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(3);
-        assertThat(Utils.getInstalledVersion(TestApp.A)).isEqualTo(2);
-        RollbackInfo available = Utils.getAvailableRollback(TestApp.Apex);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(3);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+        InstallUtils.processUserData(TestApp.A);
+
+        RollbackInfo available = RollbackUtils.getAvailableRollback(TestApp.Apex);
         assertThat(available).isStaged();
         assertThat(available).packagesContainsExactly(
                 Rollback.from(TestApp.Apex3).to(TestApp.Apex2),
                 Rollback.from(TestApp.A2).to(TestApp.A1));
 
-        Utils.rollback(available.getRollbackId(), TestApp.Apex3, TestApp.A2);
-        RollbackInfo committed = Utils.getCommittedRollback(TestApp.A);
+        RollbackUtils.rollback(available.getRollbackId(), TestApp.Apex3, TestApp.A2);
+        RollbackInfo committed = RollbackUtils.getCommittedRollback(TestApp.A);
         assertThat(committed).isNotNull();
         assertThat(committed).isStaged();
         assertThat(committed).packagesContainsExactly(
@@ -262,9 +299,9 @@
 
         // Note: The app is not rolled back until after the rollback is staged
         // and the device has been rebooted.
-        Utils.waitForSessionReady(committed.getCommittedSessionId());
-        assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(3);
-        assertThat(Utils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+        InstallUtils.waitForSessionReady(committed.getCommittedSessionId());
+        assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(3);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
 
         // At this point, the host test driver will reboot the device and run
         // testApexOnlyConfirmRollback().
@@ -276,9 +313,11 @@
      */
     @Test
     public void testApexAndApkConfirmRollback() throws Exception {
-        assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+        InstallUtils.processUserData(TestApp.A);
 
-        RollbackInfo committed = Utils.getCommittedRollback(TestApp.A);
+        RollbackInfo committed = RollbackUtils.getCommittedRollback(TestApp.A);
         assertThat(committed).isStaged();
         assertThat(committed).packagesContainsExactly(
                 Rollback.from(TestApp.Apex3).to(TestApp.Apex2),
@@ -299,7 +338,7 @@
      */
     @Test
     public void testApexRollbackExpirationEnableRollback() throws Exception {
-        assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(1);
 
         Install.single(TestApp.Apex2).setStaged().setEnableRollback().commit();
 
@@ -313,8 +352,8 @@
      */
     @Test
     public void testApexRollbackExpirationUpdateApex() throws Exception {
-        assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
-        assertThat(Utils.getAvailableRollback(TestApp.Apex)).isNotNull();
+        assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+        assertThat(RollbackUtils.getAvailableRollback(TestApp.Apex)).isNotNull();
         Install.single(TestApp.Apex3).setStaged().commit();
 
         // At this point, the host test driver will reboot the device and run
@@ -327,7 +366,7 @@
      */
     @Test
     public void testApexRollbackExpirationConfirmExpiration() throws Exception {
-        assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(3);
-        assertThat(Utils.getAvailableRollback(TestApp.Apex)).isNull();
+        assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(3);
+        assertThat(RollbackUtils.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 60e154c..736b47c 100644
--- a/hostsidetests/rollback/src/com/android/cts/rollback/host/RollbackManagerHostTest.java
+++ b/hostsidetests/rollback/src/com/android/cts/rollback/host/RollbackManagerHostTest.java
@@ -36,7 +36,7 @@
 public class RollbackManagerHostTest extends BaseHostJUnit4Test {
 
     private static final String SHIM_APEX_PACKAGE_NAME = "com.android.apex.cts.shim";
-    private static final String TEST_APK_PACKAGE_NAME = "com.android.cts.rollback.lib.testapp.A";
+    private static final String TEST_APK_PACKAGE_NAME = "com.android.cts.install.lib.testapp.A";
 
     /**
      * Runs the helper app test method on device.
@@ -133,6 +133,20 @@
     }
 
     /**
+     * Tests staged rollbacks to system version involving only apex.
+     */
+    @Test
+    public void testApexOnlySystemVersionStagedRollback() throws Exception {
+        assumeTrue("Device does not support updating APEX", isApexUpdateSupported());
+
+        run("testApexOnlySystemVersion_EnableRollback");
+        getDevice().reboot();
+        run("testApexOnlySystemVersion_CommitRollback");
+        getDevice().reboot();
+        run("testApexOnlySystemVersion_ConfirmRollback");
+    }
+
+    /**
      * Tests staged rollbacks involving only apex.
      */
     @Test
diff --git a/hostsidetests/sample/OWNERS b/hostsidetests/sample/OWNERS
new file mode 100644
index 0000000..84828b2
--- /dev/null
+++ b/hostsidetests/sample/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 346961
+include /tests/sample/OWNERS
diff --git a/hostsidetests/security/AndroidTest.xml b/hostsidetests/security/AndroidTest.xml
index ac79c0e..0f03245 100755
--- a/hostsidetests/security/AndroidTest.xml
+++ b/hostsidetests/security/AndroidTest.xml
@@ -18,6 +18,7 @@
     <option name="config-descriptor:metadata" key="component" value="security" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
 
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsSecurityHostTestCases.jar" />
diff --git a/hostsidetests/security/OWNERS b/hostsidetests/security/OWNERS
new file mode 100644
index 0000000..94522e3
--- /dev/null
+++ b/hostsidetests/security/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 36824
+include /tests/tests/security/OWNERS
diff --git a/hostsidetests/security/src/android/cts/security/KernelConfigTest.java b/hostsidetests/security/src/android/cts/security/KernelConfigTest.java
index 113ec76..5ff9802 100644
--- a/hostsidetests/security/src/android/cts/security/KernelConfigTest.java
+++ b/hostsidetests/security/src/android/cts/security/KernelConfigTest.java
@@ -31,6 +31,7 @@
 import java.io.InputStreamReader;
 import java.lang.String;
 import java.util.stream.Collectors;
+import java.util.Set;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -236,4 +237,61 @@
                     configSet.contains("CONFIG_PAGE_TABLE_ISOLATION=y"));
         }
     }
+
+    /**
+     * Test that the kernel enables static usermodehelper and sets
+     * the path to a whitelisted path.
+     *
+     * @throws Exception
+     */
+    @CddTest(requirement="9.7")
+    public void testConfigDisableUsermodehelper() throws Exception {
+        if (PropertyUtil.getFirstApiLevel(mDevice) < 29) {
+            return;
+        }
+
+        final String ENABLE_CONFIG = "CONFIG_STATIC_USERMODEHELPER=y";
+        final String PATH_CONFIG = "CONFIG_STATIC_USERMODEHELPER_PATH=";
+
+        final Set<String> ALLOWED_PATH_PREFIXES = new HashSet<String>();
+        ALLOWED_PATH_PREFIXES.add("/vendor/");
+        ALLOWED_PATH_PREFIXES.add("/system/");
+
+        assertTrue("Linux kernel must enable static usermodehelper: " + ENABLE_CONFIG,
+            configSet.contains(ENABLE_CONFIG));
+
+        String configPath = null;
+
+        for (String option : configSet) {
+            if (option.startsWith(PATH_CONFIG)) {
+                configPath = option;
+            }
+        }
+
+        int index = configPath.indexOf('=');
+        String path = configPath.substring(index+1);
+
+        assertTrue("Linux kernel must specify an absolute path for static usermodehelper path",
+            configPath.contains("..") == false);
+
+        boolean pathIsWhitelisted = false;
+
+        for (String allowedPath : ALLOWED_PATH_PREFIXES) {
+            if (path.startsWith(allowedPath)) {
+                pathIsWhitelisted = true;
+                break;
+            }
+        }
+
+        // Specifying no path, which disables usermodehelper, is also
+        // valid.
+        pathIsWhitelisted |= path.isEmpty();
+
+        String whitelistedPathPrefixExample = "'" +
+            String.join("', '", ALLOWED_PATH_PREFIXES) + "'";
+
+        assertTrue("Linux kernel must specify a whitelisted static usermodehelper path, "
+                   + "and it must be empty or start with one of the following "
+                   + "prefixes: " + whitelistedPathPrefixExample, pathIsWhitelisted);
+    }
 }
diff --git a/hostsidetests/securitybulletin/OWNERS b/hostsidetests/securitybulletin/OWNERS
new file mode 100644
index 0000000..ca82a6a
--- /dev/null
+++ b/hostsidetests/securitybulletin/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 36824
+mspector@google.com
+samschumacher@google.com
diff --git a/hostsidetests/securitybulletin/securityPatch/Bug-115739809/poc.cpp b/hostsidetests/securitybulletin/securityPatch/Bug-115739809/poc.cpp
index 83227af..73251cd 100755
--- a/hostsidetests/securitybulletin/securityPatch/Bug-115739809/poc.cpp
+++ b/hostsidetests/securitybulletin/securityPatch/Bug-115739809/poc.cpp
@@ -108,6 +108,10 @@
             outMsg->body.motion.xPrecision = msg.body.motion.xPrecision;
             // float yPrecision
             outMsg->body.motion.yPrecision = msg.body.motion.yPrecision;
+            // float xCursorPosition
+            outMsg->body.motion.xCursorPosition = msg.body.motion.xCursorPosition;
+            // float yCursorPosition
+            outMsg->body.motion.yCursorPosition = msg.body.motion.yCursorPosition;
             // uint32_t pointerCount
             outMsg->body.motion.pointerCount = msg.body.motion.pointerCount;
             //struct Pointer pointers[MAX_POINTERS]
diff --git a/hostsidetests/shortcuts/deviceside/upgrade/Android.bp b/hostsidetests/shortcuts/deviceside/upgrade/Android.bp
index a0e435f..f386a71 100644
--- a/hostsidetests/shortcuts/deviceside/upgrade/Android.bp
+++ b/hostsidetests/shortcuts/deviceside/upgrade/Android.bp
@@ -27,6 +27,9 @@
         "vts",
         "general-tests",
     ],
+    static_libs: [
+        "compatibility-device-util-axt",
+    ],
 }
 
 android_test_helper_app {
diff --git a/hostsidetests/signedconfig/hostside/OWNERS b/hostsidetests/signedconfig/hostside/OWNERS
new file mode 100644
index 0000000..f968305
--- /dev/null
+++ b/hostsidetests/signedconfig/hostside/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 533114
+mathewi@google.com
+*
diff --git a/hostsidetests/stagedinstall/Android.bp b/hostsidetests/stagedinstall/Android.bp
index 01bb8be..585a26a 100644
--- a/hostsidetests/stagedinstall/Android.bp
+++ b/hostsidetests/stagedinstall/Android.bp
@@ -44,54 +44,34 @@
     manifest : "app/AndroidManifest.xml",
 
     java_resources:  [
+        ":ApexKeyRotationTestV2_SignedBob",
+        ":ApexKeyRotationTestV2_SignedBobRot",
+        ":ApexKeyRotationTestV3_SignedBob",
+        ":ApexKeyRotationTestV3_SignedBobRot",
         ":StagedInstallTestApexV1_NotPreInstalled",
+        ":StagedInstallTestApexV1",
         ":StagedInstallTestApexV2",
         ":StagedInstallTestApexV2_AdditionalFile",
         ":StagedInstallTestApexV2_AdditionalFolder",
+        ":StagedInstallTestApexV2_DifferentCertificate",
         ":StagedInstallTestApexV2_WithPostInstallHook",
         ":StagedInstallTestApexV2_WithPreInstallHook",
         ":StagedInstallTestApexV2_WrongSha",
         ":StagedInstallTestApexV3",
-        ":StagedInstallTestAppAv1",
-        ":StagedInstallTestAppAv2",
-        ":StagedInstallTestAppBv1",
         ":StagedInstallTestAppSamePackageNameAsApex",
     ],
     static_libs: [
         "androidx.test.runner",
         "androidx.test.core",
         "truth-prebuilt",
+        "cts-install-lib",
     ],
-    sdk_version: "system_current",
+    sdk_version: "test_current",
     test_suites: ["device-tests"],
 
 }
 
 android_test_helper_app {
-    name: "StagedInstallTestAppAv1",
-
-    srcs:   ["testdata/apk/src/**/*java"],
-
-    manifest:   "testdata/apk/Av1.xml",
-}
-
-android_test_helper_app {
-    name: "StagedInstallTestAppAv2",
-
-    srcs:   ["testdata/apk/src/**/*java"],
-
-    manifest:   "testdata/apk/Av2.xml",
-}
-
-android_test_helper_app {
-    name: "StagedInstallTestAppBv1",
-
-    srcs:   ["testdata/apk/src/**/*java"],
-
-    manifest:   "testdata/apk/Bv1.xml",
-}
-
-android_test_helper_app {
     name: "StagedInstallTestAppSamePackageNameAsApex",
 
     srcs:   ["testdata/apk/src/**/*java"],
@@ -100,6 +80,41 @@
 }
 
 prebuilt_apex {
+    name: "ApexKeyRotationTestV2_SignedBob",
+    src: "testdata/apex/com.android.apex.cts.shim.v2_signed_bob.apex",
+    filename: "com.android.apex.cts.shim.v2_signed_bob.apex",
+    installable: false,
+}
+
+prebuilt_apex {
+    name: "ApexKeyRotationTestV2_SignedBobRot",
+    src: "testdata/apex/com.android.apex.cts.shim.v2_signed_bob_rot.apex",
+    filename: "com.android.apex.cts.shim.v2_signed_bob_rot.apex",
+    installable: false,
+}
+
+prebuilt_apex {
+    name: "ApexKeyRotationTestV3_SignedBob",
+    src: "testdata/apex/com.android.apex.cts.shim.v3_signed_bob.apex",
+    filename: "com.android.apex.cts.shim.v3_signed_bob.apex",
+    installable: false,
+}
+
+prebuilt_apex {
+    name: "ApexKeyRotationTestV3_SignedBobRot",
+    src: "testdata/apex/com.android.apex.cts.shim.v3_signed_bob_rot.apex",
+    filename: "com.android.apex.cts.shim.v3_signed_bob_rot.apex",
+    installable: false,
+}
+
+prebuilt_apex {
+    name: "StagedInstallTestApexV1",
+    src: "testdata/apex/com.android.apex.cts.shim.v1.apex",
+    filename: "com.android.apex.cts.shim.v1.apex",
+    installable: false,
+}
+
+prebuilt_apex {
     name: "StagedInstallTestApexV2",
     src: "testdata/apex/com.android.apex.cts.shim.v2.apex",
     filename: "com.android.apex.cts.shim.v2.apex",
@@ -154,3 +169,10 @@
     filename: "com.android.apex.cts.shim_not_pre_installed.apex",
     installable: false,
 }
+
+prebuilt_apex {
+    name: "StagedInstallTestApexV2_DifferentCertificate",
+    src: "testdata/apex/com.android.apex.cts.shim.v2_different_certificate.apex",
+    filename: "com.android.apex.cts.shim.v2_different_certificate.apex",
+    installable: false,
+}
diff --git a/hostsidetests/stagedinstall/AndroidTest.xml b/hostsidetests/stagedinstall/AndroidTest.xml
index ad944b0..64d7495 100644
--- a/hostsidetests/stagedinstall/AndroidTest.xml
+++ b/hostsidetests/stagedinstall/AndroidTest.xml
@@ -24,10 +24,10 @@
         <option name="test-file-name" value="StagedInstallTest.apk" />
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <option name="run-command" value="pm uninstall com.android.tests.stagedinstall.testapp.A" />
-        <option name="run-command" value="pm uninstall com.android.tests.stagedinstall.testapp.B" />
-        <option name="teardown-command" value="pm uninstall com.android.tests.stagedinstall.testapp.A" />
-        <option name="teardown-command" value="pm uninstall com.android.tests.stagedinstall.testapp.B" />
+        <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
+        <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
+        <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
+        <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.HostTest" >
         <option name="class" value="com.android.tests.stagedinstall.host.StagedInstallTest" />
diff --git a/hostsidetests/stagedinstall/OWNERS b/hostsidetests/stagedinstall/OWNERS
new file mode 100644
index 0000000..2dd1076
--- /dev/null
+++ b/hostsidetests/stagedinstall/OWNERS
@@ -0,0 +1,6 @@
+# Bug component: 36137
+dariofreni@google.com
+toddke@google.com
+narayan@google.com
+patb@google.com
+ioffe@google.com
diff --git a/hostsidetests/stagedinstall/TEST_MAPPING b/hostsidetests/stagedinstall/TEST_MAPPING
index c487fa3..8a1c753 100644
--- a/hostsidetests/stagedinstall/TEST_MAPPING
+++ b/hostsidetests/stagedinstall/TEST_MAPPING
@@ -1,6 +1,16 @@
 {
   "presubmit": [
     {
+      "name": "CtsStagedInstallHostTestCases",
+      "options": [
+        {
+          "exclude-annotation": "android.platform.test.annotations.LargeTest"
+        }
+      ]
+    }
+  ],
+  "postsubmit": [
+    {
       "name": "CtsStagedInstallHostTestCases"
     }
   ]
diff --git a/hostsidetests/stagedinstall/app/AndroidManifest.xml b/hostsidetests/stagedinstall/app/AndroidManifest.xml
index c327b79..51c2ec4 100644
--- a/hostsidetests/stagedinstall/app/AndroidManifest.xml
+++ b/hostsidetests/stagedinstall/app/AndroidManifest.xml
@@ -18,7 +18,7 @@
           package="com.android.tests.stagedinstall" >
 
     <application>
-        <receiver android:name="com.android.tests.stagedinstall.LocalIntentSender"
+        <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
                   android:exported="true" />
         <receiver android:name="com.android.tests.stagedinstall.SessionUpdateBroadcastReceiver">
             <intent-filter>
@@ -32,4 +32,4 @@
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.tests.stagedinstall"
                      android:label="StagedInstall Test"/>
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/ApexShimValidationTest.java b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/ApexShimValidationTest.java
index f9c4ba8..c36c4f7 100644
--- a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/ApexShimValidationTest.java
+++ b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/ApexShimValidationTest.java
@@ -21,22 +21,22 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.Manifest;
-import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
-import android.content.pm.PackageManager;
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.cts.install.lib.Install;
+import com.android.cts.install.lib.InstallUtils;
+import com.android.cts.install.lib.LocalIntentSender;
+import com.android.cts.install.lib.TestApp;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -120,63 +120,17 @@
 
     @Test
     public void testInstallRejected_VerifyPostReboot() throws Exception {
-        assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
-    }
-
-    private static PackageInstaller getPackageInstaller() {
-        return InstrumentationRegistry.getInstrumentation().getContext().getPackageManager()
-                .getPackageInstaller();
-    }
-
-    private static long getInstalledVersion(String packageName) {
-        Context context = InstrumentationRegistry.getInstrumentation().getContext();
-        PackageManager pm = context.getPackageManager();
-        try {
-            PackageInfo info = pm.getPackageInfo(packageName, PackageManager.MATCH_APEX);
-            return info.getLongVersionCode();
-        } catch (PackageManager.NameNotFoundException e) {
-            return -1;
-        }
+        assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
     }
 
     private static int stageApex(String apexFileName) throws Exception {
-        PackageInstaller installer = getPackageInstaller();
-
-        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
-                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
-        params.setInstallAsApex();
-        params.setStaged();
-        int sessionId = installer.createSession(params);
-        PackageInstaller.Session session = installer.openSession(sessionId);
-        writeApex(session, apexFileName);
+        TestApp apexTestApp = new TestApp("ShimApex", SHIM_APEX_PACKAGE_NAME, 2,
+                true, apexFileName);
+        int sessionId = Install.single(apexTestApp).setStaged().createSession();
+        PackageInstaller.Session session = InstallUtils.openPackageInstallerSession(sessionId);
         session.commit(LocalIntentSender.getIntentSender());
         Intent result = LocalIntentSender.getIntentSenderResult();
-        assertStatusSuccess(result);
+        InstallUtils.assertStatusSuccess(result);
         return sessionId;
     }
-
-    private static void writeApex(PackageInstaller.Session session, String apkFileName)
-            throws Exception {
-        try (OutputStream packageInSession = session.openWrite(apkFileName, 0, -1);
-             InputStream is =
-                     ApexShimValidationTest.class.getClassLoader().getResourceAsStream(
-                             apkFileName)) {
-            byte[] buffer = new byte[4096];
-            int n;
-            while ((n = is.read(buffer)) >= 0) {
-                packageInSession.write(buffer, 0, n);
-            }
-        }
-    }
-
-    private static void assertStatusSuccess(Intent result) {
-        int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
-                PackageInstaller.STATUS_FAILURE);
-        if (status == -1) {
-            throw new AssertionError("PENDING USER ACTION");
-        } else if (status > 0) {
-            String message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
-            throw new AssertionError(message == null ? "UNKNOWN FAILURE" : message);
-        }
-    }
 }
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 968960a..cde5dcd 100644
--- a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
@@ -16,6 +16,7 @@
 
 package com.android.tests.stagedinstall;
 
+import static com.android.cts.install.lib.InstallUtils.getPackageInstaller;
 import static com.android.tests.stagedinstall.PackageInstallerSessionInfoSubject.assertThat;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -26,16 +27,22 @@
 import android.Manifest;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.util.Log;
-import android.util.Pair;
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.cts.install.lib.Install;
+import com.android.cts.install.lib.InstallUtils;
+import com.android.cts.install.lib.LocalIntentSender;
+import com.android.cts.install.lib.TestApp;
+import com.android.cts.install.lib.Uninstall;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -47,6 +54,7 @@
 import java.io.File;
 import java.io.FileReader;
 import java.io.FileWriter;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.file.Files;
@@ -82,12 +90,6 @@
 
     private static final String TAG = "StagedInstallTest";
 
-    private static final String TEST_APP_A = "com.android.tests.stagedinstall.testapp.A";
-    private static final String TEST_APP_B = "com.android.tests.stagedinstall.testapp.B";
-    private static final String SHIM_APEX_PACKAGE_NAME = "com.android.apex.cts.shim";
-    private static final String NOT_PRE_INSTALLED_SHIM_APEX_PACKAGE_NAME =
-            "com.android.apex.cts.shim_not_pre_installed";
-
     private File mTestStateFile = new File(
             InstrumentationRegistry.getInstrumentation().getContext().getFilesDir(),
             "ctsstagedinstall_state");
@@ -95,6 +97,26 @@
     private static final Duration WAIT_FOR_SESSION_REMOVED_TTL = Duration.ofSeconds(10);
     private static final Duration SLEEP_DURATION = Duration.ofMillis(200);
 
+    private static final String SHIM_PACKAGE_NAME = "com.android.apex.cts.shim";
+    private static final TestApp TESTAPP_SAME_NAME_AS_APEX = new TestApp(
+            "TestAppSamePackageNameAsApex", SHIM_PACKAGE_NAME, 1, /*isApex*/ false,
+            "StagedInstallTestAppSamePackageNameAsApex.apk");
+    public static final TestApp Apex2DifferentCertificate = new TestApp(
+            "Apex2DifferentCertificate", SHIM_PACKAGE_NAME, 2, /*isApex*/true,
+            "com.android.apex.cts.shim.v2_different_certificate.apex");
+    private static final TestApp Apex2SignedBob = new TestApp(
+            "Apex2SignedBob", SHIM_PACKAGE_NAME, 2, /*isApex*/true,
+                    "com.android.apex.cts.shim.v2_signed_bob.apex");
+    private static final TestApp Apex2SignedBobRot = new TestApp(
+            "Apex2SignedBobRot", SHIM_PACKAGE_NAME, 2, /*isApex*/true,
+                    "com.android.apex.cts.shim.v2_signed_bob_rot.apex");
+    private static final TestApp Apex3SignedBob = new TestApp(
+            "Apex3SignedBob", SHIM_PACKAGE_NAME, 3, /*isApex*/true,
+                    "com.android.apex.cts.shim.v3_signed_bob.apex");
+    private static final TestApp Apex3SignedBobRot = new TestApp(
+            "Apex3SignedBobRot", SHIM_PACKAGE_NAME, 3, /*isApex*/true,
+                    "com.android.apex.cts.shim.v3_signed_bob_rot.apex");
+
     @Before
     public void adoptShellPermissions() {
         InstrumentationRegistry
@@ -133,8 +155,7 @@
                 Log.e(TAG, "Failed to abandon session " + sessionInfo.getSessionId(), e);
             }
         }
-        uninstall(TEST_APP_A);
-        uninstall(TEST_APP_B);
+        Uninstall.packages(TestApp.A, TestApp.B);
         Files.deleteIfExists(mTestStateFile.toPath());
     }
 
@@ -156,20 +177,19 @@
 
     @Test
     public void testInstallStagedApk_Commit() throws Exception {
-        int sessionId = stageSingleApk(
-                "StagedInstallTestAppAv1.apk").assertSuccessful().getSessionId();
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
+        int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
         waitForIsReadyBroadcast(sessionId);
         assertSessionReady(sessionId);
         storeSessionId(sessionId);
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
     }
 
     @Test
     public void testInstallStagedApk_VerifyPostReboot() throws Exception {
         int sessionId = retrieveLastSessionId();
         assertSessionApplied(sessionId);
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(1);
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(1);
     }
 
     @Test
@@ -184,33 +204,30 @@
 
     @Test
     public void testInstallMultipleStagedApks_Commit() throws Exception {
-        int sessionId = stageMultipleApks(
-                "StagedInstallTestAppAv1.apk",
-                "StagedInstallTestAppBv1.apk")
+        int sessionId = stageMultipleApks(TestApp.A1, TestApp.B1)
                 .assertSuccessful().getSessionId();
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
-        assertThat(getInstalledVersion(TEST_APP_B)).isEqualTo(-1);
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+        assertThat(getInstalledVersion(TestApp.B)).isEqualTo(-1);
         waitForIsReadyBroadcast(sessionId);
         assertSessionReady(sessionId);
         storeSessionId(sessionId);
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
-        assertThat(getInstalledVersion(TEST_APP_B)).isEqualTo(-1);
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+        assertThat(getInstalledVersion(TestApp.B)).isEqualTo(-1);
     }
 
     @Test
     public void testInstallMultipleStagedApks_VerifyPostReboot() throws Exception {
         int sessionId = retrieveLastSessionId();
         assertSessionApplied(sessionId);
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(1);
-        assertThat(getInstalledVersion(TEST_APP_B)).isEqualTo(1);
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(1);
+        assertThat(getInstalledVersion(TestApp.B)).isEqualTo(1);
     }
 
     @Test
     public void testFailInstallAnotherSessionAlreadyInProgress_BothSinglePackage()
             throws Exception {
-        int sessionId = stageSingleApk(
-                "StagedInstallTestAppAv1.apk").assertSuccessful().getSessionId();
-        StageSessionResult failedSessionResult = stageSingleApk("StagedInstallTestAppAv1.apk");
+        int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
+        StageSessionResult failedSessionResult = stageSingleApk(TestApp.A1);
         assertThat(failedSessionResult.getErrorMessage()).contains(
                 "There is already in-progress committed staged session");
         getPackageInstaller().abandonSession(sessionId);
@@ -219,11 +236,8 @@
     @Test
     public void testFailInstallAnotherSessionAlreadyInProgress_SinglePackageMultiPackage()
             throws Exception {
-        int sessionId = stageSingleApk(
-                "StagedInstallTestAppAv1.apk").assertSuccessful().getSessionId();
-        StageSessionResult failedSessionResult = stageMultipleApks(
-                "StagedInstallTestAppAv1.apk",
-                "StagedInstallTestAppBv1.apk");
+        int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
+        StageSessionResult failedSessionResult = stageMultipleApks(TestApp.A1, TestApp.B1);
         assertThat(failedSessionResult.getErrorMessage()).contains(
                 "There is already in-progress committed staged session");
         getPackageInstaller().abandonSession(sessionId);
@@ -232,11 +246,9 @@
     @Test
     public void testFailInstallAnotherSessionAlreadyInProgress_MultiPackageSinglePackage()
             throws Exception {
-        int sessionId = stageMultipleApks(
-                "StagedInstallTestAppAv1.apk",
-                "StagedInstallTestAppBv1.apk").assertSuccessful().getSessionId();
-        StageSessionResult failedSessionResult = stageSingleApk(
-                "StagedInstallTestAppAv1.apk");
+        int sessionId = stageMultipleApks(TestApp.A1, TestApp.B1)
+                .assertSuccessful().getSessionId();
+        StageSessionResult failedSessionResult = stageSingleApk(TestApp.A1);
         assertThat(failedSessionResult.getErrorMessage()).contains(
                 "There is already in-progress committed staged session");
         getPackageInstaller().abandonSession(sessionId);
@@ -245,12 +257,9 @@
     @Test
     public void testFailInstallAnotherSessionAlreadyInProgress_BothMultiPackage()
             throws Exception {
-        int sessionId = stageMultipleApks(
-                "StagedInstallTestAppAv1.apk",
-                "StagedInstallTestAppBv1.apk").assertSuccessful().getSessionId();
-        StageSessionResult failedSessionResult = stageMultipleApks(
-                "StagedInstallTestAppAv1.apk",
-                "StagedInstallTestAppBv1.apk");
+        int sessionId = stageMultipleApks(TestApp.A1, TestApp.B1)
+                .assertSuccessful().getSessionId();
+        StageSessionResult failedSessionResult = stageMultipleApks(TestApp.A1, TestApp.B1);
         assertThat(failedSessionResult.getErrorMessage()).contains(
                 "There is already in-progress committed staged session");
         getPackageInstaller().abandonSession(sessionId);
@@ -258,9 +267,8 @@
 
     @Test
     public void testAbandonStagedApkBeforeReboot_CommitAndAbandon() throws Exception {
-        int sessionId = stageSingleApk(
-                "StagedInstallTestAppAv1.apk").assertSuccessful().getSessionId();
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
+        int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
         waitForIsReadyBroadcast(sessionId);
         PackageInstaller.SessionInfo session = getStagedSessionInfo(sessionId);
         assertSessionReady(sessionId);
@@ -287,14 +295,13 @@
 
     @Test
     public void testAbandonStagedApkBeforeReboot_VerifyPostReboot() throws Exception {
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
     }
 
     @Test
     public void testGetActiveStagedSession() throws Exception {
         PackageInstaller packageInstaller = getPackageInstaller();
-        int sessionId = stageSingleApk(
-                "StagedInstallTestAppAv1.apk").assertSuccessful().getSessionId();
+        int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
         PackageInstaller.SessionInfo session = packageInstaller.getActiveStagedSession();
         assertThat(session.getSessionId()).isEqualTo(sessionId);
     }
@@ -308,9 +315,7 @@
 
     @Test
     public void testGetGetActiveStagedSession_MultiApkSession() throws Exception {
-        int sessionId = stageMultipleApks(
-                "StagedInstallTestAppAv1.apk",
-                "StagedInstallTestAppBv1.apk")
+        int sessionId = stageMultipleApks(TestApp.A1, TestApp.B1)
                 .assertSuccessful().getSessionId();
         PackageInstaller.SessionInfo session = getPackageInstaller().getActiveStagedSession();
         assertThat(session.getSessionId()).isEqualTo(sessionId);
@@ -318,22 +323,20 @@
 
     @Test
     public void testStagedInstallDowngrade_DowngradeNotRequested_Fails_Commit()  throws Exception {
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
-        installNonStaged("StagedInstallTestAppAv2.apk");
-        int sessionId = stageSingleApk(
-                "StagedInstallTestAppAv1.apk").assertSuccessful().getSessionId();
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(2);
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+        Install.single(TestApp.A2).commit();
+        int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2);
         PackageInstaller.SessionInfo sessionInfo = waitForBroadcast(sessionId);
         assertThat(sessionInfo).isStagedSessionFailed();
     }
 
     @Test
     public void testStagedInstallDowngrade_DowngradeRequested_Commit() throws Exception {
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
-        installNonStaged("StagedInstallTestAppAv2.apk");
-        int sessionId = stageDowngradeSingleApk(
-                "StagedInstallTestAppAv1.apk").assertSuccessful().getSessionId();
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(2);
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+        Install.single(TestApp.A2).commit();
+        int sessionId = stageDowngradeSingleApk(TestApp.A1).assertSuccessful().getSessionId();
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2);
         waitForIsReadyBroadcast(sessionId);
         assertSessionReady(sessionId);
         storeSessionId(sessionId);
@@ -341,11 +344,10 @@
 
     @Test
     public void testStagedInstallDowngrade_DowngradeRequested_Fails_Commit() throws Exception {
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
-        installNonStaged("StagedInstallTestAppAv2.apk");
-        int sessionId = stageDowngradeSingleApk(
-                "StagedInstallTestAppAv1.apk").assertSuccessful().getSessionId();
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(2);
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+        Install.single(TestApp.A2).commit();
+        int sessionId = stageDowngradeSingleApk(TestApp.A1).assertSuccessful().getSessionId();
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2);
         PackageInstaller.SessionInfo sessionInfo = waitForBroadcast(sessionId);
         assertThat(sessionInfo).isStagedSessionFailed();
     }
@@ -356,49 +358,47 @@
         int sessionId = retrieveLastSessionId();
         assertSessionApplied(sessionId);
         // App should be downgraded.
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(1);
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(1);
     }
 
     @Test
     public void testInstallStagedApex_Commit() throws Exception {
-        assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
-        int sessionId = stageSingleApk(
-                "com.android.apex.cts.shim.v2.apex").assertSuccessful().getSessionId();
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+        int sessionId = stageSingleApk(TestApp.Apex2).assertSuccessful().getSessionId();
         waitForIsReadyBroadcast(sessionId);
         assertSessionReady(sessionId);
         storeSessionId(sessionId);
         // Version shouldn't change before reboot.
-        assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
     }
 
     @Test
     public void testInstallStagedApex_VerifyPostReboot() throws Exception {
         int sessionId = retrieveLastSessionId();
         assertSessionApplied(sessionId);
-        assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2);
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
     }
 
     @Test
     public void testInstallStagedApexAndApk_Commit() throws Exception {
-        assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
-        int sessionId = stageMultipleApks(
-                "com.android.apex.cts.shim.v2.apex",
-                "StagedInstallTestAppAv1.apk").assertSuccessful().getSessionId();
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+        int sessionId = stageMultipleApks(TestApp.Apex2, TestApp.A1)
+                .assertSuccessful().getSessionId();
         waitForIsReadyBroadcast(sessionId);
         assertSessionReady(sessionId);
         storeSessionId(sessionId);
         // Version shouldn't change before reboot.
-        assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
     }
 
     @Test
     public void testInstallStagedApexAndApk_VerifyPostReboot() throws Exception {
         int sessionId = retrieveLastSessionId();
         assertSessionApplied(sessionId);
-        assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2);
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(1);
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(1);
     }
 
     @Test
@@ -418,9 +418,9 @@
 
     @Test
     public void testInstallStagedNonPreInstalledApex_Fails() throws Exception {
-        assertThat(getInstalledVersion(NOT_PRE_INSTALLED_SHIM_APEX_PACKAGE_NAME)).isEqualTo(-1);
+        assertThat(getInstalledVersion(TestApp.NotPreInstalledApex)).isEqualTo(-1);
         int sessionId = stageSingleApk(
-                "com.android.apex.cts.shim_not_pre_installed.apex")
+                TestApp.ApexNotPreInstalled)
                 .assertSuccessful().getSessionId();
         PackageInstaller.SessionInfo sessionInfo = waitForBroadcast(sessionId);
         assertThat(sessionInfo).isStagedSessionFailed();
@@ -428,9 +428,9 @@
 
     @Test
     public void testStageApkWithSameNameAsApexShouldFail_Commit() throws Exception {
-        assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
-        int sessionId = stageSingleApk(
-                "StagedInstallTestAppSamePackageNameAsApex.apk").assertSuccessful().getSessionId();
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+        int sessionId = stageSingleApk(TESTAPP_SAME_NAME_AS_APEX)
+                .assertSuccessful().getSessionId();
         waitForIsReadyBroadcast(sessionId);
         assertSessionReady(sessionId);
         storeSessionId(sessionId);
@@ -440,28 +440,51 @@
     public void testStageApkWithSameNameAsApexShouldFail_VerifyPostReboot() throws Exception {
         int sessionId = retrieveLastSessionId();
         assertSessionFailed(sessionId);
-        assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
     }
 
     @Test
     public void testNonStagedInstallApkWithSameNameAsApexShouldFail() throws Exception {
-        assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
-        PackageInstaller packageInstaller = getPackageInstaller();
-        PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(
-                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
-        int sessionId = packageInstaller.createSession(sessionParams);
-        PackageInstaller.Session session = packageInstaller.openSession(sessionId);
-        writeApk(session, "StagedInstallTestAppSamePackageNameAsApex.apk");
-        session.commit(LocalIntentSender.getIntentSender());
-        final String errorMessage = extractErrorMessage(LocalIntentSender.getIntentSenderResult());
-        assertThat(errorMessage).contains("is an APEX package and can't be installed as an APK");
-        assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+        InstallUtils.commitExpectingFailure(AssertionError.class,
+                "is an APEX package and can't be installed as an APK",
+                Install.single(TESTAPP_SAME_NAME_AS_APEX));
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+    }
+
+    @Test
+    public void testInstallV2Apex_Commit() throws Exception {
+        int sessionId = stageSingleApk(TestApp.Apex2).assertSuccessful().getSessionId();
+        waitForIsReadyBroadcast(sessionId);
+        assertSessionReady(sessionId);
+        storeSessionId(sessionId);
+    }
+
+    @Test
+    public void testInstallV2Apex_VerifyPostReboot() throws Exception {
+        int sessionId = retrieveLastSessionId();
+        assertSessionApplied(sessionId);
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+    }
+
+    @Test
+    public void testInstallV2SignedBobApex_Commit() throws Exception {
+        int sessionId = stageSingleApk(Apex2SignedBobRot).assertSuccessful().getSessionId();
+        waitForIsReadyBroadcast(sessionId);
+        assertSessionReady(sessionId);
+        storeSessionId(sessionId);
+    }
+
+    @Test
+    public void testInstallV2SignedBobApex_VerifyPostReboot() throws Exception {
+        int sessionId = retrieveLastSessionId();
+        assertSessionApplied(sessionId);
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
     }
 
     @Test
     public void testInstallV3Apex_Commit() throws Exception {
-        int sessionId = stageSingleApk(
-                "com.android.apex.cts.shim.v3.apex").assertSuccessful().getSessionId();
+        int sessionId = stageSingleApk(TestApp.Apex3).assertSuccessful().getSessionId();
         waitForIsReadyBroadcast(sessionId);
         assertSessionReady(sessionId);
         storeSessionId(sessionId);
@@ -471,15 +494,14 @@
     public void testInstallV3Apex_VerifyPostReboot() throws Exception {
         int sessionId = retrieveLastSessionId();
         assertSessionApplied(sessionId);
-        assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3);
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(3);
     }
 
     @Test
     public void testStagedInstallDowngradeApex_DowngradeNotRequested_Fails_Commit()
             throws Exception {
-        assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3);
-        int sessionId = stageSingleApk(
-                "com.android.apex.cts.shim.v2.apex").assertSuccessful().getSessionId();
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(3);
+        int sessionId = stageSingleApk(TestApp.Apex2).assertSuccessful().getSessionId();
         PackageInstaller.SessionInfo sessionInfo = waitForBroadcast(sessionId);
         assertThat(sessionInfo).isStagedSessionFailed();
         // Also verify that correct session info is reported by PackageManager.
@@ -493,15 +515,14 @@
         int sessionId = retrieveLastSessionId();
         assertSessionFailed(sessionId);
         // INSTALL_REQUEST_DOWNGRADE wasn't set, so apex shouldn't be downgraded.
-        assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3);
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(3);
     }
 
     @Test
     public void testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild_Commit()
             throws Exception {
-        assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3);
-        int sessionId = stageDowngradeSingleApk(
-                "com.android.apex.cts.shim.v2.apex").assertSuccessful().getSessionId();
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(3);
+        int sessionId = stageDowngradeSingleApk(TestApp.Apex2).assertSuccessful().getSessionId();
         waitForIsReadyBroadcast(sessionId);
         assertSessionReady(sessionId);
         storeSessionId(sessionId);
@@ -513,15 +534,14 @@
         int sessionId = retrieveLastSessionId();
         assertSessionApplied(sessionId);
         // Apex should be downgraded.
-        assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2);
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
     }
 
     @Test
     public void testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails_Commit()
             throws Exception {
-        assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3);
-        int sessionId = stageDowngradeSingleApk(
-                "com.android.apex.cts.shim.v2.apex").assertSuccessful().getSessionId();
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(3);
+        int sessionId = stageDowngradeSingleApk(TestApp.Apex2).assertSuccessful().getSessionId();
         PackageInstaller.SessionInfo sessionInfo = waitForBroadcast(sessionId);
         assertThat(sessionInfo).isStagedSessionFailed();
         // Also verify that correct session info is reported by PackageManager.
@@ -535,25 +555,39 @@
         int sessionId = retrieveLastSessionId();
         assertSessionFailed(sessionId);
         // Apex shouldn't be downgraded.
-        assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3);
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(3);
+    }
+
+    @Test
+    public void testStagedInstallDowngradeApexToSystemVersion_DebugBuild_Commit()
+            throws Exception {
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+        int sessionId = stageDowngradeSingleApk(TestApp.Apex1).assertSuccessful().getSessionId();
+        waitForIsReadyBroadcast(sessionId);
+        assertSessionReady(sessionId);
+        storeSessionId(sessionId);
+    }
+
+    @Test
+    public void testStagedInstallDowngradeApexToSystemVersion_DebugBuild_VerifyPostReboot()
+            throws Exception {
+        int sessionId = retrieveLastSessionId();
+        assertSessionApplied(sessionId);
+        // Apex should be downgraded.
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
     }
 
     @Test
     public void testInstallApex_DeviceDoesNotSupportApex_Fails() throws Exception {
-        try {
-            stageSingleApk("com.android.apex.cts.shim.v2.apex");
-            fail("IllegalArgumentException expected");
-        } catch (IllegalArgumentException expected) {
-            assertThat(expected.getMessage()).contains(
-                    "This device doesn't support the installation of APEX files");
-        }
+        InstallUtils.commitExpectingFailure(IllegalArgumentException.class,
+                "This device doesn't support the installation of APEX files",
+                Install.single(TestApp.Apex2).setStaged());
     }
 
     @Test
     public void testFailsInvalidApexInstall_Commit() throws Exception {
-        assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
-        int sessionId = stageSingleApk(
-                "com.android.apex.cts.shim.v2_wrong_sha.apex").assertSuccessful()
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+        int sessionId = stageSingleApk(TestApp.ApexWrongSha2).assertSuccessful()
                 .getSessionId();
         waitForIsFailedBroadcast(sessionId);
         assertSessionFailed(sessionId);
@@ -603,13 +637,12 @@
         };
 
         Context context = InstrumentationRegistry.getInstrumentation().getContext();
-        PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
+        PackageInstaller packageInstaller = getPackageInstaller();
         packageInstaller.registerSessionCallback(callback, handler);
 
-        int sessionId = stageSingleApk(
-                "StagedInstallTestAppAv1.apk").assertSuccessful().getSessionId();
+        int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
 
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
         waitForIsReadyBroadcast(sessionId);
         assertSessionReady(sessionId);
 
@@ -627,9 +660,139 @@
         packageInstaller.abandonSession(sessionId);
     }
 
-    private static PackageInstaller getPackageInstaller() {
-        return InstrumentationRegistry.getInstrumentation().getContext().getPackageManager()
-                .getPackageInstaller();
+    @Test
+    public void testInstallStagedApexWithoutApexSuffix_Commit() throws Exception {
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+
+        int sessionId = stageSingleApk("com.android.apex.cts.shim.v2.apex", "package")
+                .assertSuccessful().getSessionId();
+        waitForIsReadyBroadcast(sessionId);
+        assertSessionReady(sessionId);
+        storeSessionId(sessionId);
+        // Version shouldn't change before reboot.
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+    }
+
+    @Test
+    public void testInstallStagedApexWithoutApexSuffix_VerifyPostReboot() throws Exception {
+        int sessionId = retrieveLastSessionId();
+        assertSessionApplied(sessionId);
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+    }
+
+    @Test
+    public void testRejectsApexDifferentCertificate() throws Exception {
+        int sessionId = stageSingleApk(Apex2DifferentCertificate)
+                .assertSuccessful().getSessionId();
+        PackageInstaller.SessionInfo info =
+                SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(60, TimeUnit.SECONDS);
+        assertThat(info.getSessionId()).isEqualTo(sessionId);
+        assertThat(info).isStagedSessionFailed();
+        assertThat(info.getStagedSessionErrorMessage()).contains("is not compatible with the one "
+                + "currently installed on device");
+    }
+
+    /**
+     * Tests for staged install involving rotated keys.
+     *
+     * Here alice means the original default key that cts.shim.v1 package was signed with and
+     * bob is the new key alice rotates to. Where ambiguous, we will refer keys as alice and bob
+     * instead of "old key" and "new key".
+     */
+
+    // The update should fail if it is signed with a different non-rotated key
+    @Test
+    public void testUpdateWithDifferentKeyButNoRotation() throws Exception {
+        int sessionId = stageSingleApk(Apex2SignedBob).assertSuccessful().getSessionId();
+        PackageInstaller.SessionInfo info =
+                SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(60, TimeUnit.SECONDS);
+        assertThat(info.getSessionId()).isEqualTo(sessionId);
+        assertThat(info).isStagedSessionFailed();
+    }
+
+    // The update should pass if it is signed with a proper rotated key
+    @Test
+    public void testUpdateWithDifferentKey_Commit() throws Exception {
+        int sessionId = stageSingleApk(Apex2SignedBobRot).assertSuccessful().getSessionId();
+        PackageInstaller.SessionInfo info =
+                SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(60, TimeUnit.SECONDS);
+        assertThat(info.getSessionId()).isEqualTo(sessionId);
+        assertThat(info).isStagedSessionReady();
+    }
+
+    @Test
+    public void testUpdateWithDifferentKey_VerifyPostReboot() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+    }
+
+    // Once updated with a new rotated key (bob), further updates with old key (alice) should fail
+    @Test
+    public void testAfterRotationOldKeyIsRejected() throws Exception {
+        // Assume updateWithDifferentKey_Commit has been run already
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+        int sessionId = stageSingleApk(TestApp.Apex3).assertSuccessful().getSessionId();
+        PackageInstaller.SessionInfo info =
+                SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(60, TimeUnit.SECONDS);
+        assertThat(info.getSessionId()).isEqualTo(sessionId);
+        assertThat(info).isStagedSessionFailed();
+    }
+
+    // Once updated with a new rotated key (bob), further updates with new key (bob) should pass
+    @Test
+    public void testAfterRotationNewKeyCanUpdateFurther_CommitPostReboot() throws Exception {
+        // Assume updateWithDifferentKey_Commit has been run already
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+        int sessionId = stageSingleApk(Apex3SignedBobRot).assertSuccessful().getSessionId();
+        PackageInstaller.SessionInfo info =
+                SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(60, TimeUnit.SECONDS);
+        assertThat(info.getSessionId()).isEqualTo(sessionId);
+        assertThat(info).isStagedSessionReady();
+    }
+
+    @Test
+    public void testAfterRotationNewKeyCanUpdateFurther_VerifyPostReboot() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(3);
+    }
+
+    // Once updated with a new rotated key (bob), further updates can be done with key only
+    @Test
+    public void testAfterRotationNewKeyCanUpdateFurtherWithoutLineage()
+            throws Exception {
+        // Assume updateWithDifferentKey_Commit has been run already
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+        int sessionId = stageSingleApk(Apex3SignedBob).assertSuccessful().getSessionId();
+        PackageInstaller.SessionInfo info =
+                SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(60, TimeUnit.SECONDS);
+        assertThat(info.getSessionId()).isEqualTo(sessionId);
+        assertThat(info).isStagedSessionReady();
+    }
+
+    @Test
+    public void testSamegradeSystemApex_Commit() throws Exception {
+        final PackageInfo shim = InstrumentationRegistry.getInstrumentation().getContext()
+                .getPackageManager().getPackageInfo(SHIM_PACKAGE_NAME, PackageManager.MATCH_APEX);
+        assertThat(shim.getLongVersionCode()).isEqualTo(1);
+        assertThat(shim.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM).isEqualTo(
+                ApplicationInfo.FLAG_SYSTEM);
+        assertThat(shim.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED).isEqualTo(
+                ApplicationInfo.FLAG_INSTALLED);
+        int sessionId = stageDowngradeSingleApk(TestApp.Apex1).assertSuccessful().getSessionId();
+        waitForIsReadyBroadcast(sessionId);
+        assertSessionReady(sessionId);
+        storeSessionId(sessionId);
+    }
+
+    @Test
+    public void testSamegradeSystemApex_VerifyPostReboot() throws Exception {
+        int sessionId = retrieveLastSessionId();
+        assertSessionApplied(sessionId);
+        final PackageInfo shim = InstrumentationRegistry.getInstrumentation().getContext()
+                .getPackageManager().getPackageInfo(SHIM_PACKAGE_NAME, PackageManager.MATCH_APEX);
+        assertThat(shim.getLongVersionCode()).isEqualTo(1);
+        // Check that APEX on /data wins.
+        assertThat(shim.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM).isEqualTo(0);
+        assertThat(shim.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED).isEqualTo(
+                ApplicationInfo.FLAG_INSTALLED);
     }
 
     private static long getInstalledVersion(String packageName) {
@@ -646,73 +809,52 @@
     // It becomes harder to maintain this variety of install-related helper methods.
     // TODO(ioffe): refactor install-related helper methods into a separate utility.
     private static int createStagedSession() throws Exception {
-        return createStagedSession(getPackageInstaller(), false, false, false);
+        return Install.single(TestApp.A1).setStaged().createSession();
     }
 
-    private static int createStagedSession(
-            PackageInstaller packageInstaller,
-            boolean multiPackage, boolean isDowngrade, boolean isApexSession) throws Exception {
-        PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(
-                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
-        if (multiPackage) {
-            sessionParams.setMultiPackage();
-        }
-        sessionParams.setStaged();
-        sessionParams.setRequestDowngrade(isDowngrade);
-        if (isApexSession) {
-            sessionParams.setInstallAsApex();
-        }
-
-        return packageInstaller.createSession(sessionParams);
+    private static void commitSession(int sessionId) throws IOException {
+        InstallUtils.openPackageInstallerSession(sessionId)
+                .commit(LocalIntentSender.getIntentSender());
     }
 
-    private static StageSessionResult stageDowngradeSingleApk(String apkFileName) throws Exception {
-        Log.i(TAG, "Staging a downgrade of " + apkFileName);
-        PackageInstaller packageInstaller = getPackageInstaller();
-
-        Pair<Integer, PackageInstaller.Session> sessionPair =
-                prepareSingleApkStagedSession(packageInstaller, apkFileName, true);
+    private static StageSessionResult stageDowngradeSingleApk(TestApp testApp) throws Exception {
+        Log.i(TAG, "Staging a downgrade of " + testApp);
+        int sessionId = Install.single(testApp).setStaged().setRequestDowngrade().createSession();
         // Commit the session (this will start the installation workflow).
-        Log.i(TAG, "Committing downgrade session for apk: " + apkFileName);
-        sessionPair.second.commit(LocalIntentSender.getIntentSender());
-        return new StageSessionResult(sessionPair.first, LocalIntentSender.getIntentSenderResult());
+        Log.i(TAG, "Committing downgrade session for apk: " + testApp);
+        commitSession(sessionId);
+        return new StageSessionResult(sessionId, LocalIntentSender.getIntentSenderResult());
     }
 
-    private static StageSessionResult stageSingleApk(String apkFileName) throws Exception {
+    private static StageSessionResult stageSingleApk(String apkFileName, String outputFileName)
+            throws Exception {
         Log.i(TAG, "Staging an install of " + apkFileName);
-        PackageInstaller packageInstaller = getPackageInstaller();
-
-        Pair<Integer, PackageInstaller.Session> sessionPair =
-                prepareSingleApkStagedSession(packageInstaller, apkFileName, false);
+        // this is a trick to open an empty install session so we can manually write the package
+        // using writeApk
+        TestApp empty = new TestApp(null, null, -1,
+                apkFileName.endsWith(".apex"));
+        int sessionId = Install.single(empty).setStaged().createSession();
+        PackageInstaller.Session session = InstallUtils.openPackageInstallerSession(sessionId);
+        writeApk(session, apkFileName, outputFileName);
         // Commit the session (this will start the installation workflow).
         Log.i(TAG, "Committing session for apk: " + apkFileName);
-        sessionPair.second.commit(LocalIntentSender.getIntentSender());
-        return new StageSessionResult(sessionPair.first, LocalIntentSender.getIntentSenderResult());
+        commitSession(sessionId);
+        return new StageSessionResult(sessionId, LocalIntentSender.getIntentSenderResult());
     }
 
-    private static Pair<Integer, PackageInstaller.Session>
-            prepareSingleApkStagedSession(PackageInstaller packageInstaller, String apkFileName,
-            boolean isDowngrade)
-            throws Exception {
-        int sessionId = createStagedSession(packageInstaller, false, isDowngrade,
-                apkFileName.endsWith(".apex"));
-        PackageInstaller.Session session = packageInstaller.openSession(sessionId);
-        writeApk(session, apkFileName);
-        return new Pair<>(sessionId, session);
+    private static StageSessionResult stageSingleApk(TestApp testApp) throws Exception {
+        Log.i(TAG, "Staging an install of " + testApp);
+        int sessionId = Install.single(testApp).setStaged().createSession();
+        // Commit the session (this will start the installation workflow).
+        Log.i(TAG, "Committing session for apk: " + testApp);
+        commitSession(sessionId);
+        return new StageSessionResult(sessionId, LocalIntentSender.getIntentSenderResult());
     }
 
-    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(
-                multiPackageSessionId);
-        for (String apkFileName : apkFileNames) {
-            Pair<Integer, PackageInstaller.Session> sessionPair =
-                    prepareSingleApkStagedSession(packageInstaller, apkFileName, false);
-            multiPackageSession.addChildSessionId(sessionPair.first);
-        }
-        multiPackageSession.commit(LocalIntentSender.getIntentSender());
+    private static StageSessionResult stageMultipleApks(TestApp... testApps) throws Exception {
+        Log.i(TAG, "Staging an install of " + Arrays.toString(testApps));
+        int multiPackageSessionId = Install.multi(testApps).setStaged().createSession();
+        commitSession(multiPackageSessionId);
         return new StageSessionResult(
                 multiPackageSessionId, LocalIntentSender.getIntentSenderResult());
     }
@@ -764,20 +906,10 @@
         }
     }
 
-    private static void installNonStaged(String apkFileName) throws Exception {
-        PackageInstaller packageInstaller = getPackageInstaller();
-        PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(
-                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
-        int sessionId = packageInstaller.createSession(sessionParams);
-        PackageInstaller.Session session = packageInstaller.openSession(sessionId);
-        writeApk(session, apkFileName);
-        session.commit(LocalIntentSender.getIntentSender());
-        assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
-    }
-
-    private static void writeApk(PackageInstaller.Session session, String apkFileName)
+    private static void writeApk(PackageInstaller.Session session, String apkFileName,
+            String outputFileName)
             throws Exception {
-        try (OutputStream packageInSession = session.openWrite(apkFileName, 0, -1);
+        try (OutputStream packageInSession = session.openWrite(outputFileName, 0, -1);
              InputStream is =
                      StagedInstallTest.class.getClassLoader().getResourceAsStream(apkFileName)) {
             byte[] buffer = new byte[4096];
@@ -845,19 +977,6 @@
         return getPackageInstaller().getSessionInfo(sessionId);
     }
 
-    private static void uninstall(String packageName) throws Exception {
-        // No need to uninstall if the package isn't installed.
-        if (getInstalledVersion(packageName) == -1) {
-            return;
-        }
-
-        Context context = InstrumentationRegistry.getInstrumentation().getContext();
-        PackageManager packageManager = context.getPackageManager();
-        PackageInstaller packageInstaller = packageManager.getPackageInstaller();
-        packageInstaller.uninstall(packageName, LocalIntentSender.getIntentSender());
-        assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
-    }
-
     private static void assertStatusSuccess(Intent result) {
         int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
                 PackageInstaller.STATUS_FAILURE);
diff --git a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/ApexShimValidationTest.java b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/ApexShimValidationTest.java
index 9182ee9..094fd8d 100644
--- a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/ApexShimValidationTest.java
+++ b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/ApexShimValidationTest.java
@@ -21,6 +21,8 @@
 
 import static org.junit.Assume.assumeThat;
 
+import android.platform.test.annotations.LargeTest;
+
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
@@ -87,6 +89,7 @@
     }
 
     @Test
+    @LargeTest
     public void testRejectsApexWithAdditionalFile() throws Exception {
         runPhase("testRejectsApexWithAdditionalFile_Commit");
         getDevice().reboot();
@@ -94,6 +97,7 @@
     }
 
     @Test
+    @LargeTest
     public void testRejectsApexWithAdditionalFolder() throws Exception {
         runPhase("testRejectsApexWithAdditionalFolder_Commit");
         getDevice().reboot();
@@ -101,6 +105,7 @@
     }
 
     @Test
+    @LargeTest
     public void testRejectsApexWithPostInstallHook() throws Exception {
         runPhase("testRejectsApexWithPostInstallHook_Commit");
         getDevice().reboot();
@@ -108,6 +113,7 @@
     }
 
     @Test
+    @LargeTest
     public void testRejectsApexWithPreInstallHook() throws Exception {
         runPhase("testRejectsApexWithPreInstallHook_Commit");
         getDevice().reboot();
@@ -115,6 +121,7 @@
     }
 
     @Test
+    @LargeTest
     public void testRejectsApexWrongSHA() throws Exception {
         runPhase("testRejectsApexWrongSHA_Commit");
         getDevice().reboot();
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 41ad7ee..7999e34 100644
--- a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
@@ -24,6 +24,8 @@
 import static org.junit.Assume.assumeThat;
 import static org.junit.Assume.assumeTrue;
 
+import android.platform.test.annotations.LargeTest;
+
 import com.android.ddmlib.Log;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
@@ -73,9 +75,10 @@
     }
 
     /**
-     * Tests staged install involving only one apk.
+     * Tests for staged install involving only one apk.
      */
     @Test
+    @LargeTest
     public void testInstallStagedApk() throws Exception {
         runPhase("testInstallStagedApk_Commit");
         getDevice().reboot();
@@ -109,6 +112,7 @@
     }
 
     @Test
+    @LargeTest
     public void testAbandonStagedApkBeforeReboot() throws Exception {
         runPhase("testAbandonStagedApkBeforeReboot_CommitAndAbandon");
         getDevice().reboot();
@@ -116,6 +120,7 @@
     }
 
     @Test
+    @LargeTest
     public void testInstallMultipleStagedApks() throws Exception {
         runPhase("testInstallMultipleStagedApks_Commit");
         getDevice().reboot();
@@ -143,6 +148,7 @@
     }
 
     @Test
+    @LargeTest
     public void testStagedInstallDowngrade_DowngradeRequested_DebugBuild() throws Exception {
         assumeThat(getDevice().getBuildFlavor(), not(endsWith("-user")));
 
@@ -166,6 +172,7 @@
     }
 
     @Test
+    @LargeTest
     public void testInstallStagedApex() throws Exception {
         assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
 
@@ -198,6 +205,7 @@
     }
 
     @Test
+    @LargeTest
     public void testStageApkWithSameNameAsApexShouldFail() throws Exception {
         assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
 
@@ -213,6 +221,7 @@
     }
 
     @Test
+    @LargeTest
     public void testStagedInstallDowngradeApex_DowngradeNotRequested_Fails() throws Exception {
         assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
 
@@ -223,6 +232,7 @@
     }
 
     @Test
+    @LargeTest
     public void testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild() throws Exception {
         assumeThat(getDevice().getBuildFlavor(), not(endsWith("-user")));
         assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
@@ -234,6 +244,7 @@
     }
 
     @Test
+    @LargeTest
     public void testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails()
             throws Exception {
         assumeThat(getDevice().getBuildFlavor(), endsWith("-user"));
@@ -247,6 +258,19 @@
     }
 
     @Test
+    @LargeTest
+    public void testStagedInstallDowngradeApexToSystemVersion_DebugBuild() throws Exception {
+        assumeThat(getDevice().getBuildFlavor(), not(endsWith("-user")));
+        assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+        installV2Apex();
+        runPhase("testStagedInstallDowngradeApexToSystemVersion_DebugBuild_Commit");
+        getDevice().reboot();
+        runPhase("testStagedInstallDowngradeApexToSystemVersion_DebugBuild_VerifyPostReboot");
+    }
+
+    @Test
+    @LargeTest
     public void testInstallStagedApex_SameGrade() throws Exception {
         assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
 
@@ -261,6 +285,18 @@
         runPhase("testInstallApex_DeviceDoesNotSupportApex_Fails");
     }
 
+    private void installV2Apex()throws Exception {
+        runPhase("testInstallV2Apex_Commit");
+        getDevice().reboot();
+        runPhase("testInstallV2Apex_VerifyPostReboot");
+    }
+
+    private void installV2SignedBobApex() throws Exception {
+        runPhase("testInstallV2SignedBobApex_Commit");
+        getDevice().reboot();
+        runPhase("testInstallV2SignedBobApex_VerifyPostReboot");
+    }
+
     private void installV3Apex()throws Exception {
         runPhase("testInstallV3Apex_Commit");
         getDevice().reboot();
@@ -274,16 +310,95 @@
         runPhase("testFailsInvalidApexInstall_AbandonSessionIsNoop");
     }
 
-    private boolean isUpdatingApexSupported() throws Exception {
-        final String updatable = getDevice().getProperty("ro.apex.updatable");
-        return updatable != null && updatable.equals("true");
-    }
 
     @Test
     public void testStagedApkSessionCallbacks() throws Exception {
         runPhase("testStagedApkSessionCallbacks");
     }
 
+    @Test
+    @LargeTest
+    public void testInstallStagedApexWithoutApexSuffix() throws Exception {
+        assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+        runPhase("testInstallStagedApexWithoutApexSuffix_Commit");
+        getDevice().reboot();
+        runPhase("testInstallStagedApexWithoutApexSuffix_VerifyPostReboot");
+    }
+
+    @Test
+    public void testRejectsApexDifferentCertificate() throws Exception {
+        runPhase("testRejectsApexDifferentCertificate");
+    }
+
+    /**
+     * Tests for staged install involving rotated keys.
+     *
+     * Here alice means the original default key that cts.shim.v1 package was signed with and
+     * bob is the new key alice rotates to. Where ambiguous, we will refer keys as alice and bob
+     * instead of "old key" and "new key".
+     */
+    @Test
+    public void testUpdateWithDifferentKeyButNoRotation() throws Exception {
+        assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+        runPhase("testUpdateWithDifferentKeyButNoRotation");
+    }
+
+    @Test
+    @LargeTest
+    public void testUpdateWithDifferentKey() throws Exception {
+        assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+        runPhase("testUpdateWithDifferentKey_Commit");
+        getDevice().reboot();
+        runPhase("testUpdateWithDifferentKey_VerifyPostReboot");
+    }
+
+    @Test
+    @LargeTest
+    public void testAfterRotationOldKeyIsRejected() throws Exception {
+        assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+        installV2SignedBobApex();
+        runPhase("testAfterRotationOldKeyIsRejected");
+    }
+
+    @Test
+    @LargeTest
+    public void testAfterRotationNewKeyCanUpdateFurther() throws Exception {
+        assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+        installV2SignedBobApex();
+        runPhase("testAfterRotationNewKeyCanUpdateFurther_CommitPostReboot");
+        getDevice().reboot();
+        runPhase("testAfterRotationNewKeyCanUpdateFurther_VerifyPostReboot");
+    }
+
+    @Test
+    @LargeTest
+    public void testAfterRotationNewKeyCanUpdateFurtherWithoutLineage() throws Exception {
+        assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+        installV2SignedBobApex();
+        runPhase("testAfterRotationNewKeyCanUpdateFurtherWithoutLineage");
+    }
+
+    @Test
+    @LargeTest
+    public void testSamegradeSystemApex() throws Exception {
+        assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+        runPhase("testSamegradeSystemApex_Commit");
+        getDevice().reboot();
+        runPhase("testSamegradeSystemApex_VerifyPostReboot");
+    }
+
+    private boolean isUpdatingApexSupported() throws Exception {
+        final String updatable = getDevice().getProperty("ro.apex.updatable");
+        return updatable != null && updatable.equals("true");
+    }
+
     /**
      * Uninstalls a shim apex only if it's latest version is installed on /data partition (i.e.
      * it has a version higher than {@code 1}).
@@ -297,18 +412,18 @@
             // Device doesn't support updating apex. Nothing to uninstall.
             return;
         }
-        final ITestDevice.ApexInfo shimApex = getShimApex();
-        if (shimApex.versionCode == 1) {
-            // System version is active, skipping uninstalling active apex and rebooting the device.
-            return;
-        }
         // Non system version is active, need to uninstall it and reboot the device.
         final String errorMessage = getDevice().uninstallPackage(SHIM_APEX_PACKAGE_NAME);
-        Log.i(TAG, "Uninstalling shim apex " + shimApex);
-        if (errorMessage != null) {
-            throw new AssertionError("Failed to uninstall " + shimApex);
+        if (errorMessage == null) {
+            Log.i(TAG, "Uninstalling shim apex");
+            getDevice().reboot();
+        } else {
+            // Most likely we tried to uninstall system version and failed. It should be fine to
+            // continue tests.
+            // TODO(ioffe): extend getDevice().getActiveApexes() to include isInstalled/isFactory
+            //  and remove this terrible hack.
+            Log.w(TAG, "Failed to uninstall shim APEX : " + errorMessage);
         }
-        getDevice().reboot();
         assertThat(getShimApex().versionCode).isEqualTo(1L);
     }
 
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v1.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v1.apex
new file mode 100644
index 0000000..6ce5185
--- /dev/null
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v1.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2.apex
index 9dc33de..58b7a8e 100644
--- a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_file.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_file.apex
index 0ba9d99..53f0ed1 100644
--- a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_file.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_file.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_folder.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_folder.apex
index 1d3fcf5..a1ece1a 100644
--- a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_folder.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_folder.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_different_certificate.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_different_certificate.apex
new file mode 100644
index 0000000..6365654
--- /dev/null
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_different_certificate.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_signed_bob.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_signed_bob.apex
new file mode 100644
index 0000000..95fa522
--- /dev/null
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_signed_bob.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_signed_bob_rot.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_signed_bob_rot.apex
new file mode 100644
index 0000000..fb1f45b
--- /dev/null
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_signed_bob_rot.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_post_install_hook.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_post_install_hook.apex
index c32b139..437f7a8 100644
--- a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_post_install_hook.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_post_install_hook.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_pre_install_hook.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_pre_install_hook.apex
index 6a0fb0d..0e92266 100644
--- a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_pre_install_hook.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_pre_install_hook.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_wrong_sha.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_wrong_sha.apex
index 2388490..b1c77fa 100644
--- a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_wrong_sha.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_wrong_sha.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v3.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v3.apex
index 57ab7b9..5c54d73 100644
--- a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v3.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v3.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v3_signed_bob.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v3_signed_bob.apex
new file mode 100644
index 0000000..cf3e3eb
--- /dev/null
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v3_signed_bob.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v3_signed_bob_rot.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v3_signed_bob_rot.apex
new file mode 100644
index 0000000..7c59900
--- /dev/null
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v3_signed_bob_rot.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim_not_pre_installed.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim_not_pre_installed.apex
index 45715a2..788f475 100644
--- a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim_not_pre_installed.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim_not_pre_installed.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apk/Av1.xml b/hostsidetests/stagedinstall/testdata/apk/Av1.xml
deleted file mode 100644
index 234448c..0000000
--- a/hostsidetests/stagedinstall/testdata/apk/Av1.xml
+++ /dev/null
@@ -1,32 +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
-  -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.tests.stagedinstall.testapp.A"
-          android:versionCode="1"
-          android:versionName="1.0" >
-
-
-    <uses-sdk android:minSdkVersion="19" />
-
-    <application android:label="StagedInstall Test App A v1">
-        <activity android:name="com.android.tests.stagedinstall.testapp.MainActivity">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/hostsidetests/stagedinstall/testdata/apk/Av2.xml b/hostsidetests/stagedinstall/testdata/apk/Av2.xml
deleted file mode 100644
index dd5e512..0000000
--- a/hostsidetests/stagedinstall/testdata/apk/Av2.xml
+++ /dev/null
@@ -1,32 +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
-  -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.tests.stagedinstall.testapp.A"
-          android:versionCode="2"
-          android:versionName="2.0" >
-
-
-    <uses-sdk android:minSdkVersion="19" />
-
-    <application android:label="StagedInstall Test App A v2">
-        <activity android:name="com.android.tests.stagedinstall.testapp.MainActivity">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/hostsidetests/stagedinstall/testdata/apk/Bv1.xml b/hostsidetests/stagedinstall/testdata/apk/Bv1.xml
deleted file mode 100644
index 2a108b9..0000000
--- a/hostsidetests/stagedinstall/testdata/apk/Bv1.xml
+++ /dev/null
@@ -1,32 +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
-  -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.tests.stagedinstall.testapp.B"
-          android:versionCode="1"
-          android:versionName="1.0" >
-
-
-    <uses-sdk android:minSdkVersion="19" />
-
-    <application android:label="StagedInstall Test App B v1">
-        <activity android:name="com.android.tests.stagedinstall.testapp.MainActivity">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/hostsidetests/statsd/Android.bp b/hostsidetests/statsd/Android.bp
index 68a2d15..5f2d712 100644
--- a/hostsidetests/statsd/Android.bp
+++ b/hostsidetests/statsd/Android.bp
@@ -32,5 +32,8 @@
         "platformprotos",
         "truth-host-prebuilt",
     ],
-    data: ["**/*.pbtxt"],
+    data: [
+        "**/*.pbtxt",
+        ":CtsStatsdApp",
+    ],
 }
diff --git a/hostsidetests/statsd/apps/statsdapp/Android.bp b/hostsidetests/statsd/apps/statsdapp/Android.bp
index 01b07ec..b24d1c3 100644
--- a/hostsidetests/statsd/apps/statsdapp/Android.bp
+++ b/hostsidetests/statsd/apps/statsdapp/Android.bp
@@ -46,12 +46,6 @@
         "androidx.test.rules",
     ],
     jni_libs: ["liblmkhelper"],
-    // tag this module as a cts test artifact
-    test_suites: [
-        "cts",
-        "vts",
-        "general-tests",
-    ],
     compile_multilib: "both",
 }
 
diff --git a/hostsidetests/theme/OWNERS b/hostsidetests/theme/OWNERS
index ae2c27b..b43358c 100644
--- a/hostsidetests/theme/OWNERS
+++ b/hostsidetests/theme/OWNERS
@@ -1,2 +1,3 @@
 # Bug component: 25700
-alanv@google.com
\ No newline at end of file
+alanv@google.com
+aurimas@google.com
\ No newline at end of file
diff --git a/hostsidetests/theme/assets/29/140dpi.zip b/hostsidetests/theme/assets/29/140dpi.zip
index cb385f1..48de32b 100644
--- a/hostsidetests/theme/assets/29/140dpi.zip
+++ 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
index b034a7f..4def986 100644
--- a/hostsidetests/theme/assets/29/180dpi.zip
+++ 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
index 9a0ca5e..fe2495e 100644
--- a/hostsidetests/theme/assets/29/200dpi.zip
+++ 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
index b65a035..a2c2ea2 100644
--- a/hostsidetests/theme/assets/29/220dpi.zip
+++ 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 9cdefe7..74890a2 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 2e39de5..83fd290 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 fba9c6c..25334d8 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 72e6f8f..2092326 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 3970139..c13ad5c 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 510eb94d..1df1a95 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 a457bda..d58d418 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 07355d1..535102f 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 6a85ad8..20f1c7b 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 e1a534a..582989d 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 5475608..3035146 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 67c5c03..a831e7b 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 60f5afe..bd4bf75 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 d2895a1..0dd72e2 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 637649a..64fc846 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 9ab19b1..87beb9a 100644
--- a/hostsidetests/theme/assets/29/xxxhdpi.zip
+++ b/hostsidetests/theme/assets/29/xxxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/140dpi.zip b/hostsidetests/theme/assets/R/140dpi.zip
new file mode 100644
index 0000000..48de32b
--- /dev/null
+++ b/hostsidetests/theme/assets/R/140dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/180dpi.zip b/hostsidetests/theme/assets/R/180dpi.zip
new file mode 100644
index 0000000..4def986
--- /dev/null
+++ b/hostsidetests/theme/assets/R/180dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/200dpi.zip b/hostsidetests/theme/assets/R/200dpi.zip
new file mode 100644
index 0000000..fe2495e
--- /dev/null
+++ b/hostsidetests/theme/assets/R/200dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/220dpi.zip b/hostsidetests/theme/assets/R/220dpi.zip
new file mode 100644
index 0000000..a2c2ea2
--- /dev/null
+++ b/hostsidetests/theme/assets/R/220dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/260dpi.zip b/hostsidetests/theme/assets/R/260dpi.zip
index ba5d363..74890a2 100644
--- a/hostsidetests/theme/assets/R/260dpi.zip
+++ b/hostsidetests/theme/assets/R/260dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/280dpi.zip b/hostsidetests/theme/assets/R/280dpi.zip
index 34ea14c..83fd290 100644
--- a/hostsidetests/theme/assets/R/280dpi.zip
+++ b/hostsidetests/theme/assets/R/280dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/300dpi.zip b/hostsidetests/theme/assets/R/300dpi.zip
index 7595d24..25334d8 100644
--- a/hostsidetests/theme/assets/R/300dpi.zip
+++ b/hostsidetests/theme/assets/R/300dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/340dpi.zip b/hostsidetests/theme/assets/R/340dpi.zip
index 8ce960c..2092326 100644
--- a/hostsidetests/theme/assets/R/340dpi.zip
+++ b/hostsidetests/theme/assets/R/340dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/360dpi.zip b/hostsidetests/theme/assets/R/360dpi.zip
index aae3adc..c13ad5c 100644
--- a/hostsidetests/theme/assets/R/360dpi.zip
+++ b/hostsidetests/theme/assets/R/360dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/400dpi.zip b/hostsidetests/theme/assets/R/400dpi.zip
index 363d602..1df1a95 100644
--- a/hostsidetests/theme/assets/R/400dpi.zip
+++ b/hostsidetests/theme/assets/R/400dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/420dpi.zip b/hostsidetests/theme/assets/R/420dpi.zip
index 0f2ce47..d58d418 100644
--- a/hostsidetests/theme/assets/R/420dpi.zip
+++ b/hostsidetests/theme/assets/R/420dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/440dpi.zip b/hostsidetests/theme/assets/R/440dpi.zip
index 2328c61..535102f 100644
--- a/hostsidetests/theme/assets/R/440dpi.zip
+++ b/hostsidetests/theme/assets/R/440dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/560dpi.zip b/hostsidetests/theme/assets/R/560dpi.zip
index 5f1bb0b..20f1c7b 100644
--- a/hostsidetests/theme/assets/R/560dpi.zip
+++ b/hostsidetests/theme/assets/R/560dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/hdpi.zip b/hostsidetests/theme/assets/R/hdpi.zip
index 6d82318..582989d 100644
--- a/hostsidetests/theme/assets/R/hdpi.zip
+++ b/hostsidetests/theme/assets/R/hdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/ldpi.zip b/hostsidetests/theme/assets/R/ldpi.zip
index cc60027..3035146 100644
--- a/hostsidetests/theme/assets/R/ldpi.zip
+++ b/hostsidetests/theme/assets/R/ldpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/mdpi.zip b/hostsidetests/theme/assets/R/mdpi.zip
index 66d41d4..a831e7b 100644
--- a/hostsidetests/theme/assets/R/mdpi.zip
+++ b/hostsidetests/theme/assets/R/mdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/tvdpi.zip b/hostsidetests/theme/assets/R/tvdpi.zip
index b43032f..bd4bf75 100644
--- a/hostsidetests/theme/assets/R/tvdpi.zip
+++ b/hostsidetests/theme/assets/R/tvdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/xhdpi.zip b/hostsidetests/theme/assets/R/xhdpi.zip
index 64905f3..0dd72e2 100644
--- a/hostsidetests/theme/assets/R/xhdpi.zip
+++ b/hostsidetests/theme/assets/R/xhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/xxhdpi.zip b/hostsidetests/theme/assets/R/xxhdpi.zip
index b2cb422..64fc846 100644
--- a/hostsidetests/theme/assets/R/xxhdpi.zip
+++ b/hostsidetests/theme/assets/R/xxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/xxxhdpi.zip b/hostsidetests/theme/assets/R/xxxhdpi.zip
index d00dbbd..87beb9a 100644
--- a/hostsidetests/theme/assets/R/xxxhdpi.zip
+++ b/hostsidetests/theme/assets/R/xxxhdpi.zip
Binary files differ
diff --git a/hostsidetests/trustedvoice/OWNERS b/hostsidetests/trustedvoice/OWNERS
new file mode 100644
index 0000000..f96bd04
--- /dev/null
+++ b/hostsidetests/trustedvoice/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 174421
+sharmneha@google.com
\ No newline at end of file
diff --git a/libs/deviceutillegacy/Android.bp b/libs/deviceutillegacy/Android.bp
index bbdd399..220b2ab 100644
--- a/libs/deviceutillegacy/Android.bp
+++ b/libs/deviceutillegacy/Android.bp
@@ -13,23 +13,6 @@
 // limitations under the License.
 
 java_library_static {
-    name: "ctsdeviceutillegacy",
-
-    static_libs: [
-        "compatibility-device-util",
-        "junit",
-    ],
-
-    libs: ["android.test.base.stubs"],
-
-    srcs: ["src/**/*.java"],
-
-    sdk_version: "test_current",
-
-}
-
-// A variant of ctsdeviceutillegacy that depends on androidx.test instead of android.support.test
-java_library_static {
     name: "ctsdeviceutillegacy-axt",
 
     static_libs: [
diff --git a/libs/deviceutillegacy/src/android/webkit/cts/WebViewOnUiThread.java b/libs/deviceutillegacy/src/android/webkit/cts/WebViewOnUiThread.java
index 06425c1..6e64204 100644
--- a/libs/deviceutillegacy/src/android/webkit/cts/WebViewOnUiThread.java
+++ b/libs/deviceutillegacy/src/android/webkit/cts/WebViewOnUiThread.java
@@ -490,9 +490,7 @@
     }
 
     public void evaluateJavascript(final String script, final ValueCallback<String> result) {
-        WebkitUtils.onMainThreadSync(() -> {
-            mWebView.evaluateJavascript(script, result);
-        });
+        WebkitUtils.onMainThread(() -> mWebView.evaluateJavascript(script, result));
     }
 
     public void saveWebArchive(final String basename, final boolean autoname,
diff --git a/libs/install/Android.bp b/libs/install/Android.bp
new file mode 100644
index 0000000..5ca15ae
--- /dev/null
+++ b/libs/install/Android.bp
@@ -0,0 +1,97 @@
+// 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.
+
+
+android_test_helper_app {
+    name: "TestAppAv1",
+    manifest: "testapp/Av1.xml",
+    sdk_version: "current",
+    srcs: ["testapp/src/**/*.java"],
+    resource_dirs: ["testapp/res_v1"],
+}
+
+android_test_helper_app {
+    name: "TestAppAv2",
+    manifest: "testapp/Av2.xml",
+    sdk_version: "current",
+    srcs: ["testapp/src/**/*.java"],
+    resource_dirs: ["testapp/res_v2"],
+}
+
+android_test_helper_app {
+    name: "TestAppAv3",
+    manifest: "testapp/Av3.xml",
+    sdk_version: "current",
+    srcs: ["testapp/src/**/*.java"],
+    resource_dirs: ["testapp/res_v3"],
+}
+
+android_test_helper_app {
+    name: "TestAppACrashingV2",
+    manifest: "testapp/ACrashingV2.xml",
+    sdk_version: "current",
+    srcs: ["testapp/src/**/*.java"],
+    resource_dirs: ["testapp/res_v2"],
+}
+
+android_test_helper_app {
+    name: "TestAppBv1",
+    manifest: "testapp/Bv1.xml",
+    sdk_version: "current",
+    srcs: ["testapp/src/**/*.java"],
+    resource_dirs: ["testapp/res_v1"],
+}
+
+android_test_helper_app {
+    name: "TestAppBv2",
+    manifest: "testapp/Bv2.xml",
+    sdk_version: "current",
+    srcs: ["testapp/src/**/*.java"],
+    resource_dirs: ["testapp/res_v2"],
+}
+
+android_test_helper_app {
+    name: "TestAppASplitV1",
+    manifest: "testapp/Av1.xml",
+    sdk_version: "current",
+    srcs: ["testapp/src/**/*.java"],
+    resource_dirs: ["testapp/res_v1"],
+    package_splits: ["anydpi"],
+}
+
+android_test_helper_app {
+    name: "TestAppASplitV2",
+    manifest: "testapp/Av2.xml",
+    sdk_version: "current",
+    srcs: ["testapp/src/**/*.java"],
+    resource_dirs: ["testapp/res_v2"],
+    package_splits: ["anydpi"],
+}
+
+java_library {
+    name: "cts-install-lib",
+    srcs: ["src/**/*.java"],
+    static_libs: ["androidx.test.rules", "truth-prebuilt"],
+    sdk_version: "test_current",
+    java_resources: [
+        ":TestAppAv1",
+        ":TestAppAv2",
+	":TestAppAv3",
+        ":TestAppBv1",
+        ":TestAppBv2",
+	":TestAppACrashingV2",
+	":TestAppASplitV1",
+	":TestAppASplitV2",
+    ],
+}
diff --git a/libs/install/TEST_MAPPING b/libs/install/TEST_MAPPING
new file mode 100644
index 0000000..9d0ffe9
--- /dev/null
+++ b/libs/install/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "imports": [
+    {
+      "path": "cts/tests/tests/util"
+    }
+  ]
+}
diff --git a/libs/install/src/com/android/cts/install/lib/Install.java b/libs/install/src/com/android/cts/install/lib/Install.java
new file mode 100644
index 0000000..11fbffe
--- /dev/null
+++ b/libs/install/src/com/android/cts/install/lib/Install.java
@@ -0,0 +1,243 @@
+/*
+ * 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.install.lib;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Intent;
+import android.content.pm.PackageInstaller;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Builder class for installing test apps and creating install sessions.
+ */
+public class Install {
+    // The collection of apps to be installed with parameters inherited from parent Install object.
+    private final TestApp[] mTestApps;
+    // The collection of apps to be installed with parameters independent of parent Install object.
+    private final Install[] mChildInstalls;
+    // Indicates whether Install represents a multiPackage install.
+    private final boolean mIsMultiPackage;
+    // PackageInstaller.Session parameters.
+    private boolean mIsStaged = false;
+    private boolean mIsDowngrade = false;
+    private boolean mEnableRollback = false;
+    private int mSessionMode = PackageInstaller.SessionParams.MODE_FULL_INSTALL;
+    private int mInstallFlags = 0;
+
+    private Install(boolean isMultiPackage, TestApp... testApps) {
+        mIsMultiPackage = isMultiPackage;
+        mTestApps = testApps;
+        mChildInstalls = new Install[0];
+    }
+
+    private Install(boolean isMultiPackage, Install... installs) {
+        mIsMultiPackage = isMultiPackage;
+        mTestApps = new TestApp[0];
+        mChildInstalls = installs;
+    }
+
+    /**
+     * Creates an Install builder to install a single package.
+     */
+    public static Install single(TestApp testApp) {
+        return new Install(false, testApp);
+    }
+
+    /**
+     * Creates an Install builder to install using multiPackage.
+     */
+    public static Install multi(TestApp... testApps) {
+        return new Install(true, testApps);
+    }
+
+    /**
+     * Creates an Install builder from separate Install builders. The newly created builder
+     * will be responsible for building the parent session, while each one of the other builders
+     * will be responsible for building one of the child sessions.
+     *
+     * <p>Modifications to the parent install are not propagated to the child installs,
+     * and vice versa. This gives more control over a multi install session,
+     * e.g. can setStaged on a subset of the child sessions or setStaged on a child session but
+     * not on the parent session.
+     *
+     * <p>It's encouraged to use {@link #multi} that receives {@link TestApp}s
+     * instead of {@link Install}s. This variation of {@link #multi} should be used only if it's
+     * necessary to modify parameters in a subset of the installed sessions.
+     */
+    public static Install multi(Install... installs) {
+        for (Install childInstall : installs) {
+            assertThat(childInstall.isMultiPackage()).isFalse();
+        }
+        Install install = new Install(true, installs);
+        return install;
+    }
+
+    /**
+     * Makes the install a staged install.
+     */
+    public Install setStaged() {
+        mIsStaged = true;
+        return this;
+    }
+
+    /**
+     * Marks the install as a downgrade.
+     */
+    public Install setRequestDowngrade() {
+        mIsDowngrade = true;
+        return this;
+    }
+
+    /**
+     * Enables rollback for the install.
+     */
+    public Install setEnableRollback() {
+        mEnableRollback = true;
+        return this;
+    }
+
+    /**
+     * Sets the session mode {@link PackageInstaller.SessionParams#MODE_INHERIT_EXISTING}.
+     * If it's not set, then the default session mode is
+     * {@link PackageInstaller.SessionParams#MODE_FULL_INSTALL}
+     */
+    public Install setSessionMode(int sessionMode) {
+        mSessionMode = sessionMode;
+        return this;
+    }
+
+    /**
+     * Sets the session params.
+     */
+    public Install addInstallFlags(int installFlags) {
+        mInstallFlags |= installFlags;
+        return this;
+    }
+
+    /**
+     * Commits the install.
+     *
+     * @return the session id of the install session, if the session is successful.
+     * @throws AssertionError if the install doesn't succeed.
+     */
+    public int commit() throws IOException, InterruptedException {
+        int sessionId = createSession();
+        PackageInstaller.Session session = InstallUtils.openPackageInstallerSession(sessionId);
+        session.commit(LocalIntentSender.getIntentSender());
+        Intent result = LocalIntentSender.getIntentSenderResult();
+        int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                PackageInstaller.STATUS_FAILURE);
+        if (status == -1) {
+            throw new AssertionError("PENDING USER ACTION");
+        } else if (status > 0) {
+            String message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
+            throw new AssertionError(message == null ? "UNKNOWN FAILURE" : message);
+        }
+
+        if (mIsStaged) {
+            InstallUtils.waitForSessionReady(sessionId);
+        }
+        return sessionId;
+    }
+
+    /**
+     * Kicks off an install flow by creating an install session
+     * and, in the case of a multiPackage install, child install sessions.
+     *
+     * @return the session id of the install session, if the session is successful.
+     */
+    public int createSession() throws IOException {
+        int sessionId;
+        if (isMultiPackage()) {
+            PackageInstaller.Session session;
+            sessionId = createEmptyInstallSession(/*multiPackage*/ true, /*isApex*/false);
+            session = InstallUtils.openPackageInstallerSession(sessionId);
+            for (Install subInstall : mChildInstalls) {
+                session.addChildSessionId(subInstall.createSession());
+            }
+            for (TestApp testApp : mTestApps) {
+                session.addChildSessionId(createSingleInstallSession(testApp));
+            }
+        } else {
+            assert mTestApps.length == 1;
+            sessionId = createSingleInstallSession(mTestApps[0]);
+        }
+        return sessionId;
+    }
+
+    /**
+     * Creates an empty install session with appropriate install params set.
+     *
+     * @return the session id of the newly created session
+     */
+    private int createEmptyInstallSession(boolean multiPackage, boolean isApex)
+            throws IOException {
+        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(mSessionMode);
+        if (multiPackage) {
+            params.setMultiPackage();
+        }
+        if (isApex) {
+            params.setInstallAsApex();
+        }
+        if (mIsStaged) {
+            params.setStaged();
+        }
+        params.setRequestDowngrade(mIsDowngrade);
+        params.setEnableRollback(mEnableRollback);
+        if (mInstallFlags != 0) {
+            InstallUtils.mutateInstallFlags(params, mInstallFlags);
+        }
+        return InstallUtils.getPackageInstaller().createSession(params);
+    }
+
+    /**
+     * Creates an install session for the given test app.
+     *
+     * @return the session id of the newly created session.
+     */
+    private int createSingleInstallSession(TestApp app) throws IOException {
+        int sessionId = createEmptyInstallSession(/*multiPackage*/false, app.isApex());
+        PackageInstaller.Session session = InstallUtils.getPackageInstaller()
+                .openSession(sessionId);
+
+        ClassLoader loader = TestApp.class.getClassLoader();
+        for (String resourceName : app.getResourceNames()) {
+            try (OutputStream os = session.openWrite(resourceName, 0, -1);
+                 InputStream is = loader.getResourceAsStream(resourceName);) {
+                if (is == null) {
+                    throw new IOException("Resource " + resourceName + " not found");
+                }
+                byte[] buffer = new byte[4096];
+                int n;
+                while ((n = is.read(buffer)) >= 0) {
+                    os.write(buffer, 0, n);
+                }
+            }
+        }
+        session.close();
+        return sessionId;
+    }
+
+    private boolean isMultiPackage() {
+        return mIsMultiPackage;
+    }
+
+}
diff --git a/libs/install/src/com/android/cts/install/lib/InstallUtils.java b/libs/install/src/com/android/cts/install/lib/InstallUtils.java
new file mode 100644
index 0000000..6969398
--- /dev/null
+++ b/libs/install/src/com/android/cts/install/lib/InstallUtils.java
@@ -0,0 +1,294 @@
+/*
+ * 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.install.lib;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * Utilities to facilitate installation in tests.
+ */
+public class InstallUtils {
+    /**
+     * Adopts the given shell permissions.
+     */
+    public static void adoptShellPermissionIdentity(String... permissions) {
+        InstrumentationRegistry
+                .getInstrumentation()
+                .getUiAutomation()
+                .adoptShellPermissionIdentity(permissions);
+    }
+
+    /**
+     * Drops all shell permissions.
+     */
+    public static void dropShellPermissionIdentity() {
+        InstrumentationRegistry
+                .getInstrumentation()
+                .getUiAutomation()
+                .dropShellPermissionIdentity();
+    }
+    /**
+     * Returns the version of the given package installed on device.
+     * Returns -1 if the package is not currently installed.
+     */
+    public static long getInstalledVersion(String packageName) {
+        Context context = InstrumentationRegistry.getContext();
+        PackageManager pm = context.getPackageManager();
+        try {
+            PackageInfo info = pm.getPackageInfo(packageName, PackageManager.MATCH_APEX);
+            return info.getLongVersionCode();
+        } catch (PackageManager.NameNotFoundException e) {
+            return -1;
+        }
+    }
+
+    /**
+     * Waits for the given session to be marked as ready.
+     * Throws an assertion if the session fails.
+     */
+    public static void waitForSessionReady(int sessionId) {
+        BlockingQueue<PackageInstaller.SessionInfo> sessionStatus = new LinkedBlockingQueue<>();
+        BroadcastReceiver sessionUpdatedReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                PackageInstaller.SessionInfo info =
+                        intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION);
+                if (info != null && info.getSessionId() == sessionId) {
+                    if (info.isStagedSessionReady() || info.isStagedSessionFailed()) {
+                        try {
+                            sessionStatus.put(info);
+                        } catch (InterruptedException e) {
+                            throw new AssertionError(e);
+                        }
+                    }
+                }
+            }
+        };
+        IntentFilter sessionUpdatedFilter =
+                new IntentFilter(PackageInstaller.ACTION_SESSION_UPDATED);
+
+        Context context = InstrumentationRegistry.getContext();
+        context.registerReceiver(sessionUpdatedReceiver, sessionUpdatedFilter);
+
+        PackageInstaller installer = getPackageInstaller();
+        PackageInstaller.SessionInfo info = installer.getSessionInfo(sessionId);
+
+        try {
+            if (info.isStagedSessionReady() || info.isStagedSessionFailed()) {
+                sessionStatus.put(info);
+            }
+
+            info = sessionStatus.take();
+            context.unregisterReceiver(sessionUpdatedReceiver);
+            if (info.isStagedSessionFailed()) {
+                throw new AssertionError(info.getStagedSessionErrorMessage());
+            }
+        } catch (InterruptedException e) {
+            throw new AssertionError(e);
+        }
+    }
+
+    /**
+     * Returns the info for the given package name.
+     */
+    public static PackageInfo getPackageInfo(String packageName) {
+        Context context = InstrumentationRegistry.getContext();
+        PackageManager pm = context.getPackageManager();
+        try {
+            return pm.getPackageInfo(packageName, PackageManager.MATCH_APEX);
+        } catch (PackageManager.NameNotFoundException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the PackageInstaller instance of the current {@code Context}
+     */
+    public static PackageInstaller getPackageInstaller() {
+        return InstrumentationRegistry.getContext().getPackageManager().getPackageInstaller();
+    }
+
+    /**
+     * Returns an existing session to actively perform work.
+     * {@see PackageInstaller#openSession}
+     */
+    public static PackageInstaller.Session openPackageInstallerSession(int sessionId)
+            throws IOException {
+        return getPackageInstaller().openSession(sessionId);
+    }
+
+    /**
+     * Asserts that {@code result} intent has a success status.
+     */
+    public static void assertStatusSuccess(Intent result) {
+        int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                PackageInstaller.STATUS_FAILURE);
+        if (status == -1) {
+            throw new AssertionError("PENDING USER ACTION");
+        } else if (status > 0) {
+            String message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
+            throw new AssertionError(message == null ? "UNKNOWN FAILURE" : message);
+        }
+    }
+
+    /**
+     * Commits {@link Install} but expects to fail.
+     *
+     * @param expectedThrowableClass class or superclass of the expected throwable.
+     *
+     */
+    public static void commitExpectingFailure(Class expectedThrowableClass,
+            String expectedFailMessage, Install install) {
+        assertThrows(expectedThrowableClass, expectedFailMessage, () -> install.commit());
+    }
+
+    /**
+     * Mutates {@code installFlags} field of {@code params} by adding {@code
+     * additionalInstallFlags} to it.
+     */
+    @VisibleForTesting
+    public static void mutateInstallFlags(PackageInstaller.SessionParams params,
+            int additionalInstallFlags) {
+        final Class<?> clazz = params.getClass();
+        Field installFlagsField;
+        try {
+            installFlagsField = clazz.getDeclaredField("installFlags");
+        } catch (NoSuchFieldException e) {
+            throw new AssertionError("Unable to reflect over SessionParams.installFlags", e);
+        }
+
+        try {
+            int flags = installFlagsField.getInt(params);
+            flags |= additionalInstallFlags;
+            installFlagsField.setAccessible(true);
+            installFlagsField.setInt(params, flags);
+        } catch (IllegalAccessException e) {
+            throw new AssertionError("Unable to reflect over SessionParams.installFlags", e);
+        }
+    }
+
+    private static final String NO_RESPONSE = "NO RESPONSE";
+
+    /**
+     * Calls into the test app to process user data.
+     * Asserts if the user data could not be processed or was version
+     * incompatible with the previously processed user data.
+     */
+    public static void processUserData(String packageName) {
+        Intent intent = new Intent();
+        intent.setComponent(new ComponentName(packageName,
+                "com.android.cts.install.lib.testapp.ProcessUserData"));
+        Context context = InstrumentationRegistry.getContext();
+
+        HandlerThread handlerThread = new HandlerThread("RollbackTestHandlerThread");
+        handlerThread.start();
+
+        // It can sometimes take a while after rollback before the app will
+        // receive this broadcast, so try a few times in a loop.
+        String result = NO_RESPONSE;
+        for (int i = 0; result.equals(NO_RESPONSE) && i < 5; ++i) {
+            BlockingQueue<String> resultQueue = new LinkedBlockingQueue<>();
+            context.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    if (getResultCode() == 1) {
+                        resultQueue.add("OK");
+                    } else {
+                        // If the test app doesn't receive the broadcast or
+                        // fails to set the result data, then getResultData
+                        // here returns the initial NO_RESPONSE data passed to
+                        // the sendOrderedBroadcast call.
+                        resultQueue.add(getResultData());
+                    }
+                }
+            }, new Handler(handlerThread.getLooper()), 0, NO_RESPONSE, null);
+
+            try {
+                result = resultQueue.take();
+            } catch (InterruptedException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        assertThat(result).isEqualTo("OK");
+    }
+
+    /**
+     * Checks whether the given package is installed on /system and was not updated.
+     */
+    static boolean isSystemAppWithoutUpdate(String packageName) {
+        PackageInfo pi = getPackageInfo(packageName);
+        if (pi == null) {
+            return false;
+        } else {
+            return ((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)
+                    && ((pi.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0);
+        }
+    }
+
+    /**
+     * A functional interface representing an operation that takes no arguments,
+     * returns no arguments and might throw a {@link Throwable} of any kind.
+     */
+    @FunctionalInterface
+    private interface Operation {
+        /**
+         * This is the method that gets called for any object that implements this interface.
+         */
+        void run() throws Throwable;
+    }
+
+    /**
+     * Runs {@link Operation} and expects a {@link Throwable} of the given class to be thrown.
+     *
+     * @param expectedThrowableClass class or superclass of the expected throwable.
+     */
+    private static void assertThrows(Class expectedThrowableClass, String expectedFailMessage,
+            Operation operation) {
+        try {
+            operation.run();
+        } catch (Throwable expected) {
+            assertThat(expectedThrowableClass.isAssignableFrom(expected.getClass())).isTrue();
+            assertThat(expected.getMessage()).containsMatch(expectedFailMessage);
+            return;
+        }
+        fail("Operation was expected to fail!");
+    }
+}
diff --git a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/LocalIntentSender.java b/libs/install/src/com/android/cts/install/lib/LocalIntentSender.java
similarity index 88%
rename from hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/LocalIntentSender.java
rename to libs/install/src/com/android/cts/install/lib/LocalIntentSender.java
index d4b3f48..cf2abe8 100644
--- a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/LocalIntentSender.java
+++ b/libs/install/src/com/android/cts/install/lib/LocalIntentSender.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.tests.stagedinstall;
+package com.android.cts.install.lib;
 
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
@@ -29,8 +29,13 @@
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
 
+/**
+ * Helper for making IntentSenders whose results are sent back to the test
+ * app.
+ */
 public class LocalIntentSender extends BroadcastReceiver {
-    private static final String TAG = "StagedInstallTest";
+    private static final String TAG = "cts.install.lib";
+
     private static final BlockingQueue<Intent> sIntentSenderResults = new LinkedBlockingQueue<>();
 
     @Override
@@ -42,7 +47,7 @@
     /**
      * Get a LocalIntentSender.
      */
-    static IntentSender getIntentSender() {
+    public static IntentSender getIntentSender() {
         Context context = InstrumentationRegistry.getContext();
         Intent intent = new Intent(context, LocalIntentSender.class);
         PendingIntent pending = PendingIntent.getBroadcast(context, 0, intent, 0);
@@ -52,7 +57,7 @@
     /**
      * Returns the most recent Intent sent by a LocalIntentSender.
      */
-    static Intent getIntentSenderResult() throws InterruptedException {
+    public static Intent getIntentSenderResult() throws InterruptedException {
         Intent intent = sIntentSenderResults.take();
         Log.i(TAG, "Taking intent " + prettyPrint(intent));
         return intent;
diff --git a/libs/install/src/com/android/cts/install/lib/TestApp.java b/libs/install/src/com/android/cts/install/lib/TestApp.java
new file mode 100644
index 0000000..5704887
--- /dev/null
+++ b/libs/install/src/com/android/cts/install/lib/TestApp.java
@@ -0,0 +1,105 @@
+/*
+ * 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.install.lib;
+
+import android.content.pm.VersionedPackage;
+
+/**
+ * Collection of dummy apps used in tests.
+ */
+public class TestApp {
+    public static final String A = "com.android.cts.install.lib.testapp.A";
+    public static final String B = "com.android.cts.install.lib.testapp.B";
+    public static final String Apex = "com.android.apex.cts.shim";
+    public static final String NotPreInstalledApex = "com.android.apex.cts.shim_not_pre_installed";
+
+    // Apk collection
+    public static final TestApp A1 = new TestApp("Av1", A, 1, /*isApex*/false,
+            "TestAppAv1.apk");
+    public static final TestApp A2 = new TestApp("Av2", A, 2, /*isApex*/false,
+            "TestAppAv2.apk");
+    public static final TestApp A3 = new TestApp("Av3", A, 3, /*isApex*/false,
+            "TestAppAv3.apk");
+    public static final TestApp ACrashing2 = new TestApp("ACrashingV2", A, 2, /*isApex*/false,
+            "TestAppACrashingV2.apk");
+    public static final TestApp ASplit1 = new TestApp("ASplitV1", A, 1, /*isApex*/false,
+            "TestAppASplitV1.apk", "TestAppASplitV1_anydpi.apk");
+    public static final TestApp ASplit2 = new TestApp("ASplitV2", A, 2, /*isApex*/false,
+            "TestAppASplitV2.apk", "TestAppASplitV2_anydpi.apk");
+
+    public static final TestApp B1 = new TestApp("Bv1", B, 1, /*isApex*/false,
+            "TestAppBv1.apk");
+    public static final TestApp B2 = new TestApp("Bv2", B, 2, /*isApex*/false,
+            "TestAppBv2.apk");
+
+    // Apex collection
+    public static final TestApp Apex1 =
+            new TestApp("Apex1", Apex, 1, /*isApex*/true,
+                    "com.android.apex.cts.shim.v1.apex");
+    public static final TestApp Apex2 =
+            new TestApp("Apex2", Apex, 2, /*isApex*/true,
+            "com.android.apex.cts.shim.v2.apex");
+    public static final TestApp ApexWrongSha2 = new TestApp(
+            "ApexWrongSha2", Apex, 2, /*isApex*/true,
+            "com.android.apex.cts.shim.v2_wrong_sha.apex");
+    public static final TestApp Apex3 =
+            new TestApp("Apex3", Apex, 3, /*isApex*/true,
+            "com.android.apex.cts.shim.v3.apex");
+    public static final TestApp ApexNotPreInstalled =
+            new TestApp("ApexNotPreInstalled", NotPreInstalledApex, 3, /*isApex*/true,
+            "com.android.apex.cts.shim_not_pre_installed.apex");
+
+    private final String mName;
+    private final String mPackageName;
+    private final long mVersionCode;
+    private final String[] mResourceNames;
+    private final boolean mIsApex;
+
+    public TestApp(String name, String packageName, long versionCode, boolean isApex,
+            String... resourceNames) {
+        mName = name;
+        mPackageName = packageName;
+        mVersionCode = versionCode;
+        mResourceNames = resourceNames;
+        mIsApex = isApex;
+    }
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    public long getVersionCode() {
+        return mVersionCode;
+    }
+
+    public VersionedPackage getVersionedPackage() {
+        return new VersionedPackage(mPackageName, mVersionCode);
+    }
+
+    @Override
+    public String toString() {
+        return mName;
+    }
+
+    boolean isApex() {
+        return mIsApex;
+    }
+
+    String[] getResourceNames() {
+        return mResourceNames;
+    }
+}
diff --git a/libs/install/src/com/android/cts/install/lib/Uninstall.java b/libs/install/src/com/android/cts/install/lib/Uninstall.java
new file mode 100644
index 0000000..0444130
--- /dev/null
+++ b/libs/install/src/com/android/cts/install/lib/Uninstall.java
@@ -0,0 +1,52 @@
+/*
+ * 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.install.lib;
+
+import android.content.Context;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+
+import androidx.test.InstrumentationRegistry;
+
+/**
+ * Helper class for uninstalling test apps.
+ */
+public class Uninstall {
+    /**
+     * Uninstalls all of the given packages.
+     * Does nothing if the package is not installed or installed on /system
+     */
+    public static void packages(String... packageNames) throws InterruptedException {
+        for (String pkg : packageNames) {
+            uninstallSinglePackage(pkg);
+        }
+    }
+
+    private static void uninstallSinglePackage(String packageName) throws InterruptedException {
+        // No need to uninstall if the package isn't installed.
+        if (InstallUtils.getInstalledVersion(packageName) == -1
+                || InstallUtils.isSystemAppWithoutUpdate(packageName)) {
+            return;
+        }
+
+        Context context = InstrumentationRegistry.getContext();
+        PackageManager packageManager = context.getPackageManager();
+        PackageInstaller packageInstaller = packageManager.getPackageInstaller();
+        packageInstaller.uninstall(packageName, LocalIntentSender.getIntentSender());
+        InstallUtils.assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
+    }
+}
diff --git a/libs/rollback/testapp/A2.xml b/libs/install/testapp/ACrashingV2.xml
similarity index 63%
copy from libs/rollback/testapp/A2.xml
copy to libs/install/testapp/ACrashingV2.xml
index d879a91..338a5b9 100644
--- a/libs/rollback/testapp/A2.xml
+++ b/libs/install/testapp/ACrashingV2.xml
@@ -15,18 +15,21 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.cts.rollback.lib.testapp.A"
+    package="com.android.cts.install.lib.testapp.A"
     android:versionCode="2"
     android:versionName="2.0" >
 
 
     <uses-sdk android:minSdkVersion="19" />
 
-    <application android:label="Rollback Test App A2">
-        <activity android:name="com.android.cts.rollback.lib.testapp.MainActivity">
+    <application android:label="Test App A v2">
+        <receiver android:name="com.android.cts.install.lib.testapp.ProcessUserData"
+                  android:exported="true" />
+        <activity android:name="com.android.cts.install.lib.testapp.CrashingMainActivity">
             <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
+              <action android:name="android.intent.action.MAIN" />
+              <category android:name="android.intent.category.DEFAULT"/>
+              <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
     </application>
diff --git a/libs/rollback/testapp/A1.xml b/libs/install/testapp/Av1.xml
similarity index 77%
copy from libs/rollback/testapp/A1.xml
copy to libs/install/testapp/Av1.xml
index 2cd1825..e9714fc 100644
--- a/libs/rollback/testapp/A1.xml
+++ b/libs/install/testapp/Av1.xml
@@ -15,15 +15,17 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.cts.rollback.lib.testapp.A"
+    package="com.android.cts.install.lib.testapp.A"
     android:versionCode="1"
     android:versionName="1.0" >
 
 
     <uses-sdk android:minSdkVersion="19" />
 
-    <application android:label="Rollback Test App A1">
-        <activity android:name="com.android.cts.rollback.lib.testapp.MainActivity">
+    <application android:label="Test App A1">
+        <receiver android:name="com.android.cts.install.lib.testapp.ProcessUserData"
+                  android:exported="true" />
+        <activity android:name="com.android.cts.install.lib.testapp.MainActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
diff --git a/libs/rollback/testapp/A2.xml b/libs/install/testapp/Av2.xml
similarity index 78%
rename from libs/rollback/testapp/A2.xml
rename to libs/install/testapp/Av2.xml
index d879a91..fd8afa0 100644
--- a/libs/rollback/testapp/A2.xml
+++ b/libs/install/testapp/Av2.xml
@@ -15,15 +15,17 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.cts.rollback.lib.testapp.A"
+    package="com.android.cts.install.lib.testapp.A"
     android:versionCode="2"
     android:versionName="2.0" >
 
 
     <uses-sdk android:minSdkVersion="19" />
 
-    <application android:label="Rollback Test App A2">
-        <activity android:name="com.android.cts.rollback.lib.testapp.MainActivity">
+    <application android:label="Test App A2">
+        <receiver android:name="com.android.cts.install.lib.testapp.ProcessUserData"
+            android:exported="true" />
+        <activity android:name="com.android.cts.install.lib.testapp.MainActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
diff --git a/libs/rollback/testapp/A1.xml b/libs/install/testapp/Av3.xml
similarity index 73%
copy from libs/rollback/testapp/A1.xml
copy to libs/install/testapp/Av3.xml
index 2cd1825..a7839e3 100644
--- a/libs/rollback/testapp/A1.xml
+++ b/libs/install/testapp/Av3.xml
@@ -15,15 +15,17 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.cts.rollback.lib.testapp.A"
-    android:versionCode="1"
-    android:versionName="1.0" >
+    package="com.android.cts.install.lib.testapp.A"
+    android:versionCode="3"
+    android:versionName="3.0" >
 
 
     <uses-sdk android:minSdkVersion="19" />
 
-    <application android:label="Rollback Test App A1">
-        <activity android:name="com.android.cts.rollback.lib.testapp.MainActivity">
+    <application android:label="Test App A3">
+        <receiver android:name="com.android.cts.install.lib.testapp.ProcessUserData"
+            android:exported="true" />
+        <activity android:name="com.android.cts.install.lib.testapp.MainActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
diff --git a/libs/rollback/testapp/A1.xml b/libs/install/testapp/Bv1.xml
similarity index 78%
rename from libs/rollback/testapp/A1.xml
rename to libs/install/testapp/Bv1.xml
index 2cd1825..403e7e2 100644
--- a/libs/rollback/testapp/A1.xml
+++ b/libs/install/testapp/Bv1.xml
@@ -15,15 +15,17 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.cts.rollback.lib.testapp.A"
+    package="com.android.cts.install.lib.testapp.B"
     android:versionCode="1"
     android:versionName="1.0" >
 
 
     <uses-sdk android:minSdkVersion="19" />
 
-    <application android:label="Rollback Test App A1">
-        <activity android:name="com.android.cts.rollback.lib.testapp.MainActivity">
+    <application android:label="Test App B1">
+        <receiver android:name="com.android.cts.install.lib.testapp.ProcessUserData"
+            android:exported="true" />
+        <activity android:name="com.android.cts.install.lib.testapp.MainActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
diff --git a/libs/rollback/testapp/A2.xml b/libs/install/testapp/Bv2.xml
similarity index 78%
copy from libs/rollback/testapp/A2.xml
copy to libs/install/testapp/Bv2.xml
index d879a91..f030c3f 100644
--- a/libs/rollback/testapp/A2.xml
+++ b/libs/install/testapp/Bv2.xml
@@ -15,15 +15,17 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.cts.rollback.lib.testapp.A"
+    package="com.android.cts.install.lib.testapp.B"
     android:versionCode="2"
     android:versionName="2.0" >
 
 
     <uses-sdk android:minSdkVersion="19" />
 
-    <application android:label="Rollback Test App A2">
-        <activity android:name="com.android.cts.rollback.lib.testapp.MainActivity">
+    <application android:label="Test App B2">
+        <receiver android:name="com.android.cts.install.lib.testapp.ProcessUserData"
+            android:exported="true" />
+        <activity android:name="com.android.cts.install.lib.testapp.MainActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
diff --git a/libs/rollback/testapp/res_v1/values/values.xml b/libs/install/testapp/res_v1/values-anydpi/values.xml
similarity index 93%
rename from libs/rollback/testapp/res_v1/values/values.xml
rename to libs/install/testapp/res_v1/values-anydpi/values.xml
index 8e71917..90d3da2 100644
--- a/libs/rollback/testapp/res_v1/values/values.xml
+++ b/libs/install/testapp/res_v1/values-anydpi/values.xml
@@ -15,5 +15,5 @@
 -->
 
 <resources>
-    <integer name="app_version">1</integer>
+    <integer name="split_version">1</integer>
 </resources>
diff --git a/libs/rollback/testapp/res_v1/values/values.xml b/libs/install/testapp/res_v1/values/values.xml
similarity index 93%
copy from libs/rollback/testapp/res_v1/values/values.xml
copy to libs/install/testapp/res_v1/values/values.xml
index 8e71917..0447c74 100644
--- a/libs/rollback/testapp/res_v1/values/values.xml
+++ b/libs/install/testapp/res_v1/values/values.xml
@@ -16,4 +16,5 @@
 
 <resources>
     <integer name="app_version">1</integer>
+    <integer name="split_version">0</integer>
 </resources>
diff --git a/libs/rollback/testapp/res_v1/values/values.xml b/libs/install/testapp/res_v2/values-anydpi/values.xml
similarity index 93%
copy from libs/rollback/testapp/res_v1/values/values.xml
copy to libs/install/testapp/res_v2/values-anydpi/values.xml
index 8e71917..9a1aa7f 100644
--- a/libs/rollback/testapp/res_v1/values/values.xml
+++ b/libs/install/testapp/res_v2/values-anydpi/values.xml
@@ -15,5 +15,5 @@
 -->
 
 <resources>
-    <integer name="app_version">1</integer>
+    <integer name="split_version">2</integer>
 </resources>
diff --git a/libs/rollback/testapp/res_v2/values/values.xml b/libs/install/testapp/res_v2/values/values.xml
similarity index 93%
rename from libs/rollback/testapp/res_v2/values/values.xml
rename to libs/install/testapp/res_v2/values/values.xml
index dac8dd6..fd988f5 100644
--- a/libs/rollback/testapp/res_v2/values/values.xml
+++ b/libs/install/testapp/res_v2/values/values.xml
@@ -16,4 +16,5 @@
 
 <resources>
     <integer name="app_version">2</integer>
+    <integer name="split_version">0</integer>
 </resources>
diff --git a/libs/rollback/testapp/res_v1/values/values.xml b/libs/install/testapp/res_v3/values-anydpi/values.xml
similarity index 93%
copy from libs/rollback/testapp/res_v1/values/values.xml
copy to libs/install/testapp/res_v3/values-anydpi/values.xml
index 8e71917..f2d8992 100644
--- a/libs/rollback/testapp/res_v1/values/values.xml
+++ b/libs/install/testapp/res_v3/values-anydpi/values.xml
@@ -15,5 +15,5 @@
 -->
 
 <resources>
-    <integer name="app_version">1</integer>
+    <integer name="split_version">3</integer>
 </resources>
diff --git a/libs/rollback/testapp/res_v1/values/values.xml b/libs/install/testapp/res_v3/values/values.xml
similarity index 88%
copy from libs/rollback/testapp/res_v1/values/values.xml
copy to libs/install/testapp/res_v3/values/values.xml
index 8e71917..968168a 100644
--- a/libs/rollback/testapp/res_v1/values/values.xml
+++ b/libs/install/testapp/res_v3/values/values.xml
@@ -15,5 +15,6 @@
 -->
 
 <resources>
-    <integer name="app_version">1</integer>
+    <integer name="app_version">3</integer>
+    <integer name="split_version">0</integer>
 </resources>
diff --git a/libs/install/testapp/src/com/android/cts/install/lib/testapp/CrashingMainActivity.java b/libs/install/testapp/src/com/android/cts/install/lib/testapp/CrashingMainActivity.java
new file mode 100644
index 0000000..cbaccf7
--- /dev/null
+++ b/libs/install/testapp/src/com/android/cts/install/lib/testapp/CrashingMainActivity.java
@@ -0,0 +1,46 @@
+/*
+ * 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.install.lib.testapp;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+
+/**
+ * A crashing test app for testing apk rollback support.
+ */
+public class CrashingMainActivity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        incrementCountAndBroadcast();
+        throw new RuntimeException("Intended force crash");
+    }
+
+    private void incrementCountAndBroadcast() {
+        SharedPreferences preferences = getSharedPreferences("prefs", Context.MODE_PRIVATE);
+        SharedPreferences.Editor editor = preferences.edit();
+        int count = preferences.getInt("crash_count", 0);
+        editor.putInt("crash_count", ++count).commit();
+
+        Intent intent = new Intent("com.android.tests.rollback.CRASH");
+        intent.putExtra("count", count);
+        sendBroadcast(intent);
+    }
+}
diff --git a/tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java b/libs/install/testapp/src/com/android/cts/install/lib/testapp/MainActivity.java
similarity index 67%
copy from tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java
copy to libs/install/testapp/src/com/android/cts/install/lib/testapp/MainActivity.java
index 37276e2..ab2d90b 100644
--- a/tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java
+++ b/libs/install/testapp/src/com/android/cts/install/lib/testapp/MainActivity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,15 +14,24 @@
  * limitations under the License.
  */
 
-package com.android.tests.atomicinstall.testapp;
+package com.android.cts.install.lib.testapp;
 
 import android.app.Activity;
 import android.os.Bundle;
 
+/**
+ * A test app for testing apk rollback support.
+ */
 public class MainActivity extends Activity {
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+
+        try {
+            new ProcessUserData().processUserData(this);
+        } catch (ProcessUserData.UserDataException e) {
+            throw new AssertionError("Failed to process app user data", e);
+        }
     }
 }
diff --git a/libs/install/testapp/src/com/android/cts/install/lib/testapp/ProcessUserData.java b/libs/install/testapp/src/com/android/cts/install/lib/testapp/ProcessUserData.java
new file mode 100644
index 0000000..842a674
--- /dev/null
+++ b/libs/install/testapp/src/com/android/cts/install/lib/testapp/ProcessUserData.java
@@ -0,0 +1,109 @@
+/*
+ * 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 com.android.cts.install.lib.testapp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Scanner;
+
+/**
+ * A broadcast receiver to check for and update user app data version
+ * compatibility.
+ */
+public class ProcessUserData extends BroadcastReceiver {
+
+    /**
+     * Exception thrown in case of issue with user data.
+     */
+    public static class UserDataException extends Exception {
+        public UserDataException(String message) {
+            super(message);
+        }
+
+        public UserDataException(String message, Throwable cause) {
+           super(message, cause);
+        }
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        try {
+            processUserData(context);
+            setResultCode(1);
+        } catch (UserDataException e) {
+            setResultCode(0);
+            setResultData(e.getMessage());
+        }
+    }
+
+    /**
+     * Update the app's user data version to match the app version.
+     *
+     * @param context The application context.
+     * @throws UserDataException in case of problems with app user data.
+     */
+    public void processUserData(Context context) throws UserDataException {
+        Resources res = context.getResources();
+        String packageName = context.getPackageName();
+
+        int appVersionId = res.getIdentifier("app_version", "integer", packageName);
+        int appVersion = res.getInteger(appVersionId);
+
+        int splitVersionId = res.getIdentifier("split_version", "integer", packageName);
+        int splitVersion = res.getInteger(splitVersionId);
+
+        // Make sure the app version and split versions are compatible.
+        if (appVersion != splitVersion) {
+            throw new UserDataException("Split version " + splitVersion
+                    + " does not match app version " + appVersion);
+        }
+
+        // Read the version of the app's user data and ensure it is compatible
+        // with our version of the application.
+        File versionFile = new File(context.getFilesDir(), "version.txt");
+        try {
+            Scanner s = new Scanner(versionFile);
+            int userDataVersion = s.nextInt();
+            s.close();
+
+            if (userDataVersion > appVersion) {
+                throw new UserDataException("User data is from version " + userDataVersion
+                        + ", which is not compatible with this version " + appVersion
+                        + " of the RollbackTestApp");
+            }
+        } catch (FileNotFoundException e) {
+            // No problem. This is a fresh install of the app or the user data
+            // has been wiped.
+        }
+
+        // Record the current version of the app in the user data.
+        try {
+            PrintWriter pw = new PrintWriter(versionFile);
+            pw.println(appVersion);
+            pw.close();
+        } catch (IOException e) {
+            throw new UserDataException("Unable to write user data.", e);
+        }
+    }
+}
diff --git a/libs/rollback/Android.bp b/libs/rollback/Android.bp
index b4d31e2..3e4d0a3 100644
--- a/libs/rollback/Android.bp
+++ b/libs/rollback/Android.bp
@@ -12,29 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-android_test_helper_app {
-    name: "RollbackManagerTestAppA1",
-    manifest: "testapp/A1.xml",
-    sdk_version: "current",
-    srcs: ["testapp/src/**/*.java"],
-    resource_dirs: ["testapp/res_v1"],
-}
-
-android_test_helper_app {
-    name: "RollbackManagerTestAppA2",
-    manifest: "testapp/A2.xml",
-    sdk_version: "current",
-    srcs: ["testapp/src/**/*.java"],
-    resource_dirs: ["testapp/res_v2"],
-}
-
 java_library {
     name: "cts-rollback-lib",
     srcs: ["src/**/*.java"],
-    static_libs: ["androidx.test.rules", "truth-prebuilt"],
+    static_libs: ["androidx.test.rules", "truth-prebuilt", "cts-install-lib"],
     sdk_version: "test_current",
-    java_resources: [
-        ":RollbackManagerTestAppA1",
-        ":RollbackManagerTestAppA2",
-    ],
 }
diff --git a/libs/rollback/src/com/android/cts/rollback/lib/Install.java b/libs/rollback/src/com/android/cts/rollback/lib/Install.java
deleted file mode 100644
index caa39a0..0000000
--- a/libs/rollback/src/com/android/cts/rollback/lib/Install.java
+++ /dev/null
@@ -1,155 +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.
- */
-
-package com.android.cts.rollback.lib;
-
-import android.content.Intent;
-import android.content.pm.PackageInstaller;
-
-import androidx.test.InstrumentationRegistry;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * Helper class for installing test apps.
- */
-public class Install {
-    private final boolean mIsMultiPackage;
-    private final TestApp[] mTestApps;
-    private boolean mIsStaged = false;
-    private boolean mEnableRollback = false;
-
-    private Install(boolean isMultiPackage, TestApp... testApps) {
-        mIsMultiPackage = isMultiPackage;
-        mTestApps = testApps;
-    }
-
-    /**
-     * Creates an Install builder to install a single package.
-     */
-    public static Install single(TestApp testApp) {
-        return new Install(false, testApp);
-    }
-
-    /**
-     * Creates an Install builder to install using multiPackage.
-     */
-    public static Install multi(TestApp... testApps) {
-        return new Install(true, testApps);
-    }
-
-    /**
-     * Makes the install a staged install.
-     */
-    public Install setStaged() {
-        mIsStaged = true;
-        return this;
-    }
-
-    /**
-     * Enables rollback for the install.
-     */
-    public Install setEnableRollback() {
-        mEnableRollback = true;
-        return this;
-    }
-
-    private static PackageInstaller getPackageInstaller() {
-        return InstrumentationRegistry.getContext().getPackageManager().getPackageInstaller();
-    }
-
-    /**
-     * Creates an empty install session with appropriate install params set.
-     *
-     * @return the session id of the newly created session
-     */
-    private int createEmptyInstallSession(boolean multiPackage, boolean isApex)
-            throws IOException {
-        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
-                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
-        if (multiPackage) {
-            params.setMultiPackage();
-        }
-        if (isApex) {
-            params.setInstallAsApex();
-        }
-        if (mIsStaged) {
-            params.setStaged();
-        }
-        params.setEnableRollback(mEnableRollback);
-        return getPackageInstaller().createSession(params);
-    }
-
-    /**
-     * Creates an install session for the given test app.
-     *
-     * @return the session id of the newly created session.
-     */
-    private int createInstallSession(TestApp app) throws IOException {
-        int sessionId = createEmptyInstallSession(/*multiPackage*/false, app.isApex());
-        PackageInstaller.Session session = getPackageInstaller().openSession(sessionId);
-
-        ClassLoader loader = TestApp.class.getClassLoader();
-        for (String resourceName : app.getResourceNames()) {
-            try (OutputStream os = session.openWrite(resourceName, 0, -1);
-                    InputStream is = loader.getResourceAsStream(resourceName);) {
-                byte[] buffer = new byte[4096];
-                int n;
-                while ((n = is.read(buffer)) >= 0) {
-                    os.write(buffer, 0, n);
-                }
-            }
-        }
-        session.close();
-        return sessionId;
-    }
-
-    /**
-     * Commits the install.
-     */
-    public void commit() throws IOException, InterruptedException {
-        final int sessionId;
-        final PackageInstaller.Session session;
-        if (mIsMultiPackage) {
-            sessionId = createEmptyInstallSession(/*multiPackage*/ true, /*isApex*/false);
-            session = getPackageInstaller().openSession(sessionId);
-            for (TestApp app : mTestApps) {
-                session.addChildSessionId(createInstallSession(app));
-            }
-        } else {
-            assert mTestApps.length == 1;
-            sessionId = createInstallSession(mTestApps[0]);
-            session = getPackageInstaller().openSession(sessionId);
-        }
-
-        session.commit(LocalIntentSender.getIntentSender());
-        Intent result = LocalIntentSender.getIntentSenderResult();
-        int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
-                PackageInstaller.STATUS_FAILURE);
-        if (status == -1) {
-            throw new AssertionError("PENDING USER ACTION");
-        } else if (status > 0) {
-            String message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
-            throw new AssertionError(message == null ? "UNKNOWN FAILURE" : message);
-        }
-
-        if (mIsStaged) {
-            Utils.waitForSessionReady(sessionId);
-        }
-    }
-}
diff --git a/libs/rollback/src/com/android/cts/rollback/lib/LocalIntentSender.java b/libs/rollback/src/com/android/cts/rollback/lib/LocalIntentSender.java
deleted file mode 100644
index bd8f0dd..0000000
--- a/libs/rollback/src/com/android/cts/rollback/lib/LocalIntentSender.java
+++ /dev/null
@@ -1,59 +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.
- */
-
-package com.android.cts.rollback.lib;
-
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
-
-import androidx.test.InstrumentationRegistry;
-
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-
-/**
- * Helper for making IntentSenders whose results are sent back to the test
- * app.
- */
-public class LocalIntentSender extends BroadcastReceiver {
-
-    private static final BlockingQueue<Intent> sIntentSenderResults = new LinkedBlockingQueue<>();
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        sIntentSenderResults.add(intent);
-    }
-
-    /**
-     * Get a LocalIntentSender.
-     */
-    static IntentSender getIntentSender() {
-        Context context = InstrumentationRegistry.getContext();
-        Intent intent = new Intent(context, LocalIntentSender.class);
-        PendingIntent pending = PendingIntent.getBroadcast(context, 0, intent, 0);
-        return pending.getIntentSender();
-    }
-
-    /**
-     * Returns the most recent Intent sent by a LocalIntentSender.
-     */
-    static Intent getIntentSenderResult() throws InterruptedException {
-        return sIntentSenderResults.take();
-    }
-}
diff --git a/libs/rollback/src/com/android/cts/rollback/lib/Rollback.java b/libs/rollback/src/com/android/cts/rollback/lib/Rollback.java
index 14e19bb..bb0bc60 100644
--- a/libs/rollback/src/com/android/cts/rollback/lib/Rollback.java
+++ b/libs/rollback/src/com/android/cts/rollback/lib/Rollback.java
@@ -19,6 +19,8 @@
 import android.content.pm.VersionedPackage;
 import android.content.rollback.PackageRollbackInfo;
 
+import com.android.cts.install.lib.TestApp;
+
 /**
  * Helper class for asserting PackageRollbackInfo contents.
  */
diff --git a/libs/rollback/src/com/android/cts/rollback/lib/RollbackBroadcastReceiver.java b/libs/rollback/src/com/android/cts/rollback/lib/RollbackBroadcastReceiver.java
new file mode 100644
index 0000000..15438e4
--- /dev/null
+++ b/libs/rollback/src/com/android/cts/rollback/lib/RollbackBroadcastReceiver.java
@@ -0,0 +1,78 @@
+/*
+ * 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 com.android.cts.rollback.lib;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A broadcast receiver that can be used to get
+ * ACTION_ROLLBACK_COMMITTED broadcasts.
+ */
+public class RollbackBroadcastReceiver extends BroadcastReceiver {
+
+    private static final String TAG = "RollbackTest";
+
+    private final BlockingQueue<Intent> mRollbackBroadcasts = new LinkedBlockingQueue<>();
+
+    /**
+     * Creates a RollbackBroadcastReceiver and registers it with the given
+     * context.
+     */
+    public RollbackBroadcastReceiver() {
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_ROLLBACK_COMMITTED);
+        InstrumentationRegistry.getContext().registerReceiver(this, filter);
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Log.i(TAG, "Received rollback broadcast intent");
+        mRollbackBroadcasts.add(intent);
+    }
+
+    /**
+     * Polls for at most the given amount of time for the next rollback
+     * broadcast.
+     */
+    public Intent poll(long timeout, TimeUnit unit) throws InterruptedException {
+        return mRollbackBroadcasts.poll(timeout, unit);
+    }
+
+    /**
+     * Waits forever for the next rollback broadcast.
+     */
+    public Intent take() throws InterruptedException {
+        return mRollbackBroadcasts.take();
+    }
+
+    /**
+     * Unregisters this broadcast receiver.
+     */
+    public void unregister() {
+        InstrumentationRegistry.getContext().unregisterReceiver(this);
+    }
+}
diff --git a/libs/rollback/src/com/android/cts/rollback/lib/RollbackInfoSubject.java b/libs/rollback/src/com/android/cts/rollback/lib/RollbackInfoSubject.java
index 8bc9247..e81a89e 100644
--- a/libs/rollback/src/com/android/cts/rollback/lib/RollbackInfoSubject.java
+++ b/libs/rollback/src/com/android/cts/rollback/lib/RollbackInfoSubject.java
@@ -20,6 +20,8 @@
 import android.content.rollback.PackageRollbackInfo;
 import android.content.rollback.RollbackInfo;
 
+import com.android.cts.install.lib.TestApp;
+
 import com.google.common.truth.FailureMetadata;
 import com.google.common.truth.Subject;
 import com.google.common.truth.Truth;
diff --git a/libs/rollback/src/com/android/cts/rollback/lib/RollbackUtils.java b/libs/rollback/src/com/android/cts/rollback/lib/RollbackUtils.java
new file mode 100644
index 0000000..08ba87a
--- /dev/null
+++ b/libs/rollback/src/com/android/cts/rollback/lib/RollbackUtils.java
@@ -0,0 +1,264 @@
+/*
+ * 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.rollback.lib;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.ActivityManager;
+import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.VersionedPackage;
+import android.content.rollback.PackageRollbackInfo;
+import android.content.rollback.RollbackInfo;
+import android.content.rollback.RollbackManager;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.cts.install.lib.LocalIntentSender;
+import com.android.cts.install.lib.TestApp;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+/**
+ * Utilities to facilitate testing rollbacks.
+ */
+public class RollbackUtils {
+
+    private static final String TAG = "RollbackTest";
+
+    /**
+     * Time between repeated checks in {@link #retry}.
+     */
+    private static final long RETRY_CHECK_INTERVAL_MILLIS = 500;
+
+    /**
+     * Maximum number of checks in {@link #retry} before a timeout occurs.
+     */
+    private static final long RETRY_MAX_INTERVALS = 20;
+
+
+    /**
+     * Gets the RollbackManager for the instrumentation context.
+     */
+    public static RollbackManager getRollbackManager() {
+        Context context = InstrumentationRegistry.getContext();
+        RollbackManager rm = (RollbackManager) context.getSystemService(Context.ROLLBACK_SERVICE);
+        if (rm == null) {
+            throw new AssertionError("Failed to get RollbackManager");
+        }
+        return rm;
+    }
+
+    /**
+     * Returns a rollback for the given rollback Id, if found. Otherwise, returns null.
+     */
+    private static RollbackInfo getRollbackById(List<RollbackInfo> rollbacks, int rollbackId) {
+        for (RollbackInfo rollback :rollbacks) {
+            if (rollback.getRollbackId() == rollbackId) {
+                return rollback;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns an available rollback for the given package name. Returns null
+     * if there are no available rollbacks, and throws an assertion if there
+     * is more than one.
+     */
+    public static RollbackInfo getAvailableRollback(String packageName) {
+        RollbackManager rm = getRollbackManager();
+        return getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), packageName);
+    }
+
+    /**
+     * Returns a recently committed rollback for the given package name. Returns null
+     * if there are no available rollbacks, and throws an assertion if there
+     * is more than one.
+     */
+    public static RollbackInfo getCommittedRollback(String packageName) {
+        RollbackManager rm = getRollbackManager();
+        return getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(), packageName);
+    }
+
+    /**
+     * Returns a recently committed rollback for the given rollback Id.
+     * Returns null if no committed rollback with a matching Id was found.
+     */
+    public static RollbackInfo getCommittedRollbackById(int rollbackId) {
+        RollbackManager rm = getRollbackManager();
+        return getRollbackById(rm.getRecentlyCommittedRollbacks(), rollbackId);
+    }
+
+    /**
+     * Commit the given rollback.
+     * @throws AssertionError if the rollback fails.
+     */
+    public static void rollback(int rollbackId, TestApp... causePackages)
+            throws InterruptedException {
+        List<VersionedPackage> causes = new ArrayList<>();
+        for (TestApp cause : causePackages) {
+            causes.add(cause.getVersionedPackage());
+        }
+
+        RollbackManager rm = getRollbackManager();
+        rm.commitRollback(rollbackId, causes, LocalIntentSender.getIntentSender());
+        Intent result = LocalIntentSender.getIntentSenderResult();
+        int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
+                RollbackManager.STATUS_FAILURE);
+        if (status != RollbackManager.STATUS_SUCCESS) {
+            String message = result.getStringExtra(RollbackManager.EXTRA_STATUS_MESSAGE);
+            throw new AssertionError(message);
+        }
+    }
+
+    /**
+     * Forwards the device clock time by {@code offsetMillis}.
+     */
+    public static void forwardTimeBy(long offsetMillis) {
+        setTime(System.currentTimeMillis() + offsetMillis);
+        Log.i(TAG, "Forwarded time on device by " + offsetMillis + " millis");
+    }
+
+    /**
+     * Returns the RollbackInfo with a given package in the list of rollbacks.
+     * Throws an assertion failure if there is more than one such rollback
+     * info. Returns null if there are no such rollback infos.
+     */
+    public static RollbackInfo getUniqueRollbackInfoForPackage(List<RollbackInfo> rollbacks,
+            String packageName) {
+        RollbackInfo found = null;
+        for (RollbackInfo rollback : rollbacks) {
+            for (PackageRollbackInfo info : rollback.getPackages()) {
+                if (packageName.equals(info.getPackageName())) {
+                    assertThat(found).isNull();
+                    found = rollback;
+                    break;
+                }
+            }
+        }
+        return found;
+    }
+
+    /**
+     * Returns an available rollback matching the specified package name. If no such rollback is
+     * available, getAvailableRollbacks is called repeatedly until one becomes available. An
+     * assertion is raised if this does not occur after a certain number of checks.
+     */
+    public static RollbackInfo waitForAvailableRollback(String packageName)
+            throws InterruptedException {
+        return retry(() -> getAvailableRollback(packageName),
+                Objects::nonNull, "Rollback did not become available.");
+    }
+
+    /**
+     * If there is no available rollback matching the specified package name, this returns
+     * immediately. If such a rollback is available, getAvailableRollbacks is called repeatedly
+     * until it is no longer available. An assertion is raised if this does not occur after a
+     * certain number of checks.
+     */
+    public static void waitForUnavailableRollback(String packageName) throws InterruptedException {
+        retry(() -> getAvailableRollback(packageName), Objects::isNull,
+                "Rollback did not become unavailable");
+    }
+
+    private static <T> T retry(Supplier<T> supplier, Predicate<T> predicate, String message)
+            throws InterruptedException {
+        for (int i = 0; i < RETRY_MAX_INTERVALS; i++) {
+            T result = supplier.get();
+            if (predicate.test(result)) {
+                return result;
+            }
+            Thread.sleep(RETRY_CHECK_INTERVAL_MILLIS);
+        }
+        throw new AssertionError(message);
+    }
+
+    /**
+     * Send broadcast to crash {@code packageName} {@code count} times. If {@code count} is at least
+     * {@link PackageWatchdog#TRIGGER_FAILURE_COUNT}, watchdog crash detection will be triggered.
+     */
+    public static void sendCrashBroadcast(String packageName,
+            int count) throws InterruptedException, IOException {
+        for (int i = 0; i < count; ++i) {
+            launchPackageForCrash(packageName);
+        }
+    }
+
+    private static void setTime(long millis) {
+        Context context = InstrumentationRegistry.getContext();
+        AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+        am.setTime(millis);
+    }
+
+    /**
+     * Launches {@code packageName} with {@link Intent#ACTION_MAIN} and
+     * waits for a CRASH broadcast from the launched app.
+     */
+    private static void launchPackageForCrash(String packageName)
+            throws InterruptedException, IOException {
+        // Force stop the package before launching it to make sure it isn't
+        // stuck in a non-launchable state. And wait a second afterwards to
+        // avoid interfering with when we launch the app.
+        Log.i(TAG, "Force stopping " + packageName);
+        Context context = InstrumentationRegistry.getContext();
+        ActivityManager am = context.getSystemService(ActivityManager.class);
+        am.forceStopPackage(packageName);
+        Thread.sleep(1000);
+
+        // Register a receiver to listen for the CRASH broadcast.
+        CountDownLatch latch = new CountDownLatch(1);
+        IntentFilter crashFilter = new IntentFilter();
+        crashFilter.addAction("com.android.tests.rollback.CRASH");
+        crashFilter.addCategory(Intent.CATEGORY_DEFAULT);
+        BroadcastReceiver crashReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                Log.i(TAG, "Received CRASH broadcast from " + packageName);
+                latch.countDown();
+            }
+        };
+        context.registerReceiver(crashReceiver, crashFilter);
+
+        // Launch the app.
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.setPackage(packageName);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.addCategory(Intent.CATEGORY_LAUNCHER);
+        Log.i(TAG, "Launching " + packageName + " with " + intent);
+        context.startActivity(intent);
+
+        Log.i(TAG, "Waiting for CRASH broadcast from " + packageName);
+        latch.await();
+
+        context.unregisterReceiver(crashReceiver);
+
+        // Sleep long enough for packagewatchdog to be notified of crash
+        Thread.sleep(1000);
+    }
+}
+
diff --git a/libs/rollback/src/com/android/cts/rollback/lib/TestApp.java b/libs/rollback/src/com/android/cts/rollback/lib/TestApp.java
deleted file mode 100644
index 530ead7..0000000
--- a/libs/rollback/src/com/android/cts/rollback/lib/TestApp.java
+++ /dev/null
@@ -1,76 +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.
- */
-
-package com.android.cts.rollback.lib;
-
-import android.content.pm.VersionedPackage;
-
-/**
- * Collection of dummy apps used in tests.
- */
-public class TestApp {
-    public static final String A = "com.android.cts.rollback.lib.testapp.A";
-    public static final String Apex = "com.android.apex.cts.shim";
-
-    public static final TestApp A1 = new TestApp("A1", A, 1, /*isApex*/false,
-            "RollbackManagerTestAppA1.apk");
-    public static final TestApp A2 = new TestApp("A2", A, 2, /*isApex*/false,
-            "RollbackManagerTestAppA2.apk");
-    public static final TestApp Apex2 = new TestApp("Apex2", Apex, 2, /*isApex*/true,
-            "com.android.apex.cts.shim.v2.apex");
-    public static final TestApp Apex3 = new TestApp("Apex3", Apex, 3, /*isApex*/true,
-            "com.android.apex.cts.shim.v3.apex");
-
-    private final String mName;
-    private final String mPackageName;
-    private final long mVersionCode;
-    private final String[] mResourceNames;
-    private final boolean mIsApex;
-
-    public TestApp(String name, String packageName, long versionCode, boolean isApex,
-            String... resourceNames) {
-        mName = name;
-        mPackageName = packageName;
-        mVersionCode = versionCode;
-        mResourceNames = resourceNames;
-        mIsApex = isApex;
-    }
-
-    String getPackageName() {
-        return mPackageName;
-    }
-
-    long getVersionCode() {
-        return mVersionCode;
-    }
-
-    String[] getResourceNames() {
-        return mResourceNames;
-    }
-
-    VersionedPackage getVersionedPackage() {
-        return new VersionedPackage(mPackageName, mVersionCode);
-    }
-
-    boolean isApex() {
-        return mIsApex;
-    }
-
-    @Override
-    public String toString() {
-        return mName;
-    }
-}
diff --git a/libs/rollback/src/com/android/cts/rollback/lib/Utils.java b/libs/rollback/src/com/android/cts/rollback/lib/Utils.java
deleted file mode 100644
index 668f641..0000000
--- a/libs/rollback/src/com/android/cts/rollback/lib/Utils.java
+++ /dev/null
@@ -1,226 +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.
- */
-
-package com.android.cts.rollback.lib;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageManager;
-import android.content.pm.VersionedPackage;
-import android.content.rollback.PackageRollbackInfo;
-import android.content.rollback.RollbackInfo;
-import android.content.rollback.RollbackManager;
-
-import androidx.test.InstrumentationRegistry;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-
-/**
- * Utilities to facilitate testing rollbacks.
- */
-public class Utils {
-    /**
-     * Returns the version of the given package installed on device.
-     * Returns -1 if the package is not currently installed.
-     */
-    public static long getInstalledVersion(String packageName) {
-        Context context = InstrumentationRegistry.getContext();
-        PackageManager pm = context.getPackageManager();
-        try {
-            PackageInfo info = pm.getPackageInfo(packageName, PackageManager.MATCH_APEX);
-            return info.getLongVersionCode();
-        } catch (PackageManager.NameNotFoundException e) {
-            return -1;
-        }
-    }
-
-    /**
-     * Gets the RollbackManager for the instrumentation context.
-     */
-    public static RollbackManager getRollbackManager() {
-        Context context = InstrumentationRegistry.getContext();
-        RollbackManager rm = (RollbackManager) context.getSystemService(Context.ROLLBACK_SERVICE);
-        if (rm == null) {
-            throw new AssertionError("Failed to get RollbackManager");
-        }
-        return rm;
-    }
-
-    /**
-     * Returns a rollback for the given package name in the list of
-     * rollbacks. Returns null if there are no available rollbacks, and throws
-     * an assertion if there is more than one.
-     */
-    private static RollbackInfo getRollback(List<RollbackInfo> rollbacks, String packageName) {
-        RollbackInfo found = null;
-        for (RollbackInfo rollback : rollbacks) {
-            for (PackageRollbackInfo info : rollback.getPackages()) {
-                if (packageName.equals(info.getPackageName())) {
-                    if (found != null) {
-                        throw new AssertionError("Multiple available matching rollbacks found");
-                    }
-                    found = rollback;
-                    break;
-                }
-            }
-        }
-        return found;
-    }
-
-    /**
-     * Returns a rollback for the given rollback Id, if found. Otherwise, returns null.
-     */
-    private static RollbackInfo getRollbackById(List<RollbackInfo> rollbacks, int rollbackId) {
-        for (RollbackInfo rollback :rollbacks) {
-            if (rollback.getRollbackId() == rollbackId) {
-                return rollback;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns an available rollback for the given package name. Returns null
-     * if there are no available rollbacks, and throws an assertion if there
-     * is more than one.
-     */
-    public static RollbackInfo getAvailableRollback(String packageName) {
-        RollbackManager rm = getRollbackManager();
-        return getRollback(rm.getAvailableRollbacks(), packageName);
-    }
-
-    /**
-     * Returns a recently committed rollback for the given package name. Returns null
-     * if there are no available rollbacks, and throws an assertion if there
-     * is more than one.
-     */
-    public static RollbackInfo getCommittedRollback(String packageName) {
-        RollbackManager rm = getRollbackManager();
-        return getRollback(rm.getRecentlyCommittedRollbacks(), packageName);
-    }
-
-    /**
-     * Returns a recently committed rollback for the given rollback Id.
-     * Returns null if no committed rollback with a matching Id was found.
-     */
-    public static RollbackInfo getCommittedRollbackById(int rollbackId) {
-        RollbackManager rm = getRollbackManager();
-        return getRollbackById(rm.getRecentlyCommittedRollbacks(), rollbackId);
-    }
-
-    /**
-     * Uninstalls the given package.
-     * Does nothing if the package is not installed.
-     * @throws AssertionError if package can't be uninstalled.
-     */
-    public static void uninstall(String packageName) throws InterruptedException, IOException {
-        // No need to uninstall if the package isn't installed.
-        if (getInstalledVersion(packageName) == -1) {
-            return;
-        }
-
-        Context context = InstrumentationRegistry.getContext();
-        PackageManager packageManager = context.getPackageManager();
-        PackageInstaller packageInstaller = packageManager.getPackageInstaller();
-        packageInstaller.uninstall(packageName, LocalIntentSender.getIntentSender());
-        Intent result = LocalIntentSender.getIntentSenderResult();
-        int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
-                PackageInstaller.STATUS_FAILURE);
-        if (status == -1) {
-            throw new AssertionError("PENDING USER ACTION");
-        } else if (status > 0) {
-            String message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
-            throw new AssertionError(message == null ? "UNKNOWN FAILURE" : message);
-        }
-    }
-
-    /**
-     * Commit the given rollback.
-     * @throws AssertionError if the rollback fails.
-     */
-    public static void rollback(int rollbackId, TestApp... causePackages)
-            throws InterruptedException {
-        List<VersionedPackage> causes = new ArrayList<>();
-        for (TestApp cause : causePackages) {
-            causes.add(cause.getVersionedPackage());
-        }
-
-        RollbackManager rm = getRollbackManager();
-        rm.commitRollback(rollbackId, causes, LocalIntentSender.getIntentSender());
-        Intent result = LocalIntentSender.getIntentSenderResult();
-        int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
-                RollbackManager.STATUS_FAILURE);
-        if (status != RollbackManager.STATUS_SUCCESS) {
-            String message = result.getStringExtra(RollbackManager.EXTRA_STATUS_MESSAGE);
-            throw new AssertionError(message);
-        }
-    }
-
-    /**
-     * Waits for the given session to be marked as ready.
-     * Throws an assertion if the session fails.
-     */
-    public static void waitForSessionReady(int sessionId) {
-        BlockingQueue<PackageInstaller.SessionInfo> sessionStatus = new LinkedBlockingQueue<>();
-        BroadcastReceiver sessionUpdatedReceiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                PackageInstaller.SessionInfo info =
-                        intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION);
-                if (info != null && info.getSessionId() == sessionId) {
-                    if (info.isStagedSessionReady() || info.isStagedSessionFailed()) {
-                        try {
-                            sessionStatus.put(info);
-                        } catch (InterruptedException e) {
-                            throw new AssertionError(e);
-                        }
-                    }
-                }
-            }
-        };
-        IntentFilter sessionUpdatedFilter =
-                new IntentFilter(PackageInstaller.ACTION_SESSION_UPDATED);
-
-        Context context = InstrumentationRegistry.getContext();
-        context.registerReceiver(sessionUpdatedReceiver, sessionUpdatedFilter);
-
-        PackageInstaller installer = context.getPackageManager().getPackageInstaller();
-        PackageInstaller.SessionInfo info = installer.getSessionInfo(sessionId);
-
-        try {
-            if (info.isStagedSessionReady() || info.isStagedSessionFailed()) {
-                sessionStatus.put(info);
-            }
-
-            info = sessionStatus.take();
-            context.unregisterReceiver(sessionUpdatedReceiver);
-            if (info.isStagedSessionFailed()) {
-                throw new AssertionError(info.getStagedSessionErrorMessage());
-            }
-        } catch (InterruptedException e) {
-            throw new AssertionError(e);
-        }
-    }
-}
-
diff --git a/libs/rollback/testapp/src/com/android/cts/rollback/lib/testapp/MainActivity.java b/libs/rollback/testapp/src/com/android/cts/rollback/lib/testapp/MainActivity.java
deleted file mode 100644
index e426382..0000000
--- a/libs/rollback/testapp/src/com/android/cts/rollback/lib/testapp/MainActivity.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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 com.android.cts.rollback.lib.testapp;
-
-import android.app.Activity;
-
-/**
- * A test app for testing apk rollback support.
- */
-public class MainActivity extends Activity {
-}
diff --git a/libs/runner/Android.bp b/libs/runner/Android.bp
index 425c593..40977ff 100644
--- a/libs/runner/Android.bp
+++ b/libs/runner/Android.bp
@@ -12,16 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// The legacy library that brings in android-support-test transitively
-java_library {
-    name: "ctstestrunner",
-
-    static_libs: ["cts-test-runner"],
-
-    sdk_version: "current",
-
-}
-
 // The library variant that brings in androidx-test transitively
 java_library {
     name: "ctstestrunner-axt",
diff --git a/tests/DropBoxManager/OWNERS b/tests/DropBoxManager/OWNERS
new file mode 100644
index 0000000..1a5b740
--- /dev/null
+++ b/tests/DropBoxManager/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 1344
+mwachens@google.com
diff --git a/tests/JobScheduler/OWNERS b/tests/JobScheduler/OWNERS
new file mode 100644
index 0000000..ef7929f
--- /dev/null
+++ b/tests/JobScheduler/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 330738
+ctate@google.com
\ No newline at end of file
diff --git a/tests/JobSchedulerSharedUid/OWNERS b/tests/JobSchedulerSharedUid/OWNERS
new file mode 100644
index 0000000..ef7929f
--- /dev/null
+++ b/tests/JobSchedulerSharedUid/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 330738
+ctate@google.com
\ No newline at end of file
diff --git a/tests/acceleration/OWNERS b/tests/acceleration/OWNERS
new file mode 100644
index 0000000..3d2576a
--- /dev/null
+++ b/tests/acceleration/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 32850
+include platform/frameworks/base:/libs/hwui/OWNERS
diff --git a/tests/accessibility/AndroidManifest.xml b/tests/accessibility/AndroidManifest.xml
index f07494f..d4cea0f 100644
--- a/tests/accessibility/AndroidManifest.xml
+++ b/tests/accessibility/AndroidManifest.xml
@@ -21,9 +21,11 @@
           android:targetSandboxVersion="2">
 
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
 
-    <application android:theme="@android:style/Theme.Holo.NoActionBar" >
+    <application android:theme="@android:style/Theme.Holo.NoActionBar"
+            android:requestLegacyExternalStorage="true">
         <uses-library android:name="android.test.runner"/>
         <service android:name=".SpeakingAccessibilityService"
                  android:label="@string/title_speaking_accessibility_service"
diff --git a/tests/accessibility/AndroidTest.xml b/tests/accessibility/AndroidTest.xml
index 7783a25..3057ae8 100644
--- a/tests/accessibility/AndroidTest.xml
+++ b/tests/accessibility/AndroidTest.xml
@@ -30,4 +30,8 @@
         <option name="package" value="android.view.accessibility.cts" />
         <option name="runtime-hint" value="8m"/>
     </test>
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="directory-keys" value="/sdcard/android.view.accessibility.cts" />
+        <option name="collect-on-run-ended-only" value="true" />
+    </metrics_collector>
 </configuration>
diff --git a/tests/accessibility/OWNERS b/tests/accessibility/OWNERS
index d225f2c..e54f581 100644
--- a/tests/accessibility/OWNERS
+++ b/tests/accessibility/OWNERS
@@ -1,2 +1,3 @@
 # Bug component: 44214
-pweaver@google.com
\ No newline at end of file
+pweaver@google.com
+rhedjao@google.com
diff --git a/tests/accessibility/common/src/android/accessibility/cts/common/AccessibilityDumpOnFailureRule.java b/tests/accessibility/common/src/android/accessibility/cts/common/AccessibilityDumpOnFailureRule.java
new file mode 100644
index 0000000..df7c550
--- /dev/null
+++ b/tests/accessibility/common/src/android/accessibility/cts/common/AccessibilityDumpOnFailureRule.java
@@ -0,0 +1,68 @@
+/*
+ * 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.accessibility.cts.common;
+
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.rules.ExternalResource;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+/**
+ * Custom {@code TestRule} that dump accessibility related data upon test failures.
+ *
+ * <p>Note: when using other {@code TestRule}s, make sure to use a {@link RuleChain} to ensure it
+ * is applied outside of other rules that can fail a test (otherwise this rule may not know that the
+ * test failed). If using with {@link ExternalResource}-like {@code TestRule}s, {@link
+ * ActivityTestRule} or {@link InstrumentedAccessibilityService}, this rule should chaining as a
+ * inner rule to resources-like rules so that it will dump data before resources are cleaned up.
+ *
+ * <p>To capture the output of this rule, add the following to AndroidTest.xml:
+ * <pre>
+ *  <!-- Collect output of AccessibilityDumpOnFailureRule. -->
+ *  <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ *    <option name="directory-keys" value="/sdcard/<test.package.name>" />
+ *    <option name="collect-on-run-ended-only" value="true" />
+ *  </metrics_collector>
+ * </pre>
+ * <p>And disable external storage isolation:
+ * <pre>
+ *  <application ... android:requestLegacyExternalStorage="true" ... >
+ * </pre>
+ */
+public class AccessibilityDumpOnFailureRule extends TestWatcher {
+
+    public void dump(int flag) {
+        AccessibilityDumper.getInstance().dump(flag);
+    }
+
+    @Override
+    protected void starting(Description description) {
+        AccessibilityDumper.getInstance().setName(getTestNameFrom(description));
+    }
+
+    @Override
+    protected void failed(Throwable t, Description description) {
+        AccessibilityDumper.getInstance().dump();
+    }
+
+    private String getTestNameFrom(Description description) {
+        return description.getTestClass().getSimpleName()
+                + "_" + description.getMethodName();
+    }
+}
diff --git a/tests/accessibility/common/src/android/accessibility/cts/common/AccessibilityDumper.java b/tests/accessibility/common/src/android/accessibility/cts/common/AccessibilityDumper.java
new file mode 100644
index 0000000..6fbd95a6
--- /dev/null
+++ b/tests/accessibility/common/src/android/accessibility/cts/common/AccessibilityDumper.java
@@ -0,0 +1,256 @@
+/*
+ * 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.accessibility.cts.common;
+
+import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
+import static android.app.UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertFalse;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.UiAutomation;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.os.Environment;
+import android.support.test.uiautomator.Configurator;
+import android.support.test.uiautomator.UiDevice;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityWindowInfo;
+
+import com.android.compatibility.common.util.BitmapUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.time.LocalTime;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Helper class to dump data for accessibility test cases.
+ *
+ * It can dump {@code dumpsys accessibility}, accessibility node tree to logcat and/or
+ * screenshot for inspect later.
+ */
+public class AccessibilityDumper {
+    private static final String TAG = "AccessibilityDumper";
+
+    /** Dump flag to write the output of {@code dumpsys accessibility} to logcat. */
+    public static final int FLAG_DUMPSYS = 0x1;
+
+    /** Dump flag to write the output of {@code uiautomator dump} to logcat. */
+    public static final int FLAG_HIERARCHY = 0x2;
+
+    /** Dump flag to save the screenshot to external storage. */
+    public static final int FLAG_SCREENSHOT = 0x4;
+
+    /** Dump flag to write the tree of accessility node info to logcat. */
+    public static final int FLAG_NODETREE = 0x8;
+
+    /** Default dump flag */
+    public static final int FLAG_DUMP_ALL = FLAG_DUMPSYS | FLAG_HIERARCHY | FLAG_SCREENSHOT;
+
+    private static AccessibilityDumper sDumper;
+
+    private int mFlag;
+
+    /** Screenshot filename */
+    private String mName;
+
+    /** Root directory matching the directory-key of collector in AndroidTest.xml */
+    private File mRoot;
+
+    public static synchronized AccessibilityDumper getInstance() {
+        if (sDumper == null) {
+            sDumper = new AccessibilityDumper(FLAG_DUMP_ALL);
+        }
+        return sDumper;
+    }
+
+    /**
+     * Define the directory to dump/clean and initial dump options
+     *
+     * @param flag control what to dump
+     */
+    private AccessibilityDumper(int flag) {
+        mRoot = getDumpRoot(getContext().getPackageName());
+        mFlag = flag;
+    }
+
+    public void dump(int flag) {
+        final UiAutomation automation = getUiAutomation();
+
+        if ((flag & FLAG_DUMPSYS) != 0) {
+            dumpsysOnLogcat(automation);
+        }
+        if ((flag & FLAG_HIERARCHY) != 0) {
+            dumpHierarchyOnLogcat();
+        }
+        if ((flag & FLAG_SCREENSHOT) != 0) {
+            dumpScreen(automation);
+        }
+        if ((flag & FLAG_NODETREE) != 0) {
+            dumpAccessibilityNodeTreeOnLogcat(automation);
+        }
+    }
+
+    void dump() {
+        dump(mFlag);
+    }
+
+    void setName(String name) {
+        assertNotEmpty(name);
+        mName = name;
+    }
+
+    private File getDumpRoot(String directory) {
+        return new File(Environment.getExternalStorageDirectory(), directory);
+    }
+
+    private void dumpsysOnLogcat(UiAutomation automation) {
+        ShellCommandBuilder.create(automation)
+            .addCommandPrintOnLogCat("dumpsys accessibility")
+            .run();
+    }
+
+    private void dumpHierarchyOnLogcat() {
+        try(ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+            UiDevice.getInstance(getInstrumentation()).dumpWindowHierarchy(os);
+            Log.w(TAG, "Window hierarchy:");
+            for (String line : os.toString("UTF-8").split("\\n")) {
+                Log.w(TAG, line);
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "ERROR: unable to dumping hierarchy on logcat", e);
+        }
+    }
+
+    private void dumpScreen(UiAutomation automation) {
+        assertNotEmpty(mName);
+        final Bitmap screenshot = automation.takeScreenshot();
+        final String filename = String.format("%s_%s__screenshot.png", mName, LocalTime.now());
+        BitmapUtils.saveBitmap(screenshot, mRoot.toString(), filename);
+    }
+
+    /** Dump hierarchy compactly and include nodes not visible to user */
+    private void dumpAccessibilityNodeTreeOnLogcat(UiAutomation automation) {
+        final Set<AccessibilityNodeInfo> roots = new HashSet<>();
+        for (AccessibilityWindowInfo window : automation.getWindows()) {
+            AccessibilityNodeInfo root = window.getRoot();
+            if (root == null) {
+                Log.w(TAG, String.format("Skipping null root node for window: %s",
+                        window.toString()));
+            } else {
+                roots.add(root);
+            }
+        }
+        if (roots.isEmpty()) {
+            Log.w(TAG, "No node of windows to dump");
+        } else {
+            Log.w(TAG, "Accessibility nodes hierarchy:");
+            for (AccessibilityNodeInfo root : roots) {
+                dumpTreeWithPrefix(root, "");
+            }
+        }
+    }
+
+    private static void dumpTreeWithPrefix(AccessibilityNodeInfo node, String prefix) {
+        final StringBuilder nodeText = new StringBuilder(prefix);
+        appendNodeText(nodeText, node);
+        Log.v(TAG, nodeText.toString());
+        final int count = node.getChildCount();
+        for (int i = 0; i < count; i++) {
+            AccessibilityNodeInfo child = node.getChild(i);
+            if (child != null) {
+                dumpTreeWithPrefix(child, "-" + prefix);
+            } else {
+                Log.i(TAG, String.format("%sNull child %d/%d", prefix, i, count));
+            }
+        }
+    }
+
+    private static void appendNodeText(StringBuilder out, AccessibilityNodeInfo node) {
+        final CharSequence txt = node.getText();
+        final CharSequence description = node.getContentDescription();
+        final String viewId = node.getViewIdResourceName();
+
+        if (!TextUtils.isEmpty(description)) {
+            out.append(escape(description));
+        } else if (!TextUtils.isEmpty(txt)) {
+            out.append('"').append(escape(txt)).append('"');
+        }
+        if (!TextUtils.isEmpty(viewId)) {
+            out.append("(").append(viewId).append(")");
+        }
+        out.append("+").append(node.getClassName());
+        out.append("+ \t<");
+        out.append(node.isCheckable()       ? "C" : ".");
+        out.append(node.isChecked()         ? "c" : ".");
+        out.append(node.isClickable()       ? "K" : ".");
+        out.append(node.isEnabled()         ? "E" : ".");
+        out.append(node.isFocusable()       ? "F" : ".");
+        out.append(node.isFocused()         ? "f" : ".");
+        out.append(node.isLongClickable()   ? "L" : ".");
+        out.append(node.isPassword()        ? "P" : ".");
+        out.append(node.isScrollable()      ? "S" : ".");
+        out.append(node.isSelected()        ? "s" : ".");
+        out.append(node.isVisibleToUser()   ? "V" : ".");
+        out.append("> ");
+        final Rect bounds = new Rect();
+        node.getBoundsInScreen(bounds);
+        out.append(bounds.toShortString());
+    }
+
+    /**
+     * Produce a displayable string from a CharSequence
+     */
+    private static String escape(CharSequence s) {
+        final StringBuilder out = new StringBuilder();
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+            if ((c < 127) || (c == 0xa0) || ((c >= 0x2000) && (c < 0x2070))) {
+                out.append(c);
+            } else {
+                out.append("\\u").append(Integer.toHexString(c));
+            }
+        }
+        return out.toString();
+    }
+
+    private void assertNotEmpty(String name) {
+        assertFalse("Expected non empty name.", TextUtils.isEmpty(name));
+    }
+
+    private UiAutomation getUiAutomation() {
+        // Reuse UiAutomation from UiAutomator with the same flag
+        Configurator.getInstance().setUiAutomationFlags(
+                FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
+        final UiAutomation automation = getInstrumentation().getUiAutomation(
+                FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
+        // Dump window info & node tree
+        final AccessibilityServiceInfo info = automation.getServiceInfo();
+        if (info != null && ((info.flags & FLAG_RETRIEVE_INTERACTIVE_WINDOWS) == 0)) {
+            info.flags |= FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
+            automation.setServiceInfo(info);
+        }
+        return automation;
+    }
+}
diff --git a/tests/accessibility/common/src/android/accessibility/cts/common/InstrumentedAccessibilityService.java b/tests/accessibility/common/src/android/accessibility/cts/common/InstrumentedAccessibilityService.java
index 3f9a344..09af2ee 100644
--- a/tests/accessibility/common/src/android/accessibility/cts/common/InstrumentedAccessibilityService.java
+++ b/tests/accessibility/common/src/android/accessibility/cts/common/InstrumentedAccessibilityService.java
@@ -35,6 +35,7 @@
 import android.view.accessibility.AccessibilityManager;
 
 import androidx.annotation.CallSuper;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import java.lang.ref.WeakReference;
 import java.util.HashMap;
@@ -52,7 +53,7 @@
 
     // Match com.android.server.accessibility.AccessibilityManagerService#COMPONENT_NAME_SEPARATOR
     private static final String COMPONENT_NAME_SEPARATOR = ":";
-    private static final int TIMEOUT_SERVICE_PERFORM_SYNC = DEBUG ? Integer.MAX_VALUE : 5000;
+    private static final int TIMEOUT_SERVICE_PERFORM_SYNC = DEBUG ? Integer.MAX_VALUE : 10000;
 
     private static final HashMap<Class, WeakReference<InstrumentedAccessibilityService>>
             sInstances = new HashMap<>();
@@ -119,13 +120,14 @@
     public <T extends Object> T getOnService(Callable<T> callable) {
         AtomicReference<T> returnValue = new AtomicReference<>(null);
         AtomicReference<Throwable> throwable = new AtomicReference<>(null);
-        runOnServiceSync(() -> {
-            try {
-                returnValue.set(callable.call());
-            } catch (Throwable e) {
-                throwable.set(e);
-            }
-        });
+        runOnServiceSync(
+                () -> {
+                    try {
+                        returnValue.set(callable.call());
+                    } catch (Throwable e) {
+                        throwable.set(e);
+                    }
+                });
         if (throwable.get() != null) {
             throw new RuntimeException(throwable.get());
         }
@@ -167,24 +169,27 @@
     }
 
     public static <T extends InstrumentedAccessibilityService> T enableService(
-            Instrumentation instrumentation, Class<T> clazz) {
+            Class<T> clazz) {
+        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
         final String serviceName = clazz.getSimpleName();
         final Context context = instrumentation.getContext();
-        final String enabledServices = Settings.Secure.getString(
-                context.getContentResolver(),
-                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+        final String enabledServices =
+                Settings.Secure.getString(
+                        context.getContentResolver(),
+                        Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
         if (enabledServices != null) {
             assertFalse("Service is already enabled", enabledServices.contains(serviceName));
         }
-        final AccessibilityManager manager = (AccessibilityManager) context.getSystemService(
-                Context.ACCESSIBILITY_SERVICE);
+        final AccessibilityManager manager =
+                (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
         final List<AccessibilityServiceInfo> serviceInfos =
                 manager.getInstalledAccessibilityServiceList();
         for (AccessibilityServiceInfo serviceInfo : serviceInfos) {
             final String serviceId = serviceInfo.getId();
             if (serviceId.endsWith(serviceName)) {
                 ShellCommandBuilder.create(instrumentation)
-                        .putSecureSetting(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+                        .putSecureSetting(
+                                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
                                 enabledServices + COMPONENT_NAME_SEPARATOR + serviceId)
                         .putSecureSetting(Settings.Secure.ACCESSIBILITY_ENABLED, "1")
                         .run();
@@ -192,11 +197,15 @@
                 final T instance = getInstanceForClass(clazz, TIMEOUT_SERVICE_ENABLE);
                 if (instance == null) {
                     ShellCommandBuilder.create(instrumentation)
-                            .putSecureSetting(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
-                                    enabledServices)
+                            .putSecureSetting(
+                                    Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, enabledServices)
                             .run();
-                    throw new RuntimeException("Starting accessibility service " + serviceName
-                            + " took longer than " + TIMEOUT_SERVICE_ENABLE + "ms");
+                    throw new RuntimeException(
+                            "Starting accessibility service "
+                                    + serviceName
+                                    + " took longer than "
+                                    + TIMEOUT_SERVICE_ENABLE
+                                    + "ms");
                 }
                 return instance;
             }
@@ -204,19 +213,14 @@
         throw new RuntimeException("Accessibility service " + serviceName + " not found");
     }
 
-    public static <T extends InstrumentedAccessibilityService> T getInstanceForClass(Class clazz,
-            long timeoutMillis) {
+    public static <T extends InstrumentedAccessibilityService> T getInstanceForClass(
+            Class<T> clazz, long timeoutMillis) {
         final long timeoutTimeMillis = SystemClock.uptimeMillis() + timeoutMillis;
         while (SystemClock.uptimeMillis() < timeoutTimeMillis) {
             synchronized (sInstances) {
-                final WeakReference<InstrumentedAccessibilityService> ref = sInstances.get(clazz);
-                if (ref != null) {
-                    final T instance = (T) ref.get();
-                    if (instance == null) {
-                        sInstances.remove(clazz);
-                    } else {
-                        return instance;
-                    }
+                final T instance = getInstanceForClass(clazz);
+                if (instance != null) {
+                    return instance;
                 }
                 try {
                     sInstances.wait(timeoutTimeMillis - SystemClock.uptimeMillis());
@@ -228,19 +232,37 @@
         return null;
     }
 
-    public static void disableAllServices(Instrumentation instrumentation) {
+    static <T extends InstrumentedAccessibilityService> T getInstanceForClass(
+            Class<T> clazz) {
+        synchronized (sInstances) {
+            final WeakReference<InstrumentedAccessibilityService> ref = sInstances.get(clazz);
+            if (ref != null) {
+                final T instance = (T) ref.get();
+                if (instance == null) {
+                    sInstances.remove(clazz);
+                } else {
+                    return instance;
+                }
+            }
+        }
+        return null;
+    }
+
+    public static void disableAllServices() {
+        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
         final Object waitLockForA11yOff = new Object();
         final Context context = instrumentation.getContext();
         final AccessibilityManager manager =
                 (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
         // Updates to manager.isEnabled() aren't synchronized
         final AtomicBoolean accessibilityEnabled = new AtomicBoolean(manager.isEnabled());
-        manager.addAccessibilityStateChangeListener(b -> {
-            synchronized (waitLockForA11yOff) {
-                waitLockForA11yOff.notifyAll();
-                accessibilityEnabled.set(b);
-            }
-        });
+        manager.addAccessibilityStateChangeListener(
+                b -> {
+                    synchronized (waitLockForA11yOff) {
+                        waitLockForA11yOff.notifyAll();
+                        accessibilityEnabled.set(b);
+                    }
+                });
         final UiAutomation uiAutomation = instrumentation.getUiAutomation(
                 UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
         ShellCommandBuilder.create(uiAutomation)
diff --git a/tests/accessibility/common/src/android/accessibility/cts/common/InstrumentedAccessibilityServiceTestRule.java b/tests/accessibility/common/src/android/accessibility/cts/common/InstrumentedAccessibilityServiceTestRule.java
new file mode 100644
index 0000000..1684dbb
--- /dev/null
+++ b/tests/accessibility/common/src/android/accessibility/cts/common/InstrumentedAccessibilityServiceTestRule.java
@@ -0,0 +1,180 @@
+/*
+ * 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.accessibility.cts.common;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import android.util.Log;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * A JUnit rule that provides a simplified mechanism to enable and disable {@link
+ * InstrumentedAccessibilityService} before and after the duration of your test. It will
+ * automatically be disabled after the test completes and any methods annotated with
+ * <a href="http://junit.sourceforge.net/javadoc/org/junit/After.html"><code>After</code></a>
+ * are finished.
+ *
+ * <p>Usage:
+ *
+ * <pre>
+ * &#064;Rule
+ * public final InstrumentedAccessibilityServiceTestRule<InstrumentedAccessibilityService>
+ *         mServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+ *                 InstrumentedAccessibilityService.class, false);
+ *
+ * &#064;Test
+ * public void testWithEnabledAccessibilityService() {
+ *     MyService service = mServiceRule.enableService();
+ *     //do something
+ *     assertTrue("True wasn't returned", service.doSomethingToReturnTrue());
+ * }
+ * </pre>
+ *
+ * @param <T> The instrumented accessibility service class under test
+ */
+public class InstrumentedAccessibilityServiceTestRule<T extends InstrumentedAccessibilityService>
+        implements TestRule {
+
+    private static final String TAG = "InstrA11yServiceTestRule";
+
+    private final Class<T> mAccessibilityServiceClass;
+
+    private final boolean mEnableService;
+
+    /**
+     * Creates a {@link InstrumentedAccessibilityServiceTestRule} with the specified class of
+     * instrumented accessibility service and enable the service automatically.
+     *
+     * @param clazz The instrumented accessibility service under test. This must be a class in the
+     *     instrumentation targetPackage specified in the AndroidManifest.xml
+     */
+    public InstrumentedAccessibilityServiceTestRule(@NonNull Class<T> clazz) {
+        this(clazz, true);
+    }
+
+    /**
+     * Creates a {@link InstrumentedAccessibilityServiceTestRule} with the specified class of
+     * instrumented accessibility service, and enable the service automatically or not according to
+     * given {@code enableService}.
+     *
+     * @param clazz The instrumented accessibility service under test. This must be a class in the
+     *     instrumentation targetPackage specified in the AndroidManifest.xml
+     * @param enableService true if the service should be enabled once per <a
+     *     href="http://junit.org/javadoc/latest/org/junit/Test.html"><code>Test</code></a> method.
+     *     It will be enabled before the first <a
+     *     href="http://junit.sourceforge.net/javadoc/org/junit/Before.html"><code>Before</code></a>
+     *     method, and terminated after the last <a
+     *     href="http://junit.sourceforge.net/javadoc/org/junit/After.html"><code>After</code></a>
+     *     method.
+     */
+    public InstrumentedAccessibilityServiceTestRule(@NonNull Class<T> clazz,
+            boolean enableService) {
+        mAccessibilityServiceClass = clazz;
+        mEnableService = enableService;
+    }
+
+    /**
+     * Enable the instrumented accessibility service under test.
+     *
+     * <p>Don't call this method directly, unless you explicitly requested not to lazily enable the
+     * service manually using the enableService flag in {@link
+     * #InstrumentedAccessibilityServiceTestRule(Class, boolean)}.
+     *
+     * <p>Usage:
+     *
+     * <pre>
+     *    &#064;Test
+     *    public void enableAccessibilityService() {
+     *        service = mServiceRule.enableService();
+     *    }
+     * </pre>
+     *
+     * @return the instrumented accessibility service enabled by this rule.
+     */
+    @NonNull
+    public T enableService() {
+        return InstrumentedAccessibilityService.enableService(mAccessibilityServiceClass);
+    }
+
+    /**
+     * Override this method to do your own service specific clean up or shutdown.
+     * The method is called after each test method is executed including any method annotated with
+     * <a href="http://junit.sourceforge.net/javadoc/org/junit/After.html"><code>After</code></a>
+     * and after necessary calls to stop (or unbind) the service under test were called.
+     */
+    protected void disableService() {
+        callFinishOnServiceSync();
+    }
+
+    /**
+     * Returns the reference to the instrumented accessibility service instance.
+     *
+     * <p>If the service wasn't enabled yet or already disabled, {@code null} will be returned.
+     */
+    @Nullable
+    public T getService() {
+        final T instance = InstrumentedAccessibilityService.getInstanceForClass(
+                mAccessibilityServiceClass,
+                InstrumentedAccessibilityService.TIMEOUT_SERVICE_ENABLE);
+        if (instance == null) {
+            Log.i(TAG, String.format(
+                    "Accessibility service %s wasn't enabled yet or already disabled",
+                    mAccessibilityServiceClass.getSimpleName()));
+        }
+        return instance;
+    }
+
+    private void callFinishOnServiceSync() {
+        final T service = InstrumentedAccessibilityService.getInstanceForClass(
+                mAccessibilityServiceClass);
+        if (service != null) {
+            service.runOnServiceSync(service::disableSelfAndRemove);
+        }
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return new ServiceStatement(base);
+    }
+
+    /**
+     * {@link Statement} that executes the service lifecycle methods before and after the execution
+     * of the test.
+     */
+    private class ServiceStatement extends Statement {
+        private final Statement base;
+
+        public ServiceStatement(Statement base) {
+            this.base = base;
+        }
+
+        @Override
+        public void evaluate() throws Throwable {
+            try {
+                if (mEnableService) {
+                    enableService();
+                }
+                base.evaluate();
+            } finally {
+                disableService();
+            }
+        }
+    }
+}
diff --git a/tests/accessibility/common/src/android/accessibility/cts/common/ShellCommandBuilder.java b/tests/accessibility/common/src/android/accessibility/cts/common/ShellCommandBuilder.java
index c305ceb..cfeb1fd 100644
--- a/tests/accessibility/common/src/android/accessibility/cts/common/ShellCommandBuilder.java
+++ b/tests/accessibility/common/src/android/accessibility/cts/common/ShellCommandBuilder.java
@@ -21,6 +21,8 @@
 import android.os.ParcelFileDescriptor;
 import android.util.Log;
 
+import com.android.compatibility.common.util.SystemUtil;
+
 import java.io.BufferedReader;
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -88,6 +90,25 @@
         return this;
     }
 
+    public ShellCommandBuilder addCommandPrintOnLogCat(String command) {
+        mCommands.add(() -> {
+            execShellCommandAndPrintOnLogcat(mUiAutomation, command);
+        });
+        return this;
+    }
+
+    public static void execShellCommandAndPrintOnLogcat(UiAutomation automation, String command) {
+        Log.i(LOG_TAG, "command [" + command + "]");
+        try {
+            final String output = SystemUtil.runShellCommand(automation, command);
+            for (String line : output.split("\\n", -1)) {
+                Log.i(LOG_TAG, line);
+            }
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to exec shell command [" + command + "]", e);
+        }
+    }
+
     public static void execShellCommand(UiAutomation automation, String command) {
         Log.i(LOG_TAG, "command [" + command + "]");
         try (ParcelFileDescriptor fd = automation.executeShellCommand(command)) {
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityDelegateTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityDelegateTest.java
index fcc4f6e..d147b83 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityDelegateTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityDelegateTest.java
@@ -22,6 +22,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.app.Activity;
 import android.os.Bundle;
 import android.view.View;
@@ -36,6 +37,7 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 
 /**
@@ -47,10 +49,18 @@
     private LinearLayout mParentView;
     private View mChildView;
 
-    @Rule
-    public ActivityTestRule<DummyActivity> mActivityRule =
+    private ActivityTestRule<DummyActivity> mActivityRule =
             new ActivityTestRule<>(DummyActivity.class, false, false);
 
+    private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    @Rule
+    public final RuleChain mRuleChain = RuleChain
+            .outerRule(mActivityRule)
+            // Inner rule capture failure and dump data before finishing activity
+            .around(mDumpOnFailureRule);
+
     @Before
     public void setUp() throws Exception {
         Activity activity = mActivityRule.launchActivity(null);
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
index 509092f..b140f37 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
@@ -16,27 +16,45 @@
 
 package android.view.accessibility.cts;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.os.Message;
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.text.TextUtils;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityRecord;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import junit.framework.TestCase;
 
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 /**
  * Class for testing {@link AccessibilityEvent}.
  */
 @Presubmit
-public class AccessibilityEventTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityEventTest {
+
+    @Rule
+    public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
     /**
      * Tests whether accessibility events are correctly written and
      * read from a parcel (version 1).
      */
     @SmallTest
+    @Test
     public void testMarshaling() throws Exception {
         // fully populate the event to marshal
         AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
@@ -58,6 +76,7 @@
      * Tests if {@link AccessibilityEvent} are properly reused.
      */
     @SmallTest
+    @Test
     public void testReuse() {
         AccessibilityEvent firstEvent = AccessibilityEvent.obtain();
         firstEvent.recycle();
@@ -69,6 +88,7 @@
      * Tests if {@link AccessibilityEvent} are properly recycled.
      */
     @SmallTest
+    @Test
     public void testRecycle() {
         // obtain and populate an event
         AccessibilityEvent populatedEvent = AccessibilityEvent.obtain();
@@ -86,6 +106,7 @@
      * Tests whether the event types are correctly converted to strings.
      */
     @SmallTest
+    @Test
     public void testEventTypeToString() {
         assertEquals("TYPE_NOTIFICATION_STATE_CHANGED", AccessibilityEvent.eventTypeToString(
                 AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED));
@@ -123,6 +144,7 @@
      * Tests whether the event describes its contents consistently.
      */
     @SmallTest
+    @Test
     public void testDescribeContents() {
         AccessibilityEvent event = AccessibilityEvent.obtain();
         assertSame("Accessibility events always return 0 for this method.", 0,
@@ -137,6 +159,7 @@
      * read from a parcel (version 2).
      */
     @SmallTest
+    @Test
     public void testMarshaling2() {
         AccessibilityEvent marshaledEvent = AccessibilityEvent.obtain();
         fullyPopulateAccessibilityEvent(marshaledEvent);
@@ -155,6 +178,7 @@
      * can't change the object by changing the objects backing CharSequence
      */
     @SmallTest
+    @Test
     public void testChangeTextAfterSetting_shouldNotAffectEvent() {
         final String originalText = "Cassowary";
         final String newText = "Hornbill";
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityGestureInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityGestureInfoTest.java
new file mode 100644
index 0000000..0d851cf
--- /dev/null
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityGestureInfoTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.view.accessibility.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibilityservice.AccessibilityGestureInfo;
+import android.accessibilityservice.AccessibilityService;
+import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
+import android.view.Display;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Class for testing {@link android.accessibilityservice.AccessibilityGestureInfo}.
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityGestureInfoTest {
+
+    @Rule
+    public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    private static final int SENT_GESTURE = AccessibilityService.GESTURE_SWIPE_DOWN;
+    private static final int TARGET_DISPLAY = Display.DEFAULT_DISPLAY;
+
+    @SmallTest
+    @Test
+    public void testMarshaling() {
+
+        // Fully populate the gesture info to marshal.
+        AccessibilityGestureInfo sentGestureInfo = new AccessibilityGestureInfo(
+                SENT_GESTURE, TARGET_DISPLAY);
+
+        // Marshal and unmarshal the gesture info.
+        Parcel parcel = Parcel.obtain();
+        sentGestureInfo.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        AccessibilityGestureInfo receivedGestureInfo =
+                AccessibilityGestureInfo.CREATOR.createFromParcel(parcel);
+
+        // Make sure all fields properly marshaled.
+        assertEqualsGestureInfo(sentGestureInfo, receivedGestureInfo);
+
+        parcel.recycle();
+    }
+
+    /**
+     * Tests whether the value of Getter method is as same as the parameter of the constructor.
+     *
+     */
+    @SmallTest
+    @Test
+    public void testGetterMethods() {
+        AccessibilityGestureInfo actualGesture = new AccessibilityGestureInfo(SENT_GESTURE,
+                TARGET_DISPLAY);
+
+        assertEquals("getGestureId is different from parameter of constructor", SENT_GESTURE,
+                actualGesture.getGestureId());
+        assertEquals("getDisplayId is different from parameter of constructor", TARGET_DISPLAY,
+                actualGesture.getDisplayId());
+    }
+
+    /**
+     * Tests whether the gesture describes its contents consistently.
+     */
+    @SmallTest
+    @Test
+    public void testDescribeContents() {
+        AccessibilityGestureInfo gesture1 = new AccessibilityGestureInfo(SENT_GESTURE,TARGET_DISPLAY);
+        assertSame("accessibility gesture infos always return 0 for this method.", 0,
+                gesture1.describeContents());
+        AccessibilityGestureInfo gesture2 = new AccessibilityGestureInfo(
+                AccessibilityService.GESTURE_SWIPE_LEFT, TARGET_DISPLAY);
+        assertSame("accessibility gesture infos always return 0 for this method.", 0,
+                gesture2.describeContents());
+    }
+
+    private void assertEqualsGestureInfo(AccessibilityGestureInfo sentGestureInfo,
+            AccessibilityGestureInfo receivedGestureInfo) {
+        assertEquals("getDisplayId has incorrectValue", sentGestureInfo.getDisplayId(),
+                receivedGestureInfo.getDisplayId());
+        assertEquals("getGestureId has incorrectValue", sentGestureInfo.getGestureId(),
+                receivedGestureInfo.getGestureId());
+    }
+}
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java
index a681baf..b0d217d 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java
@@ -28,7 +28,9 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.app.Instrumentation;
 import android.app.Service;
@@ -50,9 +52,10 @@
 import com.android.compatibility.common.util.PollingCheck;
 import com.android.compatibility.common.util.SystemUtil;
 
-import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 
 import java.io.IOException;
@@ -65,6 +68,30 @@
 @RunWith(AndroidJUnit4.class)
 public class AccessibilityManagerTest {
 
+    private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    private InstrumentedAccessibilityServiceTestRule<SpeakingAccessibilityService>
+            mSpeakingAccessibilityServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+                    SpeakingAccessibilityService.class, false);
+
+    private InstrumentedAccessibilityServiceTestRule<VibratingAccessibilityService>
+            mVibratingAccessibilityServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+                    VibratingAccessibilityService.class, false);
+
+    private InstrumentedAccessibilityServiceTestRule<SpeakingAndVibratingAccessibilityService>
+            mSpeakingAndVibratingAccessibilityServiceRule =
+            new InstrumentedAccessibilityServiceTestRule<>(
+                    SpeakingAndVibratingAccessibilityService.class, false);
+
+    @Rule
+    public final RuleChain mRuleChain = RuleChain
+            .outerRule(mSpeakingAndVibratingAccessibilityServiceRule)
+            .around(mVibratingAccessibilityServiceRule)
+            .around(mSpeakingAccessibilityServiceRule)
+            // Inner rule capture failure and dump data before finishing activity and a11y service
+            .around(mDumpOnFailureRule);
+
     private static final Instrumentation sInstrumentation =
             InstrumentationRegistry.getInstrumentation();
 
@@ -97,12 +124,7 @@
         mHandler = new Handler(mTargetContext.getMainLooper());
         // In case the test runner started a UiAutomation, destroy it to start with a clean slate.
         sInstrumentation.getUiAutomation().destroy();
-        InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
+        InstrumentedAccessibilityService.disableAllServices();
     }
 
     @Test
@@ -127,8 +149,8 @@
 
     @Test
     public void testIsTouchExplorationEnabled() throws Exception {
-        SpeakingAccessibilityService.enableSelf(sInstrumentation);
-        VibratingAccessibilityService.enableSelf(sInstrumentation);
+        mSpeakingAccessibilityServiceRule.enableService();
+        mVibratingAccessibilityServiceRule.enableService();
         new PollingCheck() {
             @Override
             protected boolean check() {
@@ -163,8 +185,8 @@
 
     @Test
     public void testGetEnabledAccessibilityServiceList() throws Exception {
-        SpeakingAccessibilityService.enableSelf(sInstrumentation);
-        VibratingAccessibilityService.enableSelf(sInstrumentation);
+        mSpeakingAccessibilityServiceRule.enableService();
+        mVibratingAccessibilityServiceRule.enableService();
         List<AccessibilityServiceInfo> enabledServices =
             mAccessibilityManager.getEnabledAccessibilityServiceList(
                     AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
@@ -189,8 +211,8 @@
 
     @Test
     public void testGetEnabledAccessibilityServiceListForType() throws Exception {
-        SpeakingAccessibilityService.enableSelf(sInstrumentation);
-        VibratingAccessibilityService.enableSelf(sInstrumentation);
+        mSpeakingAccessibilityServiceRule.enableService();
+        mVibratingAccessibilityServiceRule.enableService();
         List<AccessibilityServiceInfo> enabledServices =
             mAccessibilityManager.getEnabledAccessibilityServiceList(
                     AccessibilityServiceInfo.FEEDBACK_SPOKEN);
@@ -209,10 +231,10 @@
 
     @Test
     public void testGetEnabledAccessibilityServiceListForTypes() throws Exception {
-        SpeakingAccessibilityService.enableSelf(sInstrumentation);
-        VibratingAccessibilityService.enableSelf(sInstrumentation);
+        mSpeakingAccessibilityServiceRule.enableService();
+        mVibratingAccessibilityServiceRule.enableService();
         // For this test, also enable a service with multiple feedback types
-        SpeakingAndVibratingAccessibilityService.enableSelf(sInstrumentation);
+        mSpeakingAndVibratingAccessibilityServiceRule.enableService();
 
         List<AccessibilityServiceInfo> enabledServices =
                 mAccessibilityManager.getEnabledAccessibilityServiceList(
@@ -272,8 +294,8 @@
     public void testInterrupt() throws Exception {
         // The APIs are heavily tested in the android.accessibilityservice package.
         // This just makes sure the call does not throw an exception.
-        SpeakingAccessibilityService.enableSelf(sInstrumentation);
-        VibratingAccessibilityService.enableSelf(sInstrumentation);
+        mSpeakingAccessibilityServiceRule.enableService();
+        mVibratingAccessibilityServiceRule.enableService();
         waitForAccessibilityEnabled();
         mAccessibilityManager.interrupt();
     }
@@ -282,8 +304,8 @@
     public void testSendAccessibilityEvent() throws Exception {
         // The APIs are heavily tested in the android.accessibilityservice package.
         // This just makes sure the call does not throw an exception.
-        SpeakingAccessibilityService.enableSelf(sInstrumentation);
-        VibratingAccessibilityService.enableSelf(sInstrumentation);
+        mSpeakingAccessibilityServiceRule.enableService();
+        mVibratingAccessibilityServiceRule.enableService();
         waitForAccessibilityEnabled();
         mAccessibilityManager.sendAccessibilityEvent(AccessibilityEvent.obtain(
                 AccessibilityEvent.TYPE_VIEW_CLICKED));
@@ -301,13 +323,13 @@
             }
         };
         mAccessibilityManager.addTouchExplorationStateChangeListener(listener);
-        SpeakingAccessibilityService.enableSelf(sInstrumentation);
-        VibratingAccessibilityService.enableSelf(sInstrumentation);
+        mSpeakingAccessibilityServiceRule.enableService();
+        mVibratingAccessibilityServiceRule.enableService();
         assertAtomicBooleanBecomes(atomicBoolean, true, waitObject,
                 "Touch exploration state listener not called when services enabled");
         assertTrue("Listener told that touch exploration is enabled, but manager says disabled",
                 mAccessibilityManager.isTouchExplorationEnabled());
-        InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
+        InstrumentedAccessibilityService.disableAllServices();
         assertAtomicBooleanBecomes(atomicBoolean, false, waitObject,
                 "Touch exploration state listener not called when services disabled");
         assertFalse("Listener told that touch exploration is disabled, but manager says it enabled",
@@ -327,13 +349,13 @@
             }
         };
         mAccessibilityManager.addTouchExplorationStateChangeListener(listener, mHandler);
-        SpeakingAccessibilityService.enableSelf(sInstrumentation);
-        VibratingAccessibilityService.enableSelf(sInstrumentation);
+        mSpeakingAccessibilityServiceRule.enableService();
+        mVibratingAccessibilityServiceRule.enableService();
         assertAtomicBooleanBecomes(atomicBoolean, true, waitObject,
                 "Touch exploration state listener not called when services enabled");
         assertTrue("Listener told that touch exploration is enabled, but manager says disabled",
                 mAccessibilityManager.isTouchExplorationEnabled());
-        InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
+        InstrumentedAccessibilityService.disableAllServices();
         assertAtomicBooleanBecomes(atomicBoolean, false, waitObject,
                 "Touch exploration state listener not called when services disabled");
         assertFalse("Listener told that touch exploration is disabled, but manager says it enabled",
@@ -353,12 +375,12 @@
             }
         };
         mAccessibilityManager.addAccessibilityStateChangeListener(listener);
-        SpeakingAndVibratingAccessibilityService.enableSelf(sInstrumentation);
+        mSpeakingAndVibratingAccessibilityServiceRule.enableService();
         assertAtomicBooleanBecomes(atomicBoolean, true, waitObject,
                 "Accessibility state listener not called when services enabled");
         assertTrue("Listener told that accessibility is enabled, but manager says disabled",
                 mAccessibilityManager.isEnabled());
-        InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
+        InstrumentedAccessibilityService.disableAllServices();
         assertAtomicBooleanBecomes(atomicBoolean, false, waitObject,
                 "Accessibility state listener not called when services disabled");
         assertFalse("Listener told that accessibility is disabled, but manager says enabled",
@@ -378,12 +400,12 @@
             }
         };
         mAccessibilityManager.addAccessibilityStateChangeListener(listener, mHandler);
-        SpeakingAndVibratingAccessibilityService.enableSelf(sInstrumentation);
+        mSpeakingAndVibratingAccessibilityServiceRule.enableService();
         assertAtomicBooleanBecomes(atomicBoolean, true, waitObject,
                 "Accessibility state listener not called when services enabled");
         assertTrue("Listener told that accessibility is enabled, but manager says disabled",
                 mAccessibilityManager.isEnabled());
-        InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
+        InstrumentedAccessibilityService.disableAllServices();
         assertAtomicBooleanBecomes(atomicBoolean, false, waitObject,
                 "Accessibility state listener not called when services disabled");
         assertFalse("Listener told that accessibility is disabled, but manager says enabled",
@@ -393,8 +415,8 @@
 
     @Test
     public void testGetRecommendedTimeoutMillis() throws Exception {
-        SpeakingAccessibilityService.enableSelf(sInstrumentation);
-        VibratingAccessibilityService.enableSelf(sInstrumentation);
+        mSpeakingAccessibilityServiceRule.enableService();
+        mVibratingAccessibilityServiceRule.enableService();
         waitForAccessibilityEnabled();
         UiAutomation automan = sInstrumentation.getUiAutomation(
                 UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
index 0923a92..c7c9b0d 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
@@ -16,13 +16,20 @@
 
 package android.view.accessibility.cts;
 
+import static androidx.test.InstrumentationRegistry.getContext;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.text.InputType;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -35,6 +42,13 @@
 import android.view.accessibility.AccessibilityNodeInfo.RangeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -43,9 +57,15 @@
  * Class for testing {@link AccessibilityNodeInfo}.
  */
 @Presubmit
-public class AccessibilityNodeInfoTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityNodeInfoTest {
+
+    @Rule
+    public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
 
     @SmallTest
+    @Test
     public void testMarshaling() throws Exception {
         // fully populate the node info to marshal
         AccessibilityNodeInfo sentInfo = AccessibilityNodeInfo.obtain(new View(getContext()));
@@ -67,6 +87,7 @@
      * Tests if {@link AccessibilityNodeInfo}s are properly reused.
      */
     @SmallTest
+    @Test
     public void testReuse() {
         AccessibilityEvent firstInfo = AccessibilityEvent.obtain();
         firstInfo.recycle();
@@ -78,6 +99,7 @@
      * Tests if {@link AccessibilityNodeInfo} are properly recycled.
      */
     @SmallTest
+    @Test
     public void testRecycle() {
         // obtain and populate an node info
         AccessibilityNodeInfo populatedInfo = AccessibilityNodeInfo.obtain();
@@ -95,6 +117,7 @@
      * Tests whether the event describes its contents consistently.
      */
     @SmallTest
+    @Test
     public void testDescribeContents() {
         AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
         assertSame("Accessibility node infos always return 0 for this method.", 0,
@@ -108,6 +131,7 @@
      * Tests whether accessibility actions are properly added.
      */
     @SmallTest
+    @Test
     public void testAddActions() {
         List<AccessibilityAction> customActions = new ArrayList<AccessibilityAction>();
         customActions.add(new AccessibilityAction(AccessibilityNodeInfo.ACTION_FOCUS, "Foo"));
@@ -133,6 +157,7 @@
      * Tests whether we catch addition of an action with invalid id.
      */
     @SmallTest
+    @Test
     public void testCreateInvalidActionId() {
         try {
             new AccessibilityAction(3, null);
@@ -145,6 +170,7 @@
      * Tests whether accessibility actions are properly removed.
      */
     @SmallTest
+    @Test
     public void testRemoveActions() {
         AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
 
@@ -173,6 +199,7 @@
      * can't change the object by changing the objects backing CharSequence
      */
     @SmallTest
+    @Test
     public void testChangeTextAfterSetting_shouldNotAffectInfo() {
         final String originalText = "Cassowaries";
         final String newText = "Hornbill";
@@ -191,6 +218,7 @@
     }
 
     @SmallTest
+    @Test
     public void testIsHeadingTakesOldApiIntoAccount() {
         final AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
         assertFalse(info.isHeading());
@@ -399,11 +427,11 @@
                 (receivedRange != null));
         if (expectedRange != null) {
             assertEquals("RangeInfo#getCurrent has incorrect value", expectedRange.getCurrent(),
-                    receivedRange.getCurrent());
+                    receivedRange.getCurrent(), 0.0);
             assertEquals("RangeInfo#getMin has incorrect value", expectedRange.getMin(),
-                    receivedRange.getMin());
+                    receivedRange.getMin(), 0.0);
             assertEquals("RangeInfo#getMax has incorrect value", expectedRange.getMax(),
-                    receivedRange.getMax());
+                    receivedRange.getMax(), 0.0);
             assertEquals("RangeInfo#getType has incorrect value", expectedRange.getType(),
                     receivedRange.getType());
         }
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfo_CollectionInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfo_CollectionInfoTest.java
index 98b87fc..cabfd98 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfo_CollectionInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfo_CollectionInfoTest.java
@@ -16,18 +16,34 @@
 
 package android.view.accessibility.cts;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.platform.test.annotations.Presubmit;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 /**
  * Class for testing {@link CollectionInfo}.
  */
 @Presubmit
-public class AccessibilityNodeInfo_CollectionInfoTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityNodeInfo_CollectionInfoTest  {
+
+    @Rule
+    public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
 
     @SmallTest
+    @Test
     public void testObtain() {
         CollectionInfo c;
 
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfo_RangeInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfo_RangeInfoTest.java
index 4b01129..e7761f4 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfo_RangeInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfo_RangeInfoTest.java
@@ -16,22 +16,36 @@
 
 package android.view.accessibility.cts;
 
+import static org.junit.Assert.assertEquals;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.platform.test.annotations.Presubmit;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.RangeInfo;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 /**
  * Class for testing {@link AccessibilityNodeInfo.RangeInfo}.
  */
 @Presubmit
-public class AccessibilityNodeInfo_RangeInfoTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityNodeInfo_RangeInfoTest {
 
     /** Allowed tolerance for floating point equality comparisons. */
     public static final float FLOAT_TOLERANCE = 0.001f;
 
+    @Rule
+    public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
     @SmallTest
+    @Test
     public void testObtain() {
         RangeInfo r;
 
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeProviderTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeProviderTest.java
index 0c2a2dd..60bc1a3 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeProviderTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeProviderTest.java
@@ -16,17 +16,33 @@
 
 package android.view.accessibility.cts;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.platform.test.annotations.Presubmit;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.accessibility.AccessibilityNodeProvider;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 /**
  * Class for testing {@link AccessibilityNodeProvider}.
  */
 @Presubmit
-public class AccessibilityNodeProviderTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityNodeProviderTest {
+
+    @Rule
+    public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
     @SmallTest
+    @Test
     public void testDefaultBehavior() {
         AccessibilityNodeProvider p = new AccessibilityNodeProvider() {
             // Class is abstract, but has no abstract methods.
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java
index 72bb4e0..119de9f 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java
@@ -16,17 +16,23 @@
 
 package android.view.accessibility.cts;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.os.Message;
 import android.platform.test.annotations.Presubmit;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityRecord;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import junit.framework.TestCase;
 
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.util.Iterator;
 import java.util.List;
 
@@ -34,11 +40,18 @@
  * Class for testing {@link AccessibilityRecord}.
  */
 @Presubmit
-public class AccessibilityRecordTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityRecordTest {
+
+    @Rule
+    public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
     /**
      * Tests the cloning obtain method.
      */
     @SmallTest
+    @Test
     public void testObtain() {
         AccessibilityRecord originalRecord = AccessibilityRecord.obtain();
         fullyPopulateAccessibilityRecord(originalRecord);
@@ -50,6 +63,7 @@
     * Tests if {@link AccessibilityRecord}s are properly recycled.
     */
    @SmallTest
+   @Test
    public void testRecycle() {
        // obtain and populate an event
        AccessibilityRecord populatedRecord = AccessibilityRecord.obtain();
@@ -84,10 +98,10 @@
        TestCase.assertEquals("removedCount not properly recycled", -1, record.getRemovedCount());
        TestCase.assertTrue("text not properly recycled", record.getText().isEmpty());
        TestCase.assertFalse("scrollable not properly recycled", record.isScrollable());
-       TestCase.assertSame("maxScrollX not properly recycled", -1, record.getMaxScrollX());
-       TestCase.assertSame("maxScrollY not properly recycled", -1, record.getMaxScrollY());
-       TestCase.assertSame("scrollX not properly recycled", -1, record.getScrollX());
-       TestCase.assertSame("scrollY not properly recycled", -1, record.getScrollY());
+       TestCase.assertSame("maxScrollX not properly recycled", 0, record.getMaxScrollX());
+       TestCase.assertSame("maxScrollY not properly recycled", 0, record.getMaxScrollY());
+       TestCase.assertSame("scrollX not properly recycled", 0, record.getScrollX());
+       TestCase.assertSame("scrollY not properly recycled", 0, record.getScrollY());
        TestCase.assertSame("toIndex not properly recycled", -1, record.getToIndex());
    }
 
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
index 0029b53..f4abdf1 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
@@ -16,14 +16,28 @@
 
 package android.view.accessibility.cts;
 
-import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.app.Service;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
 import java.util.List;
 
 /**
@@ -33,18 +47,23 @@
  * accessibility service and the fake service used for implementing the UI
  * automation is not reported through the APIs.
  */
-public class AccessibilityServiceInfoTest  extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityServiceInfoTest {
 
-    @Override
-    public void setUp() throws Exception {
-        SpeakingAccessibilityService.enableSelf(getInstrumentation());
-        VibratingAccessibilityService.enableSelf(getInstrumentation());
-    }
+    private InstrumentedAccessibilityServiceTestRule<SpeakingAccessibilityService>
+            mSpeakingAccessibilityServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+                    SpeakingAccessibilityService.class);
 
-    @Override
-    public void tearDown() {
-        InstrumentedAccessibilityService.disableAllServices(getInstrumentation());
-    }
+    private InstrumentedAccessibilityServiceTestRule<VibratingAccessibilityService>
+            mVibratingAccessibilityServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+                    VibratingAccessibilityService.class);
+
+    @Rule
+    public final RuleChain mRuleChain = RuleChain
+            .outerRule(mVibratingAccessibilityServiceRule)
+            .around(mSpeakingAccessibilityServiceRule)
+            // Inner rule capture failure and dump data before finishing a11y service
+            .around(new AccessibilityDumpOnFailureRule());
 
     /**
      * Tests whether a service can that requested it can retrieve
@@ -52,6 +71,7 @@
      */
     @MediumTest
     @SuppressWarnings("deprecation")
+    @Test
     public void testAccessibilityServiceInfoForEnabledService() {
         AccessibilityManager accessibilityManager = (AccessibilityManager)
                 getInstrumentation().getContext().getSystemService(Service.ACCESSIBILITY_SERVICE);
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityWindowInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityWindowInfoTest.java
index e959544..9d4ecd7 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityWindowInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityWindowInfoTest.java
@@ -16,21 +16,43 @@
 
 package android.view.accessibility.cts;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.graphics.Rect;
+import android.graphics.Region;
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.text.TextUtils;
+import android.view.Display;
 import android.view.accessibility.AccessibilityWindowInfo;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 /**
  * Class for testing {@link AccessibilityWindowInfo}.
  */
 @Presubmit
-public class AccessibilityWindowInfoTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityWindowInfoTest {
+
+    @Rule
+    public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
 
     @SmallTest
+    @Test
     public void testObtain() {
         AccessibilityWindowInfo w1 = AccessibilityWindowInfo.obtain();
         assertNotNull(w1);
@@ -41,6 +63,7 @@
     }
 
     @SmallTest
+    @Test
     public void testParceling() {
         Parcel parcel = Parcel.obtain();
         AccessibilityWindowInfo w1 = AccessibilityWindowInfo.obtain();
@@ -48,11 +71,12 @@
         parcel.setDataPosition(0);
         AccessibilityWindowInfo w2 = AccessibilityWindowInfo.CREATOR.createFromParcel(parcel);
         assertNotSame(w1, w2);
-        assertTrue(areWindowsEqual(w1, w2));
+        assertTrue(w2.toString(), areWindowsEqual(w1, w2));
         parcel.recycle();
     }
 
     @SmallTest
+    @Test
     public void testDefaultValues() {
         AccessibilityWindowInfo w = AccessibilityWindowInfo.obtain();
         assertEquals(0, w.getChildCount());
@@ -60,6 +84,7 @@
         assertEquals(-1, w.getLayer());
         assertEquals(-1, w.getId());
         assertEquals(0, w.describeContents());
+        assertEquals(Display.INVALID_DISPLAY, w.getDisplayId());
         assertNull(w.getParent());
         assertNull(w.getRoot());
         assertFalse(w.isAccessibilityFocused());
@@ -71,6 +96,10 @@
         w.getBoundsInScreen(rect);
         assertTrue(rect.isEmpty());
 
+        Region region = new Region();
+        w.getRegionInScreen(region);
+        assertTrue(region.isEmpty());
+
         try {
             w.getChild(0);
             fail("Expected IndexOutOfBoundsException");
@@ -80,6 +109,7 @@
     }
 
     @SmallTest
+    @Test
     public void testRecycle() {
         AccessibilityWindowInfo w = AccessibilityWindowInfo.obtain();
         w.recycle();
@@ -99,11 +129,17 @@
         equality &= w1.isActive() == w2.isActive();
         equality &= w1.getType() == w2.getType();
         equality &= w1.getLayer() == w2.getLayer();
+        equality &= w1.getDisplayId() == w2.getDisplayId();
         Rect bounds1 = new Rect();
         Rect bounds2 = new Rect();
         w1.getBoundsInScreen(bounds1);
         w2.getBoundsInScreen(bounds2);
         equality &= bounds1.equals(bounds2);
+        Region regions1 = new Region();
+        Region regions2 = new Region();
+        w1.getRegionInScreen(regions1);
+        w2.getRegionInScreen(regions2);
+        equality &= regions1.equals(regions2);
         return equality;
     }
 }
diff --git a/tests/accessibility/src/android/view/accessibility/cts/CaptioningManagerTest.java b/tests/accessibility/src/android/view/accessibility/cts/CaptioningManagerTest.java
index d0db868..c3054ef 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/CaptioningManagerTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/CaptioningManagerTest.java
@@ -16,6 +16,12 @@
 
 package android.view.accessibility.cts;
 
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+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.mockito.Matchers.anyFloat;
 import static org.mockito.Matchers.anyObject;
 import static org.mockito.Mockito.mock;
@@ -23,13 +29,19 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
 
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.app.UiAutomation;
 import android.os.ParcelFileDescriptor;
-import android.test.InstrumentationTestCase;
 import android.view.accessibility.CaptioningManager;
 import android.view.accessibility.CaptioningManager.CaptionStyle;
 import android.view.accessibility.CaptioningManager.CaptioningChangeListener;
 
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.Mockito;
 
 import java.io.FileInputStream;
@@ -40,15 +52,19 @@
 /**
  * Tests whether the CaptioningManager APIs are functional.
  */
-public class CaptioningManagerTest extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class CaptioningManagerTest {
+
+    @Rule
+    public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
     private static final int LISTENER_TIMEOUT = 3000;
     private CaptioningManager mManager;
     private UiAutomation mUiAutomation;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
+    @Before
+    public void setUp() throws Exception {
         mManager = getInstrumentation().getTargetContext().getSystemService(
                 CaptioningManager.class);
 
@@ -60,6 +76,7 @@
     /**
      * Tests whether a client can observe changes in caption properties.
      */
+    @Test
     public void testChangeListener() {
         putSecureSetting("accessibility_captioning_enabled","0");
         putSecureSetting("accessibility_captioning_preset", "1");
@@ -97,16 +114,18 @@
         }
     }
 
+    @Test
     public void testProperties() {
         putSecureSetting("accessibility_captioning_font_scale", "2.0");
         putSecureSetting("accessibility_captioning_locale", "ja_JP");
         putSecureSetting("accessibility_captioning_enabled", "1");
 
-        assertEquals("Test runner set font scale to 2.0", 2.0f, mManager.getFontScale());
+        assertEquals("Test runner set font scale to 2.0", 2.0f, mManager.getFontScale(), 0f);
         assertEquals("Test runner set locale to Japanese", Locale.JAPAN, mManager.getLocale());
         assertEquals("Test runner set enabled to true", true, mManager.isEnabled());
     }
 
+    @Test
     public void testUserStyle() {
         putSecureSetting("accessibility_captioning_preset", "-1");
         putSecureSetting("accessibility_captioning_foreground_color", "511");
diff --git a/tests/accessibility/src/android/view/accessibility/cts/SpeakingAccessibilityService.java b/tests/accessibility/src/android/view/accessibility/cts/SpeakingAccessibilityService.java
index d05232a..8a18f58 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/SpeakingAccessibilityService.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/SpeakingAccessibilityService.java
@@ -17,7 +17,6 @@
 package android.view.accessibility.cts;
 
 import android.accessibility.cts.common.InstrumentedAccessibilityService;
-import android.app.Instrumentation;
 import android.content.ComponentName;
 
 /**
@@ -27,9 +26,4 @@
     public static final ComponentName COMPONENT_NAME = new ComponentName(
             "android.view.accessibility.cts",
             "android.view.accessibility.cts.SpeakingAccessibilityService");
-
-    public static SpeakingAccessibilityService enableSelf(Instrumentation instrumentation) {
-        return InstrumentedAccessibilityService.enableService(
-                instrumentation, SpeakingAccessibilityService.class);
-    }
 }
diff --git a/tests/accessibility/src/android/view/accessibility/cts/SpeakingAndVibratingAccessibilityService.java b/tests/accessibility/src/android/view/accessibility/cts/SpeakingAndVibratingAccessibilityService.java
index cb8126d..c7c9844 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/SpeakingAndVibratingAccessibilityService.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/SpeakingAndVibratingAccessibilityService.java
@@ -17,15 +17,9 @@
 package android.view.accessibility.cts;
 
 import android.accessibility.cts.common.InstrumentedAccessibilityService;
-import android.app.Instrumentation;
 
 /**
  * Stub accessibility service that reports itself as providing multiple feedback types.
  */
 public class SpeakingAndVibratingAccessibilityService extends InstrumentedAccessibilityService {
-    public static SpeakingAndVibratingAccessibilityService enableSelf(
-            Instrumentation instrumentation) {
-        return InstrumentedAccessibilityService.enableService(instrumentation,
-                SpeakingAndVibratingAccessibilityService.class);
-    }
 }
diff --git a/tests/accessibility/src/android/view/accessibility/cts/VibratingAccessibilityService.java b/tests/accessibility/src/android/view/accessibility/cts/VibratingAccessibilityService.java
index 6b866aa..bec74e3 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/VibratingAccessibilityService.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/VibratingAccessibilityService.java
@@ -17,14 +17,9 @@
 package android.view.accessibility.cts;
 
 import android.accessibility.cts.common.InstrumentedAccessibilityService;
-import android.app.Instrumentation;
 
 /**
  * Stub accessibility service that reports itself as providing haptic feedback.
  */
 public class VibratingAccessibilityService extends InstrumentedAccessibilityService {
-    public static VibratingAccessibilityService enableSelf(Instrumentation instrumentation) {
-        return InstrumentedAccessibilityService.enableService(instrumentation,
-                VibratingAccessibilityService.class);
-    }
 }
diff --git a/tests/accessibilityservice/Android.bp b/tests/accessibilityservice/Android.bp
index cf37486..8b2a0ec 100644
--- a/tests/accessibilityservice/Android.bp
+++ b/tests/accessibilityservice/Android.bp
@@ -19,6 +19,7 @@
         "ctstestrunner-axt",
         "hamcrest-library",
         "mockito-target-minus-junit4",
+        "compatibility-device-util-axt",
         "platform-test-annotations",
         "CtsAccessibilityCommon",
     ],
diff --git a/tests/accessibilityservice/AndroidManifest.xml b/tests/accessibilityservice/AndroidManifest.xml
index fc0b2e6..be1799a 100644
--- a/tests/accessibilityservice/AndroidManifest.xml
+++ b/tests/accessibilityservice/AndroidManifest.xml
@@ -20,10 +20,12 @@
           android:targetSandboxVersion="2">
 
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
     <uses-permission android:name="android.permission.USE_FINGERPRINT" />
 
-    <application android:theme="@android:style/Theme.Holo.NoActionBar">
+    <application android:theme="@android:style/Theme.Holo.NoActionBar"
+                 android:requestLegacyExternalStorage="true">
 
         <uses-library android:name="android.test.runner" />
 
@@ -67,6 +69,17 @@
             android:label="@string/accessibility_soft_keyboard_modes_activity"
             android:name=".AccessibilitySoftKeyboardModesTest$SoftKeyboardModesActivity" />
 
+        <activity
+            android:label="@string/accessibility_embedded_display_test_parent_activity"
+            android:name=".AccessibilityEmbeddedDisplayTest$EmbeddedDisplayParentActivity"
+            android:theme="@android:style/Theme.Dialog"
+            android:screenOrientation="locked" />
+
+        <activity
+            android:label="@string/accessibility_embedded_display_test_activity"
+            android:name=".AccessibilityEmbeddedDisplayTest$EmbeddedDisplayActivity"
+            android:screenOrientation="locked" />
+
         <service
                 android:name=".StubGestureAccessibilityService"
                 android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
@@ -93,6 +106,17 @@
         </service>
 
         <service
+                android:name=".TouchExplorationStubAccessibilityService"
+                android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
+            <intent-filter>
+                <action android:name="android.accessibilityservice.AccessibilityService" />
+                <category android:name="android.accessibilityservice.category.FEEDBACK_GENERIC" />
+            </intent-filter>
+            <meta-data
+                    android:name="android.accessibilityservice"
+                    android:resource="@xml/stub_touch_exploration_a11y_service" />
+        </service>
+        <service
             android:name="android.accessibility.cts.common.InstrumentedAccessibilityService"
             android:label="@string/title_soft_keyboard_modes_accessibility_service"
             android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
diff --git a/tests/accessibilityservice/AndroidTest.xml b/tests/accessibilityservice/AndroidTest.xml
index eb75538..b0e873c 100644
--- a/tests/accessibilityservice/AndroidTest.xml
+++ b/tests/accessibilityservice/AndroidTest.xml
@@ -34,4 +34,8 @@
         <option name="package" value="android.accessibilityservice.cts" />
         <option name="runtime-hint" value="2m12s" />
     </test>
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="directory-keys" value="/sdcard/android.accessibilityservice.cts" />
+        <option name="collect-on-run-ended-only" value="true" />
+    </metrics_collector>
 </configuration>
diff --git a/tests/accessibilityservice/OWNERS b/tests/accessibilityservice/OWNERS
index d225f2c..e54f581 100644
--- a/tests/accessibilityservice/OWNERS
+++ b/tests/accessibilityservice/OWNERS
@@ -1,2 +1,3 @@
 # Bug component: 44214
-pweaver@google.com
\ No newline at end of file
+pweaver@google.com
+rhedjao@google.com
diff --git a/tests/accessibilityservice/res/layout/accessibility_embedded_display_test.xml b/tests/accessibilityservice/res/layout/accessibility_embedded_display_test.xml
new file mode 100644
index 0000000..666bfbe
--- /dev/null
+++ b/tests/accessibilityservice/res/layout/accessibility_embedded_display_test.xml
@@ -0,0 +1,30 @@
+<?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:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:gravity="center"
+              android:orientation="vertical">
+
+    <Button
+        android:id="@+id/button"
+        android:text="@string/button_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+
+</LinearLayout>
diff --git a/tests/accessibilityservice/res/layout/accessibility_view_tree_reporting_test.xml b/tests/accessibilityservice/res/layout/accessibility_view_tree_reporting_test.xml
index 0dbd62a..e05c259 100644
--- a/tests/accessibilityservice/res/layout/accessibility_view_tree_reporting_test.xml
+++ b/tests/accessibilityservice/res/layout/accessibility_view_tree_reporting_test.xml
@@ -88,6 +88,14 @@
               android:text="@string/secondButton"
               android:textSize="10dip" />
 
+          <Button
+              android:id="@+id/hiddenButton"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:text="@string/secondButton"
+              android:textSize="10dip"
+              android:visibility="gone" />
+
       </LinearLayout>
 
     </FrameLayout>
diff --git a/tests/accessibilityservice/res/values/strings.xml b/tests/accessibilityservice/res/values/strings.xml
index ad5d3fd..3fd45bd 100644
--- a/tests/accessibilityservice/res/values/strings.xml
+++ b/tests/accessibilityservice/res/values/strings.xml
@@ -147,7 +147,9 @@
 
     <string name="stub_gesture_dispatch_a11y_service_description">com.android.accessibilityservice.cts.StubGestureAccessibilityService</string>
 
-    <string name="stub_gesture_detector_a11y_service_description">com.android.accessibilityservice.cts.StubService</string>
+    <string name="stub_gesture_detector_a11y_service_description">com.android.accessibilityservice.cts.GestureDetectionStubAccessibilityService</string>
+
+    <string name="stub_touch_exploration_a11y_service_description">com.android.accessibilityservice.cts.TouchExplorationStubAccessibilityService</string>
 
     <string name="stub_fprint_a11y_service_description">com.android.accessibilityservice.cts.StubFingerprintGestureAccessibilityService</string>
 
@@ -170,4 +172,12 @@
     <!-- Description of the accessibility service -->
     <string name="soft_keyboard_modes_accessibility_service_description">This Accessibility Service was installed for testing purposes. It can be uninstalled by going to Settings > Apps > android.view.accessibilityservice.services and selecting \"Uninstall\".</string>
 
+    <!-- AccessibilityEmbeddedDisplayTest -->
+
+    <!-- String title of accessibility embedded display test parent window activity -->
+    <string name="accessibility_embedded_display_test_parent_activity">Embedded display test parent</string>
+
+    <!-- String title of accessibility embedded display test activity -->
+    <string name="accessibility_embedded_display_test_activity">Embedded display test</string>
+
 </resources>
diff --git a/tests/accessibilityservice/res/xml/stub_touch_exploration_a11y_service.xml b/tests/accessibilityservice/res/xml/stub_touch_exploration_a11y_service.xml
new file mode 100644
index 0000000..33c5ec8
--- /dev/null
+++ b/tests/accessibilityservice/res/xml/stub_touch_exploration_a11y_service.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+
+<accessibility-service
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:description="@string/stub_touch_exploration_a11y_service_description"
+        android:accessibilityEventTypes="typeAllMask"
+        android:accessibilityFeedbackType="feedbackGeneric"
+        android:accessibilityFlags="flagDefault|flagRequestTouchExplorationMode|flagReportViewIds"
+        android:canRequestTouchExplorationMode="true"
+        android:canRetrieveWindowContent="true"
+        android:canPerformGestures="true" />
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityButtonTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityButtonTest.java
index 84fb0ef..9ecebcf 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityButtonTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityButtonTest.java
@@ -14,18 +14,20 @@
 
 package android.accessibilityservice.cts;
 
-import static android.accessibilityservice.cts.utils.CtsTestUtils.runIfNotNull;
+import static org.junit.Assert.assertNotNull;
 
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
 import android.accessibilityservice.AccessibilityButtonController;
-import android.app.Instrumentation;
 import android.platform.test.annotations.AppModeFull;
+import android.view.Display;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
-import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 
 /**
@@ -37,6 +39,17 @@
 @RunWith(AndroidJUnit4.class)
 public class AccessibilityButtonTest {
 
+    private InstrumentedAccessibilityServiceTestRule<StubAccessibilityButtonService> mServiceRule =
+            new InstrumentedAccessibilityServiceTestRule<>(StubAccessibilityButtonService.class);
+
+    private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    @Rule
+    public final RuleChain mRuleChain = RuleChain
+            .outerRule(mServiceRule)
+            .around(mDumpOnFailureRule);
+
     private StubAccessibilityButtonService mService;
     private AccessibilityButtonController mButtonController;
     private AccessibilityButtonController.AccessibilityButtonCallback mStubCallback =
@@ -55,20 +68,23 @@
 
     @Before
     public void setUp() {
-        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        mService = StubAccessibilityButtonService.enableSelf(instrumentation);
+        mService = mServiceRule.getService();
         mButtonController = mService.getAccessibilityButtonController();
     }
 
-    @After
-    public void tearDown() {
-        runIfNotNull(mService, service -> service.runOnServiceSync(service::disableSelf));
-    }
-
     @Test
     @AppModeFull
     public void testCallbackRegistrationUnregistration_serviceDoesNotCrash() {
         mButtonController.registerAccessibilityButtonCallback(mStubCallback);
         mButtonController.unregisterAccessibilityButtonCallback(mStubCallback);
     }
+
+    @Test
+    @AppModeFull
+    public void testGetAccessibilityButtonControllerByDisplayId_NotReturnNull() {
+        final AccessibilityButtonController buttonController =
+                mService.getAccessibilityButtonController(
+                        Display.DEFAULT_DISPLAY);
+        assertNotNull(buttonController);
+    }
 }
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEmbeddedDisplayTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEmbeddedDisplayTest.java
new file mode 100644
index 0000000..f2eeb34
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEmbeddedDisplayTest.java
@@ -0,0 +1,250 @@
+/*
+ * 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.accessibilityservice.cts;
+
+import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterWindowsChangeTypesAndWindowTitle;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.findWindowByTitle;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen;
+import static android.accessibilityservice.cts.utils.AsyncUtils.DEFAULT_TIMEOUT_MS;
+import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
+import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_BOUNDS;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibilityservice.cts.activities.AccessibilityTestActivity;
+import android.app.Activity;
+import android.app.ActivityView;
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.platform.test.annotations.Presubmit;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityWindowInfo;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests that AccessibilityWindowInfos and AccessibilityNodeInfos from a window on an embedded
+ * display that is re-parented to another window are properly populated.
+ */
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityEmbeddedDisplayTest {
+    private static Instrumentation sInstrumentation;
+    private static UiAutomation sUiAutomation;
+
+    private EmbeddedDisplayParentActivity mActivity;
+    private ActivityView mActivityView;
+    private Context mContext;
+
+    private String mParentActivityTitle;
+    private String mActivityTitle;
+
+    private final ActivityTestRule<EmbeddedDisplayParentActivity> mActivityRule =
+            new ActivityTestRule<>(EmbeddedDisplayParentActivity.class, false, false);
+
+    private final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    private final static int DEFAULT_ACTIVITYVIEW_HEIGHT = 500;
+    private final static int DEFAULT_ACTIVITYVIEW_WIDTH = 500;
+
+    @Rule
+    public final RuleChain mRuleChain = RuleChain
+            .outerRule(mActivityRule)
+            .around(mDumpOnFailureRule);
+
+    @BeforeClass
+    public static void oneTimeSetup() {
+        sInstrumentation = InstrumentationRegistry.getInstrumentation();
+        sUiAutomation = sInstrumentation.getUiAutomation();
+    }
+
+    @AfterClass
+    public static void postTestTearDown() {
+        sUiAutomation.destroy();
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = sInstrumentation.getContext();
+        assumeTrue(supportsMultiDisplay());
+
+        mParentActivityTitle = mContext.getString(
+                R.string.accessibility_embedded_display_test_parent_activity);
+        mActivityTitle = mContext.getString(R.string.accessibility_embedded_display_test_activity);
+
+        SystemUtil.runWithShellPermissionIdentity(() -> {
+            mActivity = launchActivityAndWaitForItToBeOnscreen(
+                    sInstrumentation, sUiAutomation, mActivityRule);
+            mActivityView = mActivity.getActivityView();
+        });
+
+        launchActivityInActivityView();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mActivityView != null) {
+            SystemUtil.runWithShellPermissionIdentity(() -> mActivityView.release());
+        }
+    }
+
+    @Presubmit
+    @Test
+    public void testA11yWindowInfoHasCorrectLayer() {
+        final AccessibilityWindowInfo parentActivityWindow =
+                findWindowByTitle(sUiAutomation, mParentActivityTitle);
+        final AccessibilityWindowInfo activityWindow =
+                findWindowByTitle(sUiAutomation, mActivityTitle);
+
+        assertNotNull(parentActivityWindow);
+        assertNotNull(activityWindow);
+        assertTrue(parentActivityWindow.getLayer() > activityWindow.getLayer());
+    }
+
+    @Presubmit
+    @Test
+    public void testA11yWindowInfoAndA11yNodeInfoHasCorrectBoundsInScreen() {
+        final AccessibilityWindowInfo parentActivityWindow =
+                findWindowByTitle(sUiAutomation, mParentActivityTitle);
+        final AccessibilityWindowInfo activityWindow =
+                findWindowByTitle(sUiAutomation, mActivityTitle);
+        final AccessibilityNodeInfo button = findWindowByTitle(sUiAutomation,
+                mActivityTitle).getRoot().findAccessibilityNodeInfosByViewId(
+                "android.accessibilityservice.cts:id/button").get(0);
+
+        assertNotNull(parentActivityWindow);
+        assertNotNull(activityWindow);
+        assertNotNull(button);
+
+        final Rect parentActivityBound = new Rect();
+        final Rect activityBound = new Rect();
+        final Rect buttonBound = new Rect();
+        parentActivityWindow.getBoundsInScreen(parentActivityBound);
+        activityWindow.getBoundsInScreen(activityBound);
+        button.getBoundsInScreen(buttonBound);
+
+        assertTrue("parentActivityBound" + parentActivityBound.toShortString()
+                        + " doesn't contain activityBound" + activityBound.toShortString(),
+                parentActivityBound.contains(activityBound));
+        assertTrue("parentActivityBound" + parentActivityBound.toShortString()
+                        + " doesn't contain buttonBound" + buttonBound.toShortString(),
+                parentActivityBound.contains(buttonBound));
+    }
+
+    @Test
+    public void testA11yWindowNotifyWhenResizeActivityView() throws Exception {
+        final AccessibilityWindowInfo oldActivityWindow =
+                findWindowByTitle(sUiAutomation, mActivityTitle);
+        final Rect oldWindowBound = new Rect();
+        oldActivityWindow.getBoundsInScreen(oldWindowBound);
+        assertEquals(DEFAULT_ACTIVITYVIEW_HEIGHT, oldWindowBound.height());
+        assertEquals(DEFAULT_ACTIVITYVIEW_WIDTH, oldWindowBound.width());
+
+        final int width = DEFAULT_ACTIVITYVIEW_WIDTH / 2;
+        final int height = DEFAULT_ACTIVITYVIEW_WIDTH / 2;
+        sUiAutomation.executeAndWaitForEvent(() -> sInstrumentation.runOnMainSync(
+                () -> mActivityView.layout(0, 0, width, height)),
+                filterWindowsChangeTypesAndWindowTitle(sUiAutomation, WINDOWS_CHANGE_BOUNDS,
+                        mActivityTitle),
+                DEFAULT_TIMEOUT_MS);
+
+        final AccessibilityWindowInfo newActivityWindow =
+                findWindowByTitle(sUiAutomation, mActivityTitle);
+        final Rect newWindowBound = new Rect();
+        newActivityWindow.getBoundsInScreen(newWindowBound);
+
+        assertEquals(height, newWindowBound.height());
+        assertEquals(width, newWindowBound.width());
+    }
+
+    private void launchActivityInActivityView() throws Exception {
+        final Rect bounds = new Rect();
+        sUiAutomation.executeAndWaitForEvent(
+                () -> sInstrumentation.runOnMainSync(() -> {
+                    Intent intent = new Intent(mContext, EmbeddedDisplayActivity.class);
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    SystemUtil.runWithShellPermissionIdentity(
+                            () -> mActivityView.startActivity(intent));
+                }),
+                (event) -> {
+                    // Ensure the target activity is shown
+                    final AccessibilityWindowInfo window =
+                            findWindowByTitle(sUiAutomation, mActivityTitle);
+                    if (window == null) {
+                        return false;
+                    }
+                    window.getBoundsInScreen(bounds);
+                    return !bounds.isEmpty();
+                }, DEFAULT_TIMEOUT_MS);
+    }
+
+    private boolean supportsMultiDisplay() {
+        return mContext.getPackageManager().hasSystemFeature(
+                FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
+    }
+
+    public static class EmbeddedDisplayParentActivity extends Activity {
+        private ActivityView mActivityView;
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            mActivityView = new ActivityView(this);
+            setContentView(mActivityView);
+
+            ViewGroup.LayoutParams layoutParams = mActivityView.getLayoutParams();
+            layoutParams.width = DEFAULT_ACTIVITYVIEW_WIDTH;
+            layoutParams.height = DEFAULT_ACTIVITYVIEW_HEIGHT;
+            mActivityView.requestLayout();
+        }
+
+        ActivityView getActivityView() {
+            return mActivityView;
+        }
+    }
+
+    public static class EmbeddedDisplayActivity extends AccessibilityTestActivity {
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            setContentView(R.layout.accessibility_embedded_display_test);
+        }
+    }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
index 60ef850..e9a0fe7 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
@@ -16,6 +16,7 @@
 
 package android.accessibilityservice.cts;
 
+import static android.accessibility.cts.common.InstrumentedAccessibilityService.enableService;
 import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterForEventType;
 import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterForEventTypeWithResource;
 import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.findWindowByTitle;
@@ -45,6 +46,7 @@
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.accessibility.cts.common.InstrumentedAccessibilityService;
 import android.accessibility.cts.common.ShellCommandBuilder;
 import android.accessibilityservice.AccessibilityServiceInfo;
@@ -101,6 +103,7 @@
 import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 
 import java.util.Iterator;
@@ -131,10 +134,17 @@
 
     private AccessibilityEndToEndActivity mActivity;
 
-    @Rule
-    public ActivityTestRule<AccessibilityEndToEndActivity> mActivityRule =
+    private ActivityTestRule<AccessibilityEndToEndActivity> mActivityRule =
             new ActivityTestRule<>(AccessibilityEndToEndActivity.class, false, false);
 
+    private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    @Rule
+    public final RuleChain mRuleChain = RuleChain
+            .outerRule(mActivityRule)
+            .around(mDumpOnFailureRule);
+
     @BeforeClass
     public static void oneTimeSetup() throws Exception {
         sInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -502,8 +512,9 @@
     public void testInterrupt_notifiesService() {
         sInstrumentation
                 .getUiAutomation(UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
-        InstrumentedAccessibilityService service = InstrumentedAccessibilityService.enableService(
-                sInstrumentation, InstrumentedAccessibilityService.class);
+        InstrumentedAccessibilityService service =
+                enableService(InstrumentedAccessibilityService.class);
+
         try {
             assertFalse(service.wasOnInterruptCalled());
 
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFingerprintGestureTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFingerprintGestureTest.java
index 1db66a2..e36d1ee 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFingerprintGestureTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFingerprintGestureTest.java
@@ -14,7 +14,6 @@
 
 package android.accessibilityservice.cts;
 
-import static android.accessibilityservice.cts.utils.CtsTestUtils.runIfNotNull;
 import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
 
 import static org.junit.Assert.assertFalse;
@@ -23,6 +22,8 @@
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
 import android.accessibilityservice.FingerprintGestureController;
 import android.accessibilityservice.FingerprintGestureController.FingerprintGestureCallback;
 import android.accessibilityservice.cts.activities.AccessibilityEndToEndActivity;
@@ -35,10 +36,10 @@
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -57,10 +58,20 @@
     FingerprintGestureController mFingerprintGestureController;
     CancellationSignal mCancellationSignal = new CancellationSignal();
 
-    @Rule
-    public ActivityTestRule<AccessibilityEndToEndActivity> mActivityRule =
+    private ActivityTestRule<AccessibilityEndToEndActivity> mActivityRule =
             new ActivityTestRule<>(AccessibilityEndToEndActivity.class, false, false);
 
+    private InstrumentedAccessibilityServiceTestRule<StubFingerprintGestureService> mServiceRule =
+            new InstrumentedAccessibilityServiceTestRule<>(StubFingerprintGestureService.class);
+
+    private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    @Rule
+    public final RuleChain mRuleChain = RuleChain
+            .outerRule(mActivityRule)
+            .around(mServiceRule)
+            .around(mDumpOnFailureRule);
 
     @Mock FingerprintManager.AuthenticationCallback mMockAuthenticationCallback;
     @Mock FingerprintGestureCallback mMockFingerprintGestureCallback;
@@ -72,17 +83,11 @@
         mFingerprintManager = instrumentation.getContext().getPackageManager()
                 .hasSystemFeature(FEATURE_FINGERPRINT)
                 ? instrumentation.getContext().getSystemService(FingerprintManager.class) : null;
-        mFingerprintGestureService = StubFingerprintGestureService.enableSelf(instrumentation);
+        mFingerprintGestureService = mServiceRule.getService();
         mFingerprintGestureController =
                 mFingerprintGestureService.getFingerprintGestureController();
     }
 
-    @After
-    public void tearDown() throws Exception {
-        runIfNotNull(mFingerprintGestureService,
-                service -> service.runOnServiceSync(service::disableSelf));
-    }
-
     @Test
     public void testGestureDetectionListener_whenAuthenticationStartsAndStops_calledBack() {
         if (!mFingerprintGestureController.isGestureDetectionAvailable()) {
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncTest.java
index 43c12e9..b7ccc19 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncTest.java
@@ -26,8 +26,8 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.accessibilityservice.AccessibilityServiceInfo;
-import android.accessibilityservice.cts.R;
 import android.accessibilityservice.cts.activities.AccessibilityFocusAndInputFocusSyncActivity;
 import android.app.Instrumentation;
 import android.app.UiAutomation;
@@ -46,6 +46,7 @@
 import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 
 import java.util.LinkedList;
@@ -65,10 +66,17 @@
 
     private AccessibilityFocusAndInputFocusSyncActivity mActivity;
 
-    @Rule
-    public ActivityTestRule<AccessibilityFocusAndInputFocusSyncActivity> mActivityRule =
+    private ActivityTestRule<AccessibilityFocusAndInputFocusSyncActivity> mActivityRule =
             new ActivityTestRule<>(AccessibilityFocusAndInputFocusSyncActivity.class, false, false);
 
+    private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    @Rule
+    public final RuleChain mRuleChain = RuleChain
+            .outerRule(mActivityRule)
+            .around(mDumpOnFailureRule);
+
     @BeforeClass
     public static void oneTimeSetup() throws Exception {
         sInstrumentation = InstrumentationRegistry.getInstrumentation();
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java
index 24c00ff..b28b419 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java
@@ -14,20 +14,24 @@
 
 package android.accessibilityservice.cts;
 
-import static android.accessibilityservice.cts.utils.CtsTestUtils.runIfNotNull;
 import static android.accessibilityservice.cts.utils.GestureUtils.click;
 import static android.accessibilityservice.cts.utils.GestureUtils.endTimeOf;
 import static android.accessibilityservice.cts.utils.GestureUtils.longClick;
 import static android.accessibilityservice.cts.utils.GestureUtils.startingAt;
+import static android.accessibilityservice.cts.utils.DisplayUtils.VirtualDisplaySession;
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
+import android.accessibilityservice.AccessibilityGestureInfo;
 import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.GestureDescription;
 import android.accessibilityservice.GestureDescription.StrokeDescription;
+import android.accessibilityservice.cts.utils.DisplayUtils;
 import android.app.Instrumentation;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -36,15 +40,17 @@
 import android.graphics.PointF;
 import android.platform.test.annotations.AppModeFull;
 import android.util.DisplayMetrics;
+import android.view.Display;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
-import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -59,6 +65,18 @@
     private static final long GESTURE_DISPATCH_TIMEOUT_MS = 3000;
     private static final long EVENT_DISPATCH_TIMEOUT_MS = 3000;
 
+    private InstrumentedAccessibilityServiceTestRule<GestureDetectionStubAccessibilityService>
+            mServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+                    GestureDetectionStubAccessibilityService.class, false);
+
+    private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    @Rule
+    public final RuleChain mRuleChain = RuleChain
+            .outerRule(mServiceRule)
+            .around(mDumpOnFailureRule);
+
     // Test AccessibilityService that collects gestures.
     GestureDetectionStubAccessibilityService mService; 
     boolean mHasTouchScreen;
@@ -97,15 +115,7 @@
             return;
         }
         // Start stub accessibility service.
-        mService = GestureDetectionStubAccessibilityService.enableSelf(instrumentation);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        if (!mHasTouchScreen || !mScreenBigEnough) {
-            return;
-        }
-        runIfNotNull(mService, service -> service.runOnServiceSync(service::disableSelf));
+        mService = mServiceRule.enableService();
     }
 
     @Test
@@ -142,27 +152,91 @@
         testPath(p(+0, +dy), p(+0, +0), AccessibilityService.GESTURE_SWIPE_DOWN_AND_UP);
     }
 
+    @Test
+    @AppModeFull
+    public void testRecognizeGesturePathOnVirtualDisplay() {
+        if (!mHasTouchScreen || !mScreenBigEnough) {
+            return;
+        }
+
+        try (final VirtualDisplaySession displaySession = new VirtualDisplaySession()) {
+            final int displayId = displaySession.createDisplayWithDefaultDisplayMetricsAndWait(
+                    InstrumentationRegistry.getInstrumentation().getTargetContext(),
+                            false).getDisplayId();
+            // Compute gesture stroke lengths, in pixels.
+            final int dx = mStrokeLenPxX;
+            final int dy = mStrokeLenPxY;
+
+            // Test recognizing various gestures.
+            testPath(p(-dx, +0), AccessibilityService.GESTURE_SWIPE_LEFT, displayId);
+            testPath(p(+dx, +0), AccessibilityService.GESTURE_SWIPE_RIGHT, displayId);
+            testPath(p(+0, -dy), AccessibilityService.GESTURE_SWIPE_UP, displayId);
+            testPath(p(+0, +dy), AccessibilityService.GESTURE_SWIPE_DOWN, displayId);
+
+            testPath(p(-dx, +0), p(+0, +0), AccessibilityService.GESTURE_SWIPE_LEFT_AND_RIGHT,
+                    displayId);
+            testPath(p(-dx, +0), p(-dx, -dy), AccessibilityService.GESTURE_SWIPE_LEFT_AND_UP,
+                    displayId);
+            testPath(p(-dx, +0), p(-dx, +dy), AccessibilityService.GESTURE_SWIPE_LEFT_AND_DOWN,
+                    displayId);
+
+            testPath(p(+dx, +0), p(+0, +0), AccessibilityService.GESTURE_SWIPE_RIGHT_AND_LEFT,
+                    displayId);
+            testPath(p(+dx, +0), p(+dx, -dy), AccessibilityService.GESTURE_SWIPE_RIGHT_AND_UP,
+                    displayId);
+            testPath(p(+dx, +0), p(+dx, +dy), AccessibilityService.GESTURE_SWIPE_RIGHT_AND_DOWN,
+                    displayId);
+
+            testPath(p(+0, -dy), p(-dx, -dy), AccessibilityService.GESTURE_SWIPE_UP_AND_LEFT,
+                    displayId);
+            testPath(p(+0, -dy), p(+dx, -dy), AccessibilityService.GESTURE_SWIPE_UP_AND_RIGHT,
+                    displayId);
+            testPath(p(+0, -dy), p(+0, +0), AccessibilityService.GESTURE_SWIPE_UP_AND_DOWN,
+                    displayId);
+
+            testPath(p(+0, +dy), p(-dx, +dy), AccessibilityService.GESTURE_SWIPE_DOWN_AND_LEFT,
+                    displayId);
+            testPath(p(+0, +dy), p(+dx, +dy), AccessibilityService.GESTURE_SWIPE_DOWN_AND_RIGHT,
+                    displayId);
+            testPath(p(+0, +dy), p(+0, +0), AccessibilityService.GESTURE_SWIPE_DOWN_AND_UP,
+                    displayId);
+        }
+    }
+
     /** Convenient short alias to make a Point. */
     private static Point p(int x, int y) {
         return new Point(x, y);
     }
 
-    /** Test recognizing path from PATH_START to PATH_START+delta. */
+    /** Test recognizing path from PATH_START to PATH_START+delta on default display. */
     private void testPath(Point delta, int gestureId) {
-        testPath(delta, null, gestureId);
+        testPath(delta, null, gestureId, Display.DEFAULT_DISPLAY);
     }
 
-    /** Test recognizing path from PATH_START to PATH_START+delta1 to PATH_START+delta2. */
+    /** Test recognizing path from PATH_START to PATH_START+delta on specified display. */
+    private void testPath(Point delta, int gestureId, int displayId) {
+        testPath(delta, null, gestureId, displayId);
+    }
+    /** Test recognizing path from PATH_START to PATH_START+delta on default display. */
     private void testPath(Point delta1, Point delta2, int gestureId) {
+        testPath(delta1, delta2, gestureId, Display.DEFAULT_DISPLAY);
+    }
+
+    /**
+     * Test recognizing path from PATH_START to PATH_START+delta1 to PATH_START+delta2. on specified
+     * display.
+     */
+    private void testPath(Point delta1, Point delta2, int gestureId, int displayId) {
         // Create gesture motions.
         int numPathSegments = (delta2 == null) ? 1 : 2;
         long pathDurationMs = numPathSegments * STROKE_MS;
         GestureDescription gesture = new GestureDescription.Builder()
                 .addStroke(new StrokeDescription(
                 linePath(mCenter, delta1, delta2), 0, pathDurationMs, false))
+                .setDisplayId(displayId)
                 .build();
 
-        // Dispatch gesture motions.
+        // Dispatch gesture motions to specified  display with GestureDescription..
         // Use AccessibilityService.dispatchGesture() instead of Instrumentation.sendPointerSync()
         // because accessibility services read gesture events upstream from the point where
         // sendPointerSync() injects events.
@@ -173,9 +247,16 @@
                 .onCompleted(any());
 
         // Wait for gesture recognizer, and check recognized gesture.
-        mService.waitUntilGesture();
-        assertEquals(1, mService.getGesturesSize());
-        assertEquals(gestureId, mService.getGesture(0));
+        mService.waitUntilGestureInfo();
+        if(displayId == Display.DEFAULT_DISPLAY) {
+            assertEquals(1, mService.getGesturesSize());
+            assertEquals(gestureId, mService.getGesture(0));
+        }
+
+        AccessibilityGestureInfo expectedGestureInfo = new AccessibilityGestureInfo(gestureId,
+                displayId);
+        assertEquals(1, mService.getGestureInfoSize());
+        assertEquals(expectedGestureInfo.toString(), mService.getGestureInfo(0).toString());
     }
 
     /** Create a path from startPoint, moving by delta1, then delta2. (delta2 may be null.) */
@@ -196,25 +277,74 @@
             return;
         }
 
-        assertEventAfterGesture(swipe(),
+        assertEventAfterGesture(swipe(Display.DEFAULT_DISPLAY),
                 AccessibilityEvent.TYPE_TOUCH_INTERACTION_START,
                 AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
 
-        assertEventAfterGesture(tap(),
+        assertEventAfterGesture(tap(Display.DEFAULT_DISPLAY),
                 AccessibilityEvent.TYPE_TOUCH_INTERACTION_START,
                 AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START,
                 AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END,
                 AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
 
-        assertEventAfterGesture(doubleTap(),
+        assertEventAfterGesture(doubleTap(Display.DEFAULT_DISPLAY),
                 AccessibilityEvent.TYPE_TOUCH_INTERACTION_START,
                 AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
 
-        assertEventAfterGesture(doubleTapAndHold(),
+        assertEventAfterGesture(doubleTapAndHold(Display.DEFAULT_DISPLAY),
                 AccessibilityEvent.TYPE_TOUCH_INTERACTION_START,
                 AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
     }
 
+    @Test
+    @AppModeFull
+    public void testVerifyGestureTouchEventOnVirtualDisplay() {
+        if (!mHasTouchScreen || !mScreenBigEnough) {
+            return;
+        }
+
+        try (final VirtualDisplaySession displaySession = new VirtualDisplaySession()) {
+            final int displayId = displaySession.createDisplayWithDefaultDisplayMetricsAndWait(
+                    InstrumentationRegistry.getInstrumentation().getTargetContext(),
+                    false).getDisplayId();
+
+            assertEventAfterGesture(swipe(displayId),
+                    AccessibilityEvent.TYPE_TOUCH_INTERACTION_START,
+                    AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+
+            assertEventAfterGesture(tap(displayId),
+                    AccessibilityEvent.TYPE_TOUCH_INTERACTION_START,
+                    AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START,
+                    AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END,
+                    AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+
+            assertEventAfterGesture(doubleTap(displayId),
+                    AccessibilityEvent.TYPE_TOUCH_INTERACTION_START,
+                    AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+
+            assertEventAfterGesture(doubleTapAndHold(displayId),
+                    AccessibilityEvent.TYPE_TOUCH_INTERACTION_START,
+                    AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+        }
+    }
+
+    @Test
+    @AppModeFull
+    public void testDispatchGesture_privateDisplay_gestureCancelled() {
+        try (final VirtualDisplaySession displaySession = new VirtualDisplaySession()) {
+            final int displayId = displaySession.createDisplayWithDefaultDisplayMetricsAndWait
+                    (InstrumentationRegistry.getInstrumentation().getTargetContext(),
+                            true).getDisplayId();
+            GestureDescription gesture = swipe(displayId);
+            mService.clearGestures();
+            mService.runOnServiceSync(() ->
+                    mService.dispatchGesture(gesture, mGestureDispatchCallback, null));
+
+            verify(mGestureDispatchCallback, timeout(GESTURE_DISPATCH_TIMEOUT_MS).atLeastOnce())
+                    .onCancelled(any());
+        }
+    }
+
     /** Test touch for accessibility events */
     private void assertEventAfterGesture(GestureDescription gesture, int... events) {
         mService.clearEvents();
@@ -230,36 +360,40 @@
         }
     }
 
-    private GestureDescription swipe() {
+    private GestureDescription swipe(int displayId) {
         GestureDescription.Builder builder = new GestureDescription.Builder();
         StrokeDescription swipe = new StrokeDescription(
                 linePath(mCenter, p(0, mStrokeLenPxY), null), 0, STROKE_MS, false);
         builder.addStroke(swipe);
+        builder.setDisplayId(displayId);
         return builder.build();
     }
 
-    private GestureDescription tap() {
+    private GestureDescription tap(int displayId) {
         GestureDescription.Builder builder = new GestureDescription.Builder();
         StrokeDescription tap = click(mTapLocation);
         builder.addStroke(tap);
+        builder.setDisplayId(displayId);
         return builder.build();
     }
 
-    private GestureDescription doubleTap() {
+    private GestureDescription doubleTap(int displayId) {
         GestureDescription.Builder builder = new GestureDescription.Builder();
         StrokeDescription tap1 = click(mTapLocation);
         StrokeDescription tap2 = startingAt(endTimeOf(tap1) + 20, click(mTapLocation));
         builder.addStroke(tap1);
         builder.addStroke(tap2);
+        builder.setDisplayId(displayId);
         return builder.build();
     }
 
-    private GestureDescription doubleTapAndHold() {
+    private GestureDescription doubleTapAndHold(int displayId) {
         GestureDescription.Builder builder = new GestureDescription.Builder();
         StrokeDescription tap1 = click(mTapLocation);
         StrokeDescription tap2 = startingAt(endTimeOf(tap1) + 20, longClick(mTapLocation));
         builder.addStroke(tap1);
         builder.addStroke(tap2);
+        builder.setDisplayId(displayId);
         return builder.build();
     }
 }
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
index 2c5da19..a56cf07 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
@@ -14,31 +14,46 @@
 
 package android.accessibilityservice.cts;
 
+import static android.accessibility.cts.common.InstrumentedAccessibilityService.enableService;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen;
 import static android.accessibilityservice.cts.utils.AsyncUtils.await;
 import static android.accessibilityservice.cts.utils.AsyncUtils.awaitCancellation;
-import static android.accessibilityservice.cts.utils.CtsTestUtils.runIfNotNull;
+import static android.accessibilityservice.cts.utils.GestureUtils.IS_ACTION_CANCEL;
+import static android.accessibilityservice.cts.utils.GestureUtils.IS_ACTION_DOWN;
+import static android.accessibilityservice.cts.utils.GestureUtils.IS_ACTION_MOVE;
+import static android.accessibilityservice.cts.utils.GestureUtils.IS_ACTION_POINTER_DOWN;
+import static android.accessibilityservice.cts.utils.GestureUtils.IS_ACTION_POINTER_UP;
+import static android.accessibilityservice.cts.utils.GestureUtils.IS_ACTION_UP;
 import static android.accessibilityservice.cts.utils.GestureUtils.add;
 import static android.accessibilityservice.cts.utils.GestureUtils.ceil;
 import static android.accessibilityservice.cts.utils.GestureUtils.click;
 import static android.accessibilityservice.cts.utils.GestureUtils.diff;
 import static android.accessibilityservice.cts.utils.GestureUtils.dispatchGesture;
+import static android.accessibilityservice.cts.utils.GestureUtils.isAtPoint;
 import static android.accessibilityservice.cts.utils.GestureUtils.longClick;
 import static android.accessibilityservice.cts.utils.GestureUtils.path;
 import static android.accessibilityservice.cts.utils.GestureUtils.times;
 
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
 import static org.hamcrest.CoreMatchers.allOf;
 import static org.hamcrest.CoreMatchers.any;
 import static org.hamcrest.CoreMatchers.both;
 import static org.hamcrest.CoreMatchers.everyItem;
 import static org.hamcrest.CoreMatchers.hasItem;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
 import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.GestureDescription;
 import android.accessibilityservice.GestureDescription.StrokeDescription;
 import android.accessibilityservice.cts.activities.AccessibilityTestActivity;
+import android.app.Instrumentation;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.graphics.Matrix;
@@ -47,7 +62,6 @@
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.platform.test.annotations.AppModeFull;
-import android.test.ActivityInstrumentationTestCase2;
 import android.util.Log;
 import android.view.Display;
 import android.view.MotionEvent;
@@ -56,9 +70,15 @@
 import android.view.WindowManager;
 import android.widget.TextView;
 
-import org.hamcrest.Description;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.hamcrest.Matcher;
-import org.hamcrest.TypeSafeMatcher;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -68,26 +88,28 @@
  * Verify that gestures dispatched from an accessibility service show up in the current UI
  */
 @AppModeFull
-public class AccessibilityGestureDispatchTest extends
-        ActivityInstrumentationTestCase2<AccessibilityGestureDispatchTest.GestureDispatchActivity> {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityGestureDispatchTest {
     private static final String TAG = AccessibilityGestureDispatchTest.class.getSimpleName();
 
     private static final int GESTURE_COMPLETION_TIMEOUT = 5000; // millis
     private static final int MOTION_EVENT_TIMEOUT = 1000; // millis
 
-    private static final Matcher<MotionEvent> IS_ACTION_DOWN =
-            new MotionEventActionMatcher(MotionEvent.ACTION_DOWN);
-    private static final Matcher<MotionEvent> IS_ACTION_POINTER_DOWN =
-            new MotionEventActionMatcher(MotionEvent.ACTION_POINTER_DOWN);
-    private static final Matcher<MotionEvent> IS_ACTION_UP =
-            new MotionEventActionMatcher(MotionEvent.ACTION_UP);
-    private static final Matcher<MotionEvent> IS_ACTION_POINTER_UP =
-            new MotionEventActionMatcher(MotionEvent.ACTION_POINTER_UP);
-    private static final Matcher<MotionEvent> IS_ACTION_CANCEL =
-            new MotionEventActionMatcher(MotionEvent.ACTION_CANCEL);
-    private static final Matcher<MotionEvent> IS_ACTION_MOVE =
-            new MotionEventActionMatcher(MotionEvent.ACTION_MOVE);
+    private ActivityTestRule<GestureDispatchActivity> mActivityRule =
+            new ActivityTestRule<>(GestureDispatchActivity.class, false, false);
 
+    private InstrumentedAccessibilityServiceTestRule<StubGestureAccessibilityService> mServiceRule =
+            new InstrumentedAccessibilityServiceTestRule<>(
+                    StubGestureAccessibilityService.class, false);
+
+    private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    @Rule
+    public final RuleChain mRuleChain = RuleChain
+            .outerRule(mActivityRule)
+            .around(mServiceRule)
+            .around(mDumpOnFailureRule);
 
     final List<MotionEvent> mMotionEvents = new ArrayList<>();
     StubGestureAccessibilityService mService;
@@ -100,27 +122,25 @@
     boolean mHasTouchScreen;
     boolean mHasMultiTouch;
 
-    public AccessibilityGestureDispatchTest() {
-        super(GestureDispatchActivity.class);
-    }
+    private GestureDispatchActivity mActivity;
 
-    @Override
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
-        PackageManager pm = getInstrumentation().getContext().getPackageManager();
+        Instrumentation instrumentation = getInstrumentation();
+        PackageManager pm = instrumentation.getContext().getPackageManager();
         mHasTouchScreen = pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)
                 || pm.hasSystemFeature(PackageManager.FEATURE_FAKETOUCH);
         if (!mHasTouchScreen) {
             return;
         }
 
-        getActivity().waitForEnterAnimationComplete();
+        mActivity = launchActivityAndWaitForItToBeOnscreen(instrumentation,
+                instrumentation.getUiAutomation(), mActivityRule);
 
         mHasMultiTouch = pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)
                 || pm.hasSystemFeature(PackageManager.FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT);
 
-        mFullScreenTextView =
-                (TextView) getActivity().findViewById(R.id.full_screen_text_view);
+        mFullScreenTextView = mActivity.findViewById(R.id.full_screen_text_view);
         getInstrumentation().runOnMainSync(() -> {
             final int midX = mFullScreenTextView.getWidth() / 2;
             final int midY = mFullScreenTextView.getHeight() / 2;
@@ -129,22 +149,13 @@
             mStartPoint.set(mViewLocation[0] + midX, mViewLocation[1] + midY);
         });
 
-        mService = StubGestureAccessibilityService.enableSelf(getInstrumentation());
+        mService = mServiceRule.enableService();
 
         mMotionEvents.clear();
         mGotUpEvent = false;
     }
 
-    @Override
-    public void tearDown() throws Exception {
-        if (!mHasTouchScreen) {
-            return;
-        }
-
-        runIfNotNull(mService, service -> service.runOnServiceSync(service::disableSelf));
-        super.tearDown();
-    }
-
+    @Test
     public void testClickAt_producesDownThenUp() throws InterruptedException {
         if (!mHasTouchScreen) {
             return;
@@ -164,10 +175,10 @@
         assertEquals(0, clickDown.getActionIndex());
         assertEquals(0, clickDown.getDeviceId());
         assertEquals(0, clickDown.getEdgeFlags());
-        assertEquals(1F, clickDown.getXPrecision());
-        assertEquals(1F, clickDown.getYPrecision());
+        assertEquals(1F, clickDown.getXPrecision(), 0F);
+        assertEquals(1F, clickDown.getYPrecision(), 0F);
         assertEquals(1, clickDown.getPointerCount());
-        assertEquals(1F, clickDown.getPressure());
+        assertEquals(1F, clickDown.getPressure(), 0F);
 
         // Verify timing matches click
         assertEquals(clickDown.getDownTime(), clickDown.getEventTime());
@@ -178,6 +189,7 @@
                 > clickUp.getEventTime());
     }
 
+    @Test
     public void testLongClickAt_producesEventsWithLongClickTiming() throws InterruptedException {
         if (!mHasTouchScreen) {
             return;
@@ -198,6 +210,7 @@
         assertEquals(clickDown.getDownTime(), clickUp.getDownTime());
     }
 
+    @Test
     public void testSwipe_shouldContainPointsInALine() throws InterruptedException {
         if (!mHasTouchScreen) {
             return;
@@ -236,6 +249,7 @@
         await(dispatchGesture(mService, gesture), timeoutMs, MILLISECONDS);
     }
 
+    @Test
     public void testSlowSwipe_shouldNotContainMovesForTinyMovement() throws InterruptedException {
         if (!mHasTouchScreen) {
             return;
@@ -260,6 +274,7 @@
         assertThat(mMotionEvents.get(4), both(IS_ACTION_UP).and(isAtPoint(endPoint)));
     }
 
+    @Test
     public void testAngledPinch_looksReasonable() throws InterruptedException {
         if (!(mHasTouchScreen && mHasMultiTouch)) {
             return;
@@ -309,6 +324,7 @@
 
     // This test assumes device's screen contains its center (W/2, H/2) with some surroundings
     // and should work for rectangular, round and round with chin screens.
+    @Test
     public void testClickWhenMagnified_matchesActualTouch() throws InterruptedException {
         final float POINT_TOL = 2.0f;
         final float CLICK_OFFSET_X = 10;
@@ -318,7 +334,7 @@
             return;
         }
 
-        int displayId = getActivity().getWindow().getDecorView().getDisplay().getDisplayId();
+        int displayId = mActivity.getWindow().getDecorView().getDisplay().getDisplayId();
         if (displayId != Display.DEFAULT_DISPLAY) {
             Log.i(TAG, "Magnification is not supported on virtual displays.");
             return;
@@ -327,7 +343,7 @@
         final WindowManager wm = (WindowManager) getInstrumentation().getContext().getSystemService(
                 Context.WINDOW_SERVICE);
         final StubMagnificationAccessibilityService magnificationService =
-                StubMagnificationAccessibilityService.enableSelf(getInstrumentation());
+                enableService(StubMagnificationAccessibilityService.class);
         final AccessibilityService.MagnificationController
                 magnificationController = magnificationService.getMagnificationController();
 
@@ -392,6 +408,7 @@
                 both(IS_ACTION_UP).and(isAtPoint(magRegionOffsetPoint, POINT_TOL)));
     }
 
+    @Test
     public void testContinuedGestures_motionEventsContinue() throws Exception {
         if (!mHasTouchScreen) {
             return;
@@ -426,6 +443,7 @@
                 allOf(IS_ACTION_UP, isAtPoint(end)));
     }
 
+    @Test
     public void testContinuedGesture_withLineDisconnect_isCancelled() throws Exception {
         if (!mHasTouchScreen) {
             return;
@@ -454,6 +472,7 @@
         assertEquals(1, mMotionEvents.size());
     }
 
+    @Test
     public void testContinuedGesture_nextGestureDoesntContinue_isCancelled() throws Exception {
         if (!mHasTouchScreen) {
             return;
@@ -486,6 +505,7 @@
                 both(IS_ACTION_UP).and(isAtPoint(endPoint)));
     }
 
+    @Test
     public void testContinuingGesture_withNothingToContinue_isCancelled() {
         if (!mHasTouchScreen) {
             return;
@@ -631,42 +651,4 @@
                 .addStroke(new StrokeDescription(path2, 0, duration))
                 .build();
     }
-
-    private static class MotionEventActionMatcher extends TypeSafeMatcher<MotionEvent> {
-        int mAction;
-
-        MotionEventActionMatcher(int action) {
-            super();
-            mAction = action;
-        }
-
-        @Override
-        protected boolean matchesSafely(MotionEvent motionEvent) {
-            return motionEvent.getActionMasked() == mAction;
-        }
-
-        @Override
-        public void describeTo(Description description) {
-            description.appendText("Matching to action " + MotionEvent.actionToString(mAction));
-        }
-    }
-
-
-    Matcher<MotionEvent> isAtPoint(final PointF point) {
-        return isAtPoint(point, 0.01f);
-    }
-
-    Matcher<MotionEvent> isAtPoint(final PointF point, final float tol) {
-        return new TypeSafeMatcher<MotionEvent>() {
-            @Override
-            protected boolean matchesSafely(MotionEvent event) {
-                return Math.hypot(event.getX() - point.x, event.getY() - point.y) < tol;
-            }
-
-            @Override
-            public void describeTo(Description description) {
-                description.appendText("Matching to point " + point);
-            }
-        };
-    }
 }
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGlobalActionsTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGlobalActionsTest.java
index c809f26..b5b5766 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGlobalActionsTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGlobalActionsTest.java
@@ -16,14 +16,30 @@
 
 package android.accessibilityservice.cts;
 
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.homeScreenOrBust;
+
+import static org.junit.Assert.assertTrue;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.accessibilityservice.AccessibilityService;
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.Instrumentation;
+import android.app.UiAutomation;
 import android.os.SystemClock;
 import android.platform.test.annotations.AppModeFull;
 import android.platform.test.annotations.Presubmit;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 
-import androidx.test.filters.FlakyTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.util.concurrent.TimeoutException;
 
@@ -32,7 +48,8 @@
  */
 @Presubmit
 @AppModeFull
-public class AccessibilityGlobalActionsTest extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityGlobalActionsTest {
     /**
      * Timeout required for pending Binder calls or event processing to
      * complete.
@@ -44,109 +61,113 @@
      */
     private static final long TIMEOUT_ACCESSIBILITY_STATE_IDLE = 500;
 
+    private static Instrumentation sInstrumentation;
+    private static UiAutomation sUiAutomation;
+
+    @Rule
+    public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    @BeforeClass
+    public static void oneTimeSetup() {
+        sInstrumentation = InstrumentationRegistry.getInstrumentation();
+        sUiAutomation = sInstrumentation.getUiAutomation();
+        AccessibilityServiceInfo info = sUiAutomation.getServiceInfo();
+        info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
+        sUiAutomation.setServiceInfo(info);
+    }
+
+    @AfterClass
+    public static void postTestTearDown() {
+        sUiAutomation.destroy();
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        // Make sure we start test on home screen so we can know if clean up is successful
+        // or not in the end.
+        homeScreenOrBust(sInstrumentation.getContext(), sUiAutomation);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        // Make sure we clean up and back to home screen again, or let test fail...
+        homeScreenOrBust(sInstrumentation.getContext(), sUiAutomation);
+    }
+
     @MediumTest
+    @Test
     public void testPerformGlobalActionBack() throws Exception {
-        assertTrue(getInstrumentation().getUiAutomation().performGlobalAction(
-                AccessibilityService.GLOBAL_ACTION_BACK));
+        assertTrue(sUiAutomation.performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK));
 
         // Sleep a bit so the UI is settled.
         waitForIdle();
     }
 
     @MediumTest
+    @Test
     public void testPerformGlobalActionHome() throws Exception {
-        assertTrue(getInstrumentation().getUiAutomation().performGlobalAction(
-                AccessibilityService.GLOBAL_ACTION_HOME));
+        assertTrue(sUiAutomation.performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME));
 
         // Sleep a bit so the UI is settled.
         waitForIdle();
     }
 
     @MediumTest
+    @Test
     public void testPerformGlobalActionRecents() throws Exception {
         // Perform the action.
-        boolean actionWasPerformed =
-                getInstrumentation().getUiAutomation().performGlobalAction(
-                        AccessibilityService.GLOBAL_ACTION_RECENTS);
+        boolean actionWasPerformed = sUiAutomation.performGlobalAction(
+                AccessibilityService.GLOBAL_ACTION_RECENTS);
 
         // Sleep a bit so the UI is settled.
         waitForIdle();
 
         if (actionWasPerformed) {
-            // This is a necessary since the back action does not
+            // This is a necessary since the global action does not
             // dismiss recents until they stop animating. Sigh...
             SystemClock.sleep(5000);
-
-            // Clean up.
-            getInstrumentation().getUiAutomation().performGlobalAction(
-                    AccessibilityService.GLOBAL_ACTION_BACK);
         }
-
-        // Sleep a bit so the UI is settled.
-        waitForIdle();
     }
 
     @MediumTest
-    @FlakyTest
+    @Test
     public void testPerformGlobalActionNotifications() throws Exception {
         // Perform the action under test
-        assertTrue(getInstrumentation().getUiAutomation().performGlobalAction(
+        assertTrue(sUiAutomation.performGlobalAction(
                 AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS));
 
         // Sleep a bit so the UI is settled.
         waitForIdle();
-
-        // Clean up.
-        assertTrue(getInstrumentation().getUiAutomation().performGlobalAction(
-                AccessibilityService.GLOBAL_ACTION_BACK));
-
-        // Sleep a bit so the UI is settled.
-        waitForIdle();
     }
 
     @MediumTest
-    @FlakyTest
+    @Test
     public void testPerformGlobalActionQuickSettings() throws Exception {
         // Check whether the action succeeded.
-        assertTrue(getInstrumentation().getUiAutomation().performGlobalAction(
+        assertTrue(sUiAutomation.performGlobalAction(
                 AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS));
 
         // Sleep a bit so the UI is settled.
         waitForIdle();
-
-        // Clean up.
-        // The GLOBAL_ACTION_HOME is needed in the case where additional
-        // notifications showing up, we need to clear them as well for the upcoming
-        // new tests to work properly.
-        getInstrumentation().getUiAutomation().performGlobalAction(
-                AccessibilityService.GLOBAL_ACTION_HOME);
-
-        // Sleep a bit so the UI is settled.
-        waitForIdle();
     }
 
     @MediumTest
-    @FlakyTest
+    @Test
     public void testPerformGlobalActionPowerDialog() throws Exception {
         // Check whether the action succeeded.
-        assertTrue(getInstrumentation().getUiAutomation().performGlobalAction(
+        assertTrue(sUiAutomation.performGlobalAction(
                 AccessibilityService.GLOBAL_ACTION_POWER_DIALOG));
 
         // Sleep a bit so the UI is settled.
         waitForIdle();
-
-        // Clean up.
-        getInstrumentation().getUiAutomation().performGlobalAction(
-                AccessibilityService.GLOBAL_ACTION_BACK);
-
-        // Sleep a bit so the UI is settled.
-        waitForIdle();
     }
 
     @MediumTest
+    @Test
     public void testPerformActionScreenshot() throws Exception {
         // Action should succeed
-        assertTrue(getInstrumentation().getUiAutomation().performGlobalAction(
+        assertTrue(sUiAutomation.performGlobalAction(
                 AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT));
         // Ideally should verify that we actually have a screenshot, but it's also possible
         // for the screenshot to fail
@@ -154,8 +175,6 @@
     }
 
     private void waitForIdle() throws TimeoutException {
-        getInstrumentation().getUiAutomation().waitForIdle(
-                TIMEOUT_ACCESSIBILITY_STATE_IDLE,
-                TIMEOUT_ASYNC_PROCESSING);
+        sUiAutomation.waitForIdle(TIMEOUT_ACCESSIBILITY_STATE_IDLE, TIMEOUT_ASYNC_PROCESSING);
     }
 }
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityLoggingTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityLoggingTest.java
index 9654352..66baa04 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityLoggingTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityLoggingTest.java
@@ -17,6 +17,7 @@
 
 import static junit.framework.Assert.assertTrue;
 
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.content.Context;
 import android.platform.test.annotations.Presubmit;
 
@@ -25,6 +26,7 @@
 
 import com.android.compatibility.common.util.AppOpsUtils;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -32,6 +34,10 @@
 @Presubmit
 public class AccessibilityLoggingTest {
 
+    @Rule
+    public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
     /**
      * Tests that new accessibility services are logged by the system.
      */
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java
index 2774457..c832368 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java
@@ -16,8 +16,16 @@
 
 package android.accessibilityservice.cts;
 
-import static android.accessibilityservice.cts.utils.CtsTestUtils.runIfNotNull;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen;
 
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.compatibility.common.util.TestUtils.waitOn;
+
+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.mockito.Mockito.any;
 import static org.mockito.Mockito.anyFloat;
 import static org.mockito.Mockito.eq;
@@ -25,16 +33,35 @@
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
 import android.accessibility.cts.common.ShellCommandBuilder;
 import android.accessibilityservice.AccessibilityService.MagnificationController;
 import android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener;
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.cts.activities.AccessibilityWindowQueryActivity;
+import android.app.Activity;
 import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.platform.test.annotations.AppModeFull;
-import android.test.InstrumentationTestCase;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.Button;
+
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
 
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -42,7 +69,8 @@
  * Class for testing {@link AccessibilityServiceInfo}.
  */
 @AppModeFull
-public class AccessibilityMagnificationTest extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityMagnificationTest {
 
     /** Maximum timeout when waiting for a magnification callback. */
     public static final int LISTENER_TIMEOUT_MILLIS = 500;
@@ -51,25 +79,39 @@
     private StubMagnificationAccessibilityService mService;
     private Instrumentation mInstrumentation;
 
-    @Override
+    private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    private final ActivityTestRule<AccessibilityWindowQueryActivity> mActivityRule =
+            new ActivityTestRule<>(AccessibilityWindowQueryActivity.class, false, false);
+
+    private InstrumentedAccessibilityServiceTestRule<InstrumentedAccessibilityService>
+            mInstrumentedAccessibilityServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+                    InstrumentedAccessibilityService.class, false);
+
+    private InstrumentedAccessibilityServiceTestRule<StubMagnificationAccessibilityService>
+            mMagnificationAccessibilityServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+                    StubMagnificationAccessibilityService.class, false);
+
+    @Rule
+    public final RuleChain mRuleChain = RuleChain
+            .outerRule(mActivityRule)
+            .around(mMagnificationAccessibilityServiceRule)
+            .around(mInstrumentedAccessibilityServiceRule)
+            .around(mDumpOnFailureRule);
+
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
-        ShellCommandBuilder.create(this.getInstrumentation())
+        ShellCommandBuilder.create(getInstrumentation())
                 .deleteSecureSetting(ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED)
                 .run();
         mInstrumentation = getInstrumentation();
         // Starting the service will force the accessibility subsystem to examine its settings, so
         // it will update magnification in the process to disable it.
-        mService = StubMagnificationAccessibilityService.enableSelf(mInstrumentation);
+        mService = mMagnificationAccessibilityServiceRule.enableService();
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        runIfNotNull(mService, service -> service.runOnServiceSync(service::disableSelfAndRemove));
-
-        super.tearDown();
-    }
-
+    @Test
     public void testSetScale() {
         final MagnificationController controller = mService.getMagnificationController();
         final float scale = 2.0f;
@@ -78,14 +120,15 @@
         mService.runOnServiceSync(() -> result.set(controller.setScale(scale, false)));
 
         assertTrue("Failed to set scale", result.get());
-        assertEquals("Failed to apply scale", scale, controller.getScale());
+        assertEquals("Failed to apply scale", scale, controller.getScale(), 0f);
 
         mService.runOnServiceSync(() -> result.set(controller.reset(false)));
 
         assertTrue("Failed to reset", result.get());
-        assertEquals("Failed to apply reset", 1.0f, controller.getScale());
+        assertEquals("Failed to apply reset", 1.0f, controller.getScale(), 0f);
     }
 
+    @Test
     public void testSetScaleAndCenter() {
         final MagnificationController controller = mService.getMagnificationController();
         final Region region = controller.getMagnificationRegion();
@@ -103,7 +146,7 @@
         });
 
         assertTrue("Failed to set scale", setScale.get());
-        assertEquals("Failed to apply scale", scale, controller.getScale());
+        assertEquals("Failed to apply scale", scale, controller.getScale(), 0f);
 
         assertTrue("Failed to set center", setCenter.get());
         assertEquals("Failed to apply center X", x, controller.getCenterX(), 5.0f);
@@ -112,9 +155,10 @@
         mService.runOnServiceSync(() -> result.set(controller.reset(false)));
 
         assertTrue("Failed to reset", result.get());
-        assertEquals("Failed to apply reset", 1.0f, controller.getScale());
+        assertEquals("Failed to apply reset", 1.0f, controller.getScale(), 0f);
     }
 
+    @Test
     public void testListener() {
         final MagnificationController controller = mService.getMagnificationController();
         final OnMagnificationChangedListener listener = mock(OnMagnificationChangedListener.class);
@@ -140,23 +184,21 @@
         }
     }
 
+    @Test
     public void testMagnificationServiceShutsDownWhileMagnifying_shouldReturnTo1x() {
         final MagnificationController controller = mService.getMagnificationController();
         mService.runOnServiceSync(() -> controller.setScale(2.0f, false));
 
         mService.runOnServiceSync(() -> mService.disableSelf());
         mService = null;
-        InstrumentedAccessibilityService service = InstrumentedAccessibilityService.enableService(
-                mInstrumentation, InstrumentedAccessibilityService.class);
+        InstrumentedAccessibilityService service =
+                mInstrumentedAccessibilityServiceRule.enableService();
         final MagnificationController controller2 = service.getMagnificationController();
-        try {
-            assertEquals("Magnification must reset when a service dies",
-                    1.0f, controller2.getScale());
-        } finally {
-            service.runOnServiceSync(() -> service.disableSelf());
-        }
+        assertEquals("Magnification must reset when a service dies",
+                1.0f, controller2.getScale(), 0f);
     }
 
+    @Test
     public void testGetMagnificationRegion_whenCanControlMagnification_shouldNotBeEmpty() {
         final MagnificationController controller = mService.getMagnificationController();
         Region magnificationRegion = controller.getMagnificationRegion();
@@ -164,42 +206,40 @@
                  + "magnification is being actively controlled", magnificationRegion.isEmpty());
     }
 
+    @Test
     public void testGetMagnificationRegion_whenCantControlMagnification_shouldBeEmpty() {
         mService.runOnServiceSync(() -> mService.disableSelf());
         mService = null;
-        InstrumentedAccessibilityService service = InstrumentedAccessibilityService.enableService(
-                mInstrumentation, InstrumentedAccessibilityService.class);
-        try {
-            final MagnificationController controller = service.getMagnificationController();
-            Region magnificationRegion = controller.getMagnificationRegion();
-            assertTrue("Magnification region should be empty when magnification "
-                    + "is not being actively controlled", magnificationRegion.isEmpty());
-        } finally {
-            service.runOnServiceSync(() -> service.disableSelf());
-        }
+        InstrumentedAccessibilityService service =
+                mInstrumentedAccessibilityServiceRule.enableService();
+        final MagnificationController controller = service.getMagnificationController();
+        Region magnificationRegion = controller.getMagnificationRegion();
+        assertTrue("Magnification region should be empty when magnification "
+                + "is not being actively controlled", magnificationRegion.isEmpty());
     }
 
+    @Test
     public void testGetMagnificationRegion_whenMagnificationGesturesEnabled_shouldNotBeEmpty() {
         ShellCommandBuilder.create(mInstrumentation)
                 .putSecureSetting(ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, "1")
                 .run();
         mService.runOnServiceSync(() -> mService.disableSelf());
         mService = null;
-        InstrumentedAccessibilityService service = InstrumentedAccessibilityService.enableService(
-                mInstrumentation, InstrumentedAccessibilityService.class);
+        InstrumentedAccessibilityService service =
+                mInstrumentedAccessibilityServiceRule.enableService();
         try {
             final MagnificationController controller = service.getMagnificationController();
             Region magnificationRegion = controller.getMagnificationRegion();
             assertFalse("Magnification region should not be empty when magnification "
                     + "gestures are active", magnificationRegion.isEmpty());
         } finally {
-            service.runOnServiceSync(() -> service.disableSelf());
             ShellCommandBuilder.create(mInstrumentation)
                     .deleteSecureSetting(ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED)
                     .run();
         }
     }
 
+    @Test
     public void testAnimatingMagnification() throws InterruptedException {
         final MagnificationController controller = mService.getMagnificationController();
         final int timeBetweenAnimationChanges = 100;
@@ -239,4 +279,121 @@
             Thread.sleep(timeBetweenAnimationChanges);
         }
     }
+
+    @Test
+    public void testA11yNodeInfoVisibility_whenOutOfMagnifiedArea_shouldVisible()
+            throws Exception{
+        final UiAutomation uiAutomation = mInstrumentation.getUiAutomation(
+                UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
+        final Activity activity = launchActivityAndWaitForItToBeOnscreen(
+                mInstrumentation, uiAutomation, mActivityRule);
+        final MagnificationController controller = mService.getMagnificationController();
+        final Rect magnifyBounds = controller.getMagnificationRegion().getBounds();
+        final float scale = 8.0f;
+        final Button button = activity.findViewById(R.id.button1);
+        adjustViewBoundsIfNeeded(button, scale, magnifyBounds);
+
+        final AccessibilityNodeInfo buttonNode = uiAutomation.getRootInActiveWindow()
+                .findAccessibilityNodeInfosByViewId(
+                        "android.accessibilityservice.cts:id/button1").get(0);
+        assertNotNull("Can't find button on the screen", buttonNode);
+        assertTrue("Button should be visible", buttonNode.isVisibleToUser());
+
+        // Get right-bottom center position
+        final float centerX = magnifyBounds.left + (((float) magnifyBounds.width() / (2.0f * scale))
+                * ((2.0f * scale) - 1.0f));
+        final float centerY = magnifyBounds.top + (((float) magnifyBounds.height() / (2.0f * scale))
+                * ((2.0f * scale) - 1.0f));
+        try {
+            waitOnMagnificationChanged(controller, scale, centerX, centerY);
+            // Waiting for UI refresh
+            mInstrumentation.waitForIdleSync();
+            buttonNode.refresh();
+
+            final Rect boundsInScreen = new Rect();
+            final DisplayMetrics displayMetrics = new DisplayMetrics();
+            buttonNode.getBoundsInScreen(boundsInScreen);
+            mInstrumentation.getContext().getDisplay().getMetrics(displayMetrics);
+            final Rect displayRect = new Rect(0, 0,
+                    displayMetrics.widthPixels, displayMetrics.heightPixels);
+            // The boundsInScreen of button is adjusted to outside of screen by framework,
+            // for example, Rect(-xxx, -xxx, -xxx, -xxx). Intersection of button and screen
+            // should be empty.
+            assertFalse("Button shouldn't be on the screen, screen is " + displayRect
+                            + ", button bounds is " + boundsInScreen,
+                    Rect.intersects(displayRect, boundsInScreen));
+            assertTrue("Button should be visible", buttonNode.isVisibleToUser());
+        } finally {
+            mService.runOnServiceSync(() -> controller.reset(false));
+        }
+    }
+
+    private void waitOnMagnificationChanged(MagnificationController controller, float newScale,
+            float newCenterX, float newCenterY) {
+        final Object waitLock = new Object();
+        final AtomicBoolean notified = new AtomicBoolean();
+        final OnMagnificationChangedListener listener = (c, region, scale, centerX, centerY) -> {
+            final float delta = 5.0f;
+            synchronized (waitLock) {
+                if (newScale == scale
+                        && (centerX > newCenterX - delta) && (centerY > newCenterY - delta)) {
+                    notified.set(true);
+                    waitLock.notifyAll();
+                }
+            }
+        };
+        controller.addListener(listener);
+        try {
+            final AtomicBoolean setScale = new AtomicBoolean();
+            final AtomicBoolean setCenter = new AtomicBoolean();
+            mService.runOnServiceSync(() -> {
+                setScale.set(controller.setScale(newScale, false));
+                setCenter.set(controller.setCenter(newCenterX, newCenterY, false));
+            });
+            assertTrue("Failed to set scale", setScale.get());
+            assertEquals("Failed to apply scale", newScale, controller.getScale(), 0f);
+            assertTrue("Failed to set center", setCenter.get());
+            waitOn(waitLock, () -> notified.get(), LISTENER_TIMEOUT_MILLIS,
+                    "waitOnMagnificationChanged");
+        } finally {
+            controller.removeListener(listener);
+        }
+    }
+
+    /**
+     * Adjust top-left view bounds if it's still in the magnified viewport after sets magnification
+     * scale and move centers to bottom-right.
+     */
+    private void adjustViewBoundsIfNeeded(View topLeftview, float scale, Rect magnifyBounds) {
+        final Point magnifyViewportTopLeft = new Point();
+        magnifyViewportTopLeft.x = (int)((scale - 1.0f) * ((float) magnifyBounds.width() / scale));
+        magnifyViewportTopLeft.y = (int)((scale - 1.0f) * ((float) magnifyBounds.height() / scale));
+        magnifyViewportTopLeft.offset(magnifyBounds.left, magnifyBounds.top);
+
+        final int[] viewLocation = new int[2];
+        topLeftview.getLocationOnScreen(viewLocation);
+        final Rect viewBounds = new Rect(viewLocation[0], viewLocation[1],
+                viewLocation[0] + topLeftview.getWidth(),
+                viewLocation[1] + topLeftview.getHeight());
+        if (viewBounds.right < magnifyViewportTopLeft.x
+                && viewBounds.bottom < magnifyViewportTopLeft.y) {
+            // no need
+            return;
+        }
+
+        final ViewGroup.LayoutParams layoutParams = topLeftview.getLayoutParams();
+        if (viewBounds.right >= magnifyViewportTopLeft.x) {
+            layoutParams.width = topLeftview.getWidth() - 1
+                    - (viewBounds.right - magnifyViewportTopLeft.x);
+            assertTrue("Needs to fix layout", layoutParams.width > 0);
+        }
+        if (viewBounds.bottom >= magnifyViewportTopLeft.y) {
+            layoutParams.height = topLeftview.getHeight() - 1
+                    - (viewBounds.bottom - magnifyViewportTopLeft.y);
+            assertTrue("Needs to fix layout", layoutParams.height > 0);
+        }
+        mInstrumentation.runOnMainSync(() -> topLeftview.setLayoutParams(layoutParams));
+        // Waiting for UI refresh
+        mInstrumentation.waitForIdleSync();
+    }
 }
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityOverlayTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityOverlayTest.java
index e461bdf..6f88779 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityOverlayTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityOverlayTest.java
@@ -16,14 +16,13 @@
 
 package android.accessibilityservice.cts;
 
-import static android.accessibilityservice.cts.utils.CtsTestUtils.runIfNotNull;
-
 import static org.junit.Assert.assertTrue;
 
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
 import android.accessibility.cts.common.InstrumentedAccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.cts.utils.AsyncUtils;
-import android.app.Instrumentation;
 import android.app.UiAutomation;
 import android.text.TextUtils;
 import android.view.WindowManager;
@@ -33,10 +32,11 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.BeforeClass;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 
 import java.util.List;
@@ -45,14 +45,24 @@
 @RunWith(AndroidJUnit4.class)
 public class AccessibilityOverlayTest {
 
-    private static Instrumentation sInstrumentation;
     private static UiAutomation sUiAutomation;
     InstrumentedAccessibilityService mService;
 
+    private InstrumentedAccessibilityServiceTestRule<StubAccessibilityButtonService>
+            mServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+                    StubAccessibilityButtonService.class);
+
+    private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    @Rule
+    public final RuleChain mRuleChain = RuleChain
+            .outerRule(mServiceRule)
+            .around(mDumpOnFailureRule);
+
     @BeforeClass
     public static void oneTimeSetUp() {
-        sInstrumentation = InstrumentationRegistry.getInstrumentation();
-        sUiAutomation = sInstrumentation
+        sUiAutomation = InstrumentationRegistry.getInstrumentation()
                 .getUiAutomation(UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
         AccessibilityServiceInfo info = sUiAutomation.getServiceInfo();
         info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
@@ -61,12 +71,7 @@
 
     @Before
     public void setUp() {
-        mService = StubAccessibilityButtonService.enableSelf(sInstrumentation);
-    }
-
-    @After
-    public void tearDown() {
-        runIfNotNull(mService, service -> service.runOnServiceSync(service::disableSelf));
+        mService = mServiceRule.getService();
     }
 
     @Test
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityPaneTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityPaneTest.java
index 959f315..d8b613f 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityPaneTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityPaneTest.java
@@ -28,6 +28,7 @@
 import static org.hamcrest.Matchers.both;
 import static org.junit.Assert.assertEquals;
 
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.accessibilityservice.cts.activities.AccessibilityEndToEndActivity;
 import android.app.Activity;
 import android.app.Instrumentation;
@@ -45,6 +46,7 @@
 import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 
 /**
@@ -58,10 +60,17 @@
     private Activity mActivity;
     private View mPaneView;
 
-    @Rule
-    public ActivityTestRule<AccessibilityEndToEndActivity> mActivityRule =
+    private ActivityTestRule<AccessibilityEndToEndActivity> mActivityRule =
             new ActivityTestRule<>(AccessibilityEndToEndActivity.class, false, false);
 
+    private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    @Rule
+    public final RuleChain mRuleChain = RuleChain
+            .outerRule(mActivityRule)
+            .around(mDumpOnFailureRule);
+
     @BeforeClass
     public static void oneTimeSetup() throws Exception {
         sInstrumentation = InstrumentationRegistry.getInstrumentation();
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityServiceInfoTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityServiceInfoTest.java
index 67626fe..01d1659 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityServiceInfoTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityServiceInfoTest.java
@@ -16,20 +16,35 @@
 
 package android.accessibilityservice.cts;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.accessibility.AccessibilityEvent;
 
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 /**
  * Class for testing {@link AccessibilityServiceInfo}.
  */
 @Presubmit
-public class AccessibilityServiceInfoTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityServiceInfoTest {
+
+    @Rule
+    public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
 
     @MediumTest
+    @Test
     public void testMarshalling() throws Exception {
 
         // fully populate the service info to marshal
@@ -51,6 +66,7 @@
      * Tests whether the service info describes its contents consistently.
      */
     @MediumTest
+    @Test
     public void testDescribeContents() {
         AccessibilityServiceInfo info = new AccessibilityServiceInfo();
         assertSame("Accessibility service info always return 0 for this method.", 0,
@@ -64,6 +80,7 @@
      * Tests whether a feedback type is correctly transformed to a string.
      */
     @MediumTest
+    @Test
     public void testFeedbackTypeToString() {
         assertEquals("[FEEDBACK_AUDIBLE]", AccessibilityServiceInfo.feedbackTypeToString(
                 AccessibilityServiceInfo.FEEDBACK_AUDIBLE));
@@ -87,6 +104,7 @@
      * Tests whether a flag is correctly transformed to a string.
      */
     @MediumTest
+    @Test
     public void testFlagToString() {
         assertEquals("DEFAULT", AccessibilityServiceInfo.flagToString(
                 AccessibilityServiceInfo.DEFAULT));
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySettingsTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySettingsTest.java
index f01251a..3199dc6 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySettingsTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySettingsTest.java
@@ -16,15 +16,25 @@
 
 package android.accessibilityservice.cts;
 
+import static androidx.test.InstrumentationRegistry.getContext;
+
+import static org.junit.Assert.assertTrue;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.platform.test.annotations.AppModeFull;
 import android.platform.test.annotations.Presubmit;
 import android.provider.Settings;
-import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.MediumTest;
 
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.util.List;
 
 /**
@@ -32,12 +42,18 @@
  * accessibility settings has an activity that handles it.
  */
 @Presubmit
-public class AccessibilitySettingsTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilitySettingsTest {
+
+    @Rule
+    public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
 
     @MediumTest
     @AppModeFull
+    @Test
     public void testAccessibilitySettingsIntentHandled() throws Throwable {
-        PackageManager packageManager = mContext.getPackageManager();
+        PackageManager packageManager = getContext().getPackageManager();
         Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
         List<ResolveInfo> resolvedActivities = packageManager.queryIntentActivities(intent,
                 PackageManager.MATCH_DEFAULT_ONLY);
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
index 8bb1bfb..0afa1fd 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
@@ -13,30 +13,32 @@
  */
 
 package android.accessibilityservice.cts;
+
+import static android.accessibility.cts.common.InstrumentedAccessibilityService.enableService;
 import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
 import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
 import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD;
-import static android.accessibilityservice.cts.utils.CtsTestUtils.runIfNotNull;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
 import android.accessibility.cts.common.InstrumentedAccessibilityService;
 import android.accessibilityservice.AccessibilityService.SoftKeyboardController;
 import android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener;
 import android.accessibilityservice.cts.activities.AccessibilityTestActivity;
 import android.accessibilityservice.cts.utils.AsyncUtils;
-import android.app.Instrumentation;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.platform.test.annotations.AppModeFull;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
-import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 
 /**
@@ -47,7 +49,6 @@
 public class AccessibilitySoftKeyboardModesTest {
     private int mLastCallbackValue;
 
-    private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
     private InstrumentedAccessibilityService mService;
     private final Object mLock = new Object();
     private final OnShowModeChangedListener mListener = (c, showMode) -> {
@@ -57,17 +58,21 @@
         }
     };
 
+    private InstrumentedAccessibilityServiceTestRule<InstrumentedAccessibilityService>
+            mServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+                    InstrumentedAccessibilityService.class);
 
+    private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    @Rule
+    public final RuleChain mRuleChain = RuleChain
+            .outerRule(mServiceRule)
+            .around(mDumpOnFailureRule);
 
     @Before
-    public void setUp() throws Exception {
-        mService = InstrumentedAccessibilityService.enableService(mInstrumentation,
-                InstrumentedAccessibilityService.class);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        runIfNotNull(mService, service -> service.runOnServiceSync(service::disableSelf));
+    public void setUp() {
+        mService = mServiceRule.getService();
     }
 
     @Test
@@ -95,7 +100,7 @@
         assertEquals(SHOW_MODE_AUTO, controller.getShowMode());
 
         final InstrumentedAccessibilityService secondService =
-                StubAccessibilityButtonService.enableSelf(mInstrumentation);
+                enableService(StubAccessibilityButtonService.class);
         try {
             // Listen on the first service
             controller.addOnShowModeChangedListener(mListener);
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java
index e89b4dd..0ca3c31 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java
@@ -30,7 +30,7 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import android.accessibilityservice.cts.R;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.accessibilityservice.cts.activities.AccessibilityTextTraversalActivity;
 import android.app.Instrumentation;
 import android.app.UiAutomation;
@@ -60,6 +60,7 @@
 import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 
 import java.util.Arrays;
@@ -80,10 +81,17 @@
 
     private AccessibilityTextTraversalActivity mActivity;
 
-    @Rule
-    public ActivityTestRule<AccessibilityTextTraversalActivity> mActivityRule =
+    private ActivityTestRule<AccessibilityTextTraversalActivity> mActivityRule =
             new ActivityTestRule<>(AccessibilityTextTraversalActivity.class, false, false);
 
+    private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    @Rule
+    public final RuleChain mRuleChain = RuleChain
+            .outerRule(mActivityRule)
+            .around(mDumpOnFailureRule);
+
     @BeforeClass
     public static void oneTimeSetup() throws Exception {
         sInstrumentation = InstrumentationRegistry.getInstrumentation();
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java
index 75eda1d..17acc3b 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java
@@ -23,7 +23,7 @@
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
-import android.accessibilityservice.cts.R;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.accessibilityservice.cts.activities.AccessibilityTextTraversalActivity;
 import android.app.Instrumentation;
 import android.app.UiAutomation;
@@ -47,6 +47,7 @@
 import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 
 /**
@@ -64,10 +65,17 @@
 
     private AccessibilityTextTraversalActivity mActivity;
 
-    @Rule
-    public ActivityTestRule<AccessibilityTextTraversalActivity> mActivityRule =
+    private ActivityTestRule<AccessibilityTextTraversalActivity> mActivityRule =
             new ActivityTestRule<>(AccessibilityTextTraversalActivity.class, false, false);
 
+    private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    @Rule
+    public final RuleChain mRuleChain = RuleChain
+            .outerRule(mActivityRule)
+            .around(mDumpOnFailureRule);
+
     @BeforeClass
     public static void oneTimeSetup() throws Exception {
         sInstrumentation = InstrumentationRegistry.getInstrumentation();
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityViewTreeReportingTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityViewTreeReportingTest.java
index 28a3acd..0ca307a 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityViewTreeReportingTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityViewTreeReportingTest.java
@@ -14,8 +14,7 @@
 
 package android.accessibilityservice.cts;
 
-import static android.accessibilityservice.cts.utils.ActivityLaunchUtils
-        .launchActivityAndWaitForItToBeOnscreen;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -23,15 +22,12 @@
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.accessibilityservice.AccessibilityServiceInfo;
-import android.accessibilityservice.cts.R;
 import android.accessibilityservice.cts.activities.AccessibilityViewTreeReportingActivity;
 import android.app.Instrumentation;
 import android.app.UiAutomation;
 import android.content.Context;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 import android.text.TextUtils;
 import android.view.View;
 import android.view.ViewGroup;
@@ -40,11 +36,16 @@
 import android.widget.Button;
 import android.widget.LinearLayout;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 
 /**
@@ -61,10 +62,17 @@
 
     private AccessibilityViewTreeReportingActivity mActivity;
 
-    @Rule
-    public ActivityTestRule<AccessibilityViewTreeReportingActivity> mActivityRule =
+    private ActivityTestRule<AccessibilityViewTreeReportingActivity> mActivityRule =
             new ActivityTestRule<>(AccessibilityViewTreeReportingActivity.class, false, false);
 
+    private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    @Rule
+    public final RuleChain mRuleChain = RuleChain
+            .outerRule(mActivityRule)
+            .around(mDumpOnFailureRule);
+
     @BeforeClass
     public static void oneTimeSetup() throws Exception {
         sInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -309,6 +317,44 @@
         assertTrue(awaitedEvent.getSource().isImportantForAccessibility());
     }
 
+
+    @Test
+    public void testHideView_receiveSubtreeEvent() throws Throwable {
+        final View view = mActivity.findViewById(R.id.secondButton);
+        AccessibilityEvent awaitedEvent =
+                sUiAutomation.executeAndWaitForEvent(
+                        () -> mActivity.runOnUiThread(() -> view.setVisibility(View.GONE)),
+                        (event) -> {
+                            boolean isContentChanged = event.getEventType()
+                                    == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
+                            int isSubTree = (event.getContentChangeTypes()
+                                    & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
+                            boolean isFromThisPackage = TextUtils.equals(event.getPackageName(),
+                                    mActivity.getPackageName());
+                            return isContentChanged && (isSubTree != 0) && isFromThisPackage;
+                        }, TIMEOUT_ASYNC_PROCESSING);
+        awaitedEvent.recycle();
+    }
+
+    @Test
+    public void testUnhideView_receiveSubtreeEvent() throws Throwable {
+        final View view = mActivity.findViewById(R.id.hiddenButton);
+        AccessibilityEvent awaitedEvent =
+                sUiAutomation.executeAndWaitForEvent(
+                        () -> mActivity.runOnUiThread(() -> view.setVisibility(View.VISIBLE)),
+                        (event) -> {
+                            boolean isContentChanged = event.getEventType()
+                                    == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
+                            int isSubTree = (event.getContentChangeTypes()
+                                    & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
+                            boolean isFromThisPackage = TextUtils.equals(event.getPackageName(),
+                                    mActivity.getPackageName());
+                            return isContentChanged && (isSubTree != 0) && isFromThisPackage;
+                        }, TIMEOUT_ASYNC_PROCESSING);
+        awaitedEvent.recycle();
+    }
+
+
     private void setGetNonImportantViews(boolean getNonImportantViews) {
         AccessibilityServiceInfo serviceInfo = sUiAutomation.getServiceInfo();
         serviceInfo.flags &= ~AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
index 16e0b68..3cf9c49 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
@@ -17,7 +17,9 @@
 
 import static org.junit.Assert.assertEquals;
 
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
 import android.app.Instrumentation;
 import android.content.pm.PackageManager;
 import android.media.AudioManager;
@@ -28,7 +30,9 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 
 /**
@@ -43,6 +47,18 @@
     // If a11y volume is stuck at a single value, don't run the tests
     boolean mFixedA11yVolume;
 
+    private InstrumentedAccessibilityServiceTestRule<InstrumentedAccessibilityService>
+            mServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+                    InstrumentedAccessibilityService.class, false);
+
+    private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    @Rule
+    public final RuleChain mRuleChain = RuleChain
+            .outerRule(mServiceRule)
+            .around(mDumpOnFailureRule);
+
     @Before
     public void setUp() {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -83,20 +99,13 @@
         final int MIN = mAudioManager.getStreamMinVolume(AudioManager.STREAM_ACCESSIBILITY);
         final int MAX = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_ACCESSIBILITY);
         final int otherVolume = (startingVolume == MIN) ? MAX : MIN;
-        final InstrumentedAccessibilityService service = InstrumentedAccessibilityService
-                .enableService(mInstrumentation, InstrumentedAccessibilityService.class);
-        try {
-            service.runOnServiceSync(() ->
-                    mAudioManager.setStreamVolume(AudioManager.STREAM_ACCESSIBILITY, otherVolume,
-                            0));
-            assertEquals("Accessibility service should be able to change accessibility volume",
-                    otherVolume, mAudioManager.getStreamVolume(AudioManager.STREAM_ACCESSIBILITY));
-            service.runOnServiceSync(() -> mAudioManager.setStreamVolume(
-                    AudioManager.STREAM_ACCESSIBILITY, startingVolume, 0));
-        } finally {
-            if (service != null) {
-                service.runOnServiceSync(() -> service.disableSelf());
-            }
-        }
+        final InstrumentedAccessibilityService service = mServiceRule.enableService();
+
+        service.runOnServiceSync(() -> mAudioManager.setStreamVolume(
+                AudioManager.STREAM_ACCESSIBILITY, otherVolume, 0));
+        assertEquals("Accessibility service should be able to change accessibility volume",
+                otherVolume, mAudioManager.getStreamVolume(AudioManager.STREAM_ACCESSIBILITY));
+        service.runOnServiceSync(() -> mAudioManager.setStreamVolume(
+                AudioManager.STREAM_ACCESSIBILITY, startingVolume, 0));
     }
 }
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
old mode 100755
new mode 100644
index f1fca9f..c33b945
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
@@ -48,9 +48,9 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
-import android.accessibilityservice.cts.activities.AccessibilityEndToEndActivity;
 import android.accessibilityservice.cts.activities.AccessibilityWindowQueryActivity;
 import android.app.ActivityTaskManager;
 import android.app.Instrumentation;
@@ -81,6 +81,7 @@
 import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
@@ -109,13 +110,16 @@
 
     private AccessibilityWindowQueryActivity mActivity;
 
-    @Rule
-    public ActivityTestRule<AccessibilityWindowQueryActivity> mActivityRule =
+    private ActivityTestRule<AccessibilityWindowQueryActivity> mActivityRule =
             new ActivityTestRule<>(AccessibilityWindowQueryActivity.class, false, false);
 
+    private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
     @Rule
-    public ActivityTestRule<AccessibilityEndToEndActivity> mEndToEndActivityRule =
-            new ActivityTestRule<>(AccessibilityEndToEndActivity.class, false, false);
+    public final RuleChain mRuleChain = RuleChain
+            .outerRule(mActivityRule)
+            .around(mDumpOnFailureRule);
 
     @BeforeClass
     public static void oneTimeSetup() throws Exception {
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java
index 71c34c2..25e6d1f 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java
@@ -39,6 +39,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.cts.activities.AccessibilityWindowReportingActivity;
 import android.app.Activity;
@@ -62,6 +63,7 @@
 import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 
 import java.util.List;
@@ -82,10 +84,17 @@
     private Activity mActivity;
     private CharSequence mActivityTitle;
 
-    @Rule
-    public ActivityTestRule<AccessibilityWindowReportingActivity> mActivityRule =
+    private ActivityTestRule<AccessibilityWindowReportingActivity> mActivityRule =
             new ActivityTestRule<>(AccessibilityWindowReportingActivity.class, false, false);
 
+    private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    @Rule
+    public final RuleChain mRuleChain = RuleChain
+            .outerRule(mActivityRule)
+            .around(mDumpOnFailureRule);
+
     @BeforeClass
     public static void oneTimeSetup() throws Exception {
         sInstrumentation = InstrumentationRegistry.getInstrumentation();
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDescriptionTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDescriptionTest.java
index 92821b8..346c62e 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDescriptionTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDescriptionTest.java
@@ -14,30 +14,48 @@
 
 package android.accessibilityservice.cts;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.accessibilityservice.GestureDescription;
 import android.accessibilityservice.GestureDescription.StrokeDescription;
 import android.graphics.Path;
 import android.graphics.PathMeasure;
 import android.platform.test.annotations.AppModeFull;
 import android.platform.test.annotations.Presubmit;
-import android.test.InstrumentationTestCase;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Tests for creating gesture descriptions.
  */
 @Presubmit
 @AppModeFull
-public class GestureDescriptionTest extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class GestureDescriptionTest {
     static final int NOMINAL_PATH_DURATION = 100;
     private Path mNominalPath;
 
-    @Override
+    @Rule
+    public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    @Before
     public void setUp() {
         mNominalPath = new Path();
         mNominalPath.moveTo(0, 0);
         mNominalPath.lineTo(10, 10);
     }
 
+    @Test
     public void testCreateStroke_noDuration_shouldThrow() {
         try {
             new StrokeDescription(mNominalPath, 0, 0);
@@ -46,6 +64,7 @@
         }
     }
 
+    @Test
     public void testCreateStroke_negativeStartTime_shouldThrow() {
         try {
             new StrokeDescription(mNominalPath, -1, NOMINAL_PATH_DURATION);
@@ -54,6 +73,7 @@
         }
     }
 
+    @Test
     public void testCreateStroke_negativeStartX_shouldThrow() {
         Path negativeStartXPath = new Path();
         negativeStartXPath.moveTo(-1, 0);
@@ -65,6 +85,7 @@
         }
     }
 
+    @Test
     public void testCreateStroke_negativeStartY_shouldThrow() {
         Path negativeStartYPath = new Path();
         negativeStartYPath.moveTo(0, -1);
@@ -76,6 +97,7 @@
         }
     }
 
+    @Test
     public void testCreateStroke_negativeEndX_shouldThrow() {
         Path negativeEndXPath = new Path();
         negativeEndXPath.moveTo(0, 0);
@@ -87,6 +109,7 @@
         }
     }
 
+    @Test
     public void testCreateStroke_negativeEndY_shouldThrow() {
         Path negativeEndYPath = new Path();
         negativeEndYPath.moveTo(0, 0);
@@ -98,6 +121,7 @@
         }
     }
 
+    @Test
     public void testCreateStroke_withEmptyPath_shouldThrow() {
         Path emptyPath = new Path();
         try {
@@ -107,6 +131,7 @@
         }
     }
 
+    @Test
     public void testCreateStroke_pathWithMultipleContours_shouldThrow() {
         Path multiContourPath = new Path();
         multiContourPath.moveTo(0, 0);
@@ -120,6 +145,7 @@
         }
     }
 
+    @Test
     public void testStrokeDescriptionWillContinue() {
         StrokeDescription strokeDescription = new StrokeDescription(mNominalPath, 0, 100);
         assertFalse(strokeDescription.willContinue());
@@ -138,6 +164,7 @@
         assertTrue(continuation.willContinue());
     }
 
+    @Test
     public void testAddStroke_allowUpToMaxPaths() {
         GestureDescription.Builder gestureBuilder = new GestureDescription.Builder();
         for (int i = 0; i < GestureDescription.getMaxStrokeCount(); i++) {
@@ -156,6 +183,7 @@
         }
     }
 
+    @Test
     public void testAddStroke_withDurationTooLong_shouldThrow() {
         Path path = new Path();
         path.moveTo(10, 10);
@@ -169,6 +197,7 @@
         }
     }
 
+    @Test
     public void testEmptyDescription_shouldThrow() {
         GestureDescription.Builder gestureBuilder = new GestureDescription.Builder();
         try {
@@ -178,6 +207,7 @@
         }
     }
 
+    @Test
     public void testStrokeDescriptionGetters_workAsExpected() {
         int x = 100;
         int startY = 100;
@@ -201,4 +231,18 @@
         PathMeasure measure = new PathMeasure(returnedPath, false);
         assertEquals(50, (int) measure.getLength());
     }
+
+    @Test
+    public void testSetDisplayId_getCorrectDisplayId() {
+        Path path = new Path();
+        path.moveTo(100, 100);
+        StrokeDescription stroke = new StrokeDescription(path, 150, 100);
+        GestureDescription.Builder builder = new GestureDescription.Builder();
+        builder.addStroke(stroke);
+        builder.setDisplayId(2);
+
+        GestureDescription gesture = builder.build();
+
+        assertEquals(2, gesture.getDisplayId());
+    }
 }
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDetectionStubAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDetectionStubAccessibilityService.java
index 042904b..1bc62f6 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDetectionStubAccessibilityService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDetectionStubAccessibilityService.java
@@ -14,32 +14,39 @@
 
 package android.accessibilityservice.cts;
 
-import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import static org.junit.Assert.fail;
 
-import android.app.Instrumentation;
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.accessibilityservice.AccessibilityGestureInfo;
 import android.view.accessibility.AccessibilityEvent;
+
 import java.util.ArrayList;
+import java.util.List;
 
 /** Accessibility service stub, which will collect recognized gestures. */
 public class GestureDetectionStubAccessibilityService extends InstrumentedAccessibilityService {
     private static final long GESTURE_RECOGNIZE_TIMEOUT_MS = 3000;
-    private static final long EVENT_RECOGNIZE_TIMEOUT_MS = 3000;
+    private static final long EVENT_RECOGNIZE_TIMEOUT_MS = 5000;
     // Member variables
-    private final Object mLock = new Object();
+    protected final Object mLock = new Object();
     private ArrayList<Integer> mCollectedGestures = new ArrayList();
-    private ArrayList<Integer> mCollectedEvents = new ArrayList();
-
-    public static GestureDetectionStubAccessibilityService enableSelf(
-            Instrumentation instrumentation) {
-        return InstrumentedAccessibilityService.enableService(
-                instrumentation, GestureDetectionStubAccessibilityService.class);
-    }
+    private ArrayList<AccessibilityGestureInfo> mCollectedGestureInfos = new ArrayList();
+    protected ArrayList<Integer> mCollectedEvents = new ArrayList();
 
     @Override
     protected boolean onGesture(int gestureId) {
         synchronized (mCollectedGestures) {
             mCollectedGestures.add(gestureId);
-            mCollectedGestures.notifyAll(); // Stop waiting for gesture.
+        }
+        return true;
+    }
+
+    @Override
+    public boolean onGesture(AccessibilityGestureInfo gestureInfo) {
+        super.onGesture(gestureInfo);
+        synchronized (mCollectedGestureInfos) {
+            mCollectedGestureInfos.add(gestureInfo);
+            mCollectedGestureInfos.notifyAll(); // Stop waiting for gesture.
         }
         return true;
     }
@@ -48,6 +55,9 @@
         synchronized (mCollectedGestures) {
             mCollectedGestures.clear();
         }
+        synchronized (mCollectedGestureInfos) {
+            mCollectedGestureInfos.clear();
+        }
     }
 
     public int getGesturesSize() {
@@ -62,20 +72,33 @@
         }
     }
 
-    /** Wait for onGesture() to collect next gesture. */
-    public void waitUntilGesture() {
-        synchronized (mCollectedGestures) {
-            if (mCollectedGestures.size() > 0) {
+    /** Waits for {@link #onGesture(AccessibilityGestureInfo)} to collect next gesture. */
+    public void waitUntilGestureInfo() {
+        synchronized (mCollectedGestureInfos) {
+            //Assume the size of mCollectedGestures is changed before mCollectedGestureInfos.
+            if (mCollectedGestureInfos.size() > 0) {
                 return;
             }
             try {
-                mCollectedGestures.wait(GESTURE_RECOGNIZE_TIMEOUT_MS);
+                mCollectedGestureInfos.wait(GESTURE_RECOGNIZE_TIMEOUT_MS);
             } catch (InterruptedException e) {
                 throw new RuntimeException(e);
             }
         }
     }
 
+    public int getGestureInfoSize() {
+        synchronized (mCollectedGestureInfos) {
+            return mCollectedGestureInfos.size();
+        }
+    }
+
+    public AccessibilityGestureInfo getGestureInfo(int index) {
+        synchronized (mCollectedGestureInfos) {
+            return mCollectedGestureInfos.get(index);
+        }
+    }
+
     @Override
     public void onAccessibilityEvent(AccessibilityEvent event) {
         synchronized (mLock) {
@@ -124,4 +147,37 @@
             }
         }
     }
+
+    /** Insure that the specified accessibility events have been received. */
+    public void assertPropagated(int... events) {
+        waitUntilEvent(events.length);
+        // Set up readable error reporting.
+        List<String> received = new ArrayList<>();
+        List<String> expected = new ArrayList<>();
+        for (int event : events) {
+            expected.add(AccessibilityEvent.eventTypeToString(event));
+        }
+        for (int i = 0; i < getEventsSize(); ++i) {
+            received.add(AccessibilityEvent.eventTypeToString(getEvent(i)));
+        }
+
+        if (events.length != getEventsSize()) {
+            String message =
+                    String.format(
+                            "Received %d events when expecting %d. Received %s, expected %s",
+                            received.size(),
+                            expected.size(),
+                            received.toString(),
+                            expected.toString());
+            fail(message);
+        }
+        else if (!expected.equals(received)) {
+            String message =
+                    String.format(
+                            "Received %s, expected %s",
+                            received.toString(),
+                            expected.toString());
+            fail(message);
+        }
+    }
 }
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java
index 72bbd97..8393012 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java
@@ -18,11 +18,11 @@
 
 import static android.accessibilityservice.cts.utils.AsyncUtils.await;
 import static android.accessibilityservice.cts.utils.AsyncUtils.waitOn;
-import static android.accessibilityservice.cts.utils.CtsTestUtils.runIfNotNull;
 import static android.accessibilityservice.cts.utils.GestureUtils.add;
 import static android.accessibilityservice.cts.utils.GestureUtils.click;
 import static android.accessibilityservice.cts.utils.GestureUtils.dispatchGesture;
 import static android.accessibilityservice.cts.utils.GestureUtils.distance;
+import static android.accessibilityservice.cts.utils.GestureUtils.doubleTap;
 import static android.accessibilityservice.cts.utils.GestureUtils.drag;
 import static android.accessibilityservice.cts.utils.GestureUtils.endTimeOf;
 import static android.accessibilityservice.cts.utils.GestureUtils.lastPointOf;
@@ -31,21 +31,17 @@
 import static android.accessibilityservice.cts.utils.GestureUtils.pointerUp;
 import static android.accessibilityservice.cts.utils.GestureUtils.startingAt;
 import static android.accessibilityservice.cts.utils.GestureUtils.swipe;
+import static android.accessibilityservice.cts.utils.GestureUtils.tripleTap;
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_MOVE;
 import static android.view.MotionEvent.ACTION_UP;
 
-import static org.hamcrest.Matchers.empty;
-import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
-import static java.util.concurrent.TimeUnit.SECONDS;
-
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
 import android.accessibilityservice.GestureDescription;
 import android.accessibilityservice.GestureDescription.StrokeDescription;
 import android.accessibilityservice.cts.AccessibilityGestureDispatchTest.GestureDispatchActivity;
@@ -53,10 +49,8 @@
 import android.app.Instrumentation;
 import android.content.pm.PackageManager;
 import android.graphics.PointF;
-import android.os.SystemClock;
 import android.platform.test.annotations.AppModeFull;
 import android.provider.Settings;
-import android.view.MotionEvent;
 import android.widget.TextView;
 
 import androidx.test.InstrumentationRegistry;
@@ -67,12 +61,9 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-
 /**
  * Class for testing magnification.
  */
@@ -95,10 +86,22 @@
 
     private final Object mZoomLock = new Object();
 
-    @Rule
-    public ActivityTestRule<GestureDispatchActivity> mActivityRule =
+    private ActivityTestRule<GestureDispatchActivity> mActivityRule =
             new ActivityTestRule<>(GestureDispatchActivity.class);
 
+    private InstrumentedAccessibilityServiceTestRule<StubMagnificationAccessibilityService>
+            mServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+                    StubMagnificationAccessibilityService.class, false);
+
+    private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    @Rule
+    public final RuleChain mRuleChain = RuleChain
+            .outerRule(mActivityRule)
+            .around(mServiceRule)
+            .around(mDumpOnFailureRule);
+
     @Before
     public void setUp() throws Exception {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -113,7 +116,7 @@
                         Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0) == 1;
         setMagnificationEnabled(true);
 
-        mService = StubMagnificationAccessibilityService.enableSelf(mInstrumentation);
+        mService = mServiceRule.enableService();
         mService.getMagnificationController().addListener(
                 (controller, region, scale, centerX, centerY) -> {
                     mCurrentScale = scale;
@@ -140,8 +143,6 @@
         if (!mHasTouchscreen) return;
 
         setMagnificationEnabled(mOriginalIsMagnificationEnabled);
-
-        runIfNotNull(mService, service -> service.runOnServiceSync(service::disableSelfAndRemove));
     }
 
     @Test
@@ -196,9 +197,9 @@
 
     private void setZoomByTripleTapping(boolean desiredZoomState) {
         if (isZoomed() == desiredZoomState) return;
-        dispatch(tripleTap());
+        dispatch(tripleTap(mTapLocation));
         waitOn(mZoomLock, () -> isZoomed() == desiredZoomState);
-        assertNoTouchInputPropagated();
+        mTouchListener.assertNonePropagated();
     }
 
     private void tripleTapAndDragViewport() {
@@ -210,10 +211,10 @@
         dispatch(drag);
         waitOn(mZoomLock, () -> distance(mCurrentZoomCenter, oldCenter) >= mPan / 5);
         assertTrue(isZoomed());
-        assertNoTouchInputPropagated();
+        mTouchListener.assertNonePropagated();
 
         dispatch(pointerUp(drag));
-        assertNoTouchInputPropagated();
+        mTouchListener.assertNonePropagated();
     }
 
     private StrokeDescription tripleTapAndHold() {
@@ -227,22 +228,18 @@
 
     private void assertGesturesPropagateToView() {
         dispatch(click(mTapLocation));
-        assertPropagated(ACTION_DOWN, ACTION_UP);
+        mTouchListener.assertPropagated(ACTION_DOWN, ACTION_UP);
 
         dispatch(longClick(mTapLocation));
-        assertPropagated(ACTION_DOWN, ACTION_UP);
+        mTouchListener.assertPropagated(ACTION_DOWN, ACTION_UP);
 
-        dispatch(doubleTap());
-        assertPropagated(ACTION_DOWN, ACTION_UP, ACTION_DOWN, ACTION_UP);
+        dispatch(doubleTap(mTapLocation));
+        mTouchListener.assertPropagated(ACTION_DOWN, ACTION_UP, ACTION_DOWN, ACTION_UP);
 
         dispatch(swipe(
                 mTapLocation,
                 add(mTapLocation, 0, 29)));
-        assertPropagated(ACTION_DOWN, ACTION_MOVE, ACTION_UP);
-    }
-
-    private void assertNoTouchInputPropagated() {
-        assertThat(prettyPrintable(mTouchListener.events), is(empty()));
+        mTouchListener.assertPropagated(ACTION_DOWN, ACTION_MOVE, ACTION_UP);
     }
 
     private void setMagnificationEnabled(boolean enabled) {
@@ -254,50 +251,6 @@
         return mCurrentScale >= MIN_SCALE;
     }
 
-    private void assertPropagated(int... eventTypes) {
-        MotionEvent ev;
-        try {
-            while (true) {
-                if (eventTypes.length == 0) return;
-                int expectedEventType = eventTypes[0];
-                long startedPollingAt = SystemClock.uptimeMillis();
-                ev = mTouchListener.events.poll(5, SECONDS);
-                assertNotNull("Expected "
-                        + MotionEvent.actionToString(expectedEventType)
-                        + " but none present after "
-                        + (SystemClock.uptimeMillis() - startedPollingAt) + "ms",
-                        ev);
-                int action = ev.getActionMasked();
-                if (action == expectedEventType) {
-                    eventTypes = Arrays.copyOfRange(eventTypes, 1, eventTypes.length);
-                } else {
-                    if (action != ACTION_MOVE) fail("Unexpected event: " + ev);
-                }
-            }
-        } catch (InterruptedException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    private GestureDescription doubleTap() {
-        return multiTap(2);
-    }
-
-    private GestureDescription tripleTap() {
-        return multiTap(3);
-    }
-
-    private GestureDescription multiTap(int taps) {
-        GestureDescription.Builder builder = new GestureDescription.Builder();
-        long time = 0;
-        for (int i = 0; i < taps; i++) {
-            StrokeDescription stroke = click(mTapLocation);
-            builder.addStroke(startingAt(time, stroke));
-            time += stroke.getDuration() + 20;
-        }
-        return builder.build();
-    }
-
     public void dispatch(StrokeDescription firstStroke, StrokeDescription... rest) {
         GestureDescription.Builder builder =
                 new GestureDescription.Builder().addStroke(firstStroke);
@@ -310,17 +263,4 @@
     public void dispatch(GestureDescription gesture) {
         await(dispatchGesture(mService, gesture));
     }
-
-    private static <T> Collection<T> prettyPrintable(Collection<T> c) {
-        return new ArrayList<T>(c) {
-
-            @Override
-            public String toString() {
-                return stream()
-                        .map(t -> "\n" + t)
-                        .reduce(String::concat)
-                        .orElse("");
-            }
-        };
-    }
 }
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubAccessibilityButtonService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubAccessibilityButtonService.java
index c0ce5b6..5223f22 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubAccessibilityButtonService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubAccessibilityButtonService.java
@@ -15,15 +15,9 @@
 package android.accessibilityservice.cts;
 
 import android.accessibility.cts.common.InstrumentedAccessibilityService;
-import android.app.Instrumentation;
-import android.app.UiAutomation;
 
 /**
  * A stub accessibility service to install for testing accessibility button APIs
  */
 public class StubAccessibilityButtonService extends InstrumentedAccessibilityService {
-    public static StubAccessibilityButtonService enableSelf(Instrumentation instrumentation) {
-        return InstrumentedAccessibilityService.enableService(
-                instrumentation, StubAccessibilityButtonService.class);
-    }
 }
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubFingerprintGestureService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubFingerprintGestureService.java
index 87539f8..bf78b48 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubFingerprintGestureService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubFingerprintGestureService.java
@@ -15,14 +15,9 @@
 package android.accessibilityservice.cts;
 
 import android.accessibility.cts.common.InstrumentedAccessibilityService;
-import android.app.Instrumentation;
 
 /**
  * A stub accessibility service to install for testing gesture dispatch
  */
 public class StubFingerprintGestureService extends InstrumentedAccessibilityService {
-    public static StubFingerprintGestureService enableSelf(Instrumentation instrumentation) {
-        return InstrumentedAccessibilityService.enableService(
-                instrumentation, StubFingerprintGestureService.class);
-    }
 }
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubGestureAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubGestureAccessibilityService.java
index 68c4e7b..eb90389 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubGestureAccessibilityService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubGestureAccessibilityService.java
@@ -15,22 +15,9 @@
 package android.accessibilityservice.cts;
 
 import android.accessibility.cts.common.InstrumentedAccessibilityService;
-import android.accessibilityservice.GestureDescription;
-import android.app.Instrumentation;
-import android.os.Handler;
 
 /**
  * A stub accessibility service to install for testing gesture dispatch
  */
 public class StubGestureAccessibilityService extends InstrumentedAccessibilityService {
-
-    public boolean doDispatchGesture(GestureDescription description, GestureResultCallback callback,
-            Handler handler) {
-        return dispatchGesture(description, callback, handler);
-    }
-
-    public static StubGestureAccessibilityService enableSelf(Instrumentation instrumentation) {
-        return InstrumentedAccessibilityService.enableService(
-                instrumentation, StubGestureAccessibilityService.class);
-    }
 }
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubMagnificationAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubMagnificationAccessibilityService.java
index 195ce89..2ce0e37 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubMagnificationAccessibilityService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubMagnificationAccessibilityService.java
@@ -15,16 +15,9 @@
 package android.accessibilityservice.cts;
 
 import android.accessibility.cts.common.InstrumentedAccessibilityService;
-import android.app.Instrumentation;
 
 /**
  * A stub accessibility service to install for testing gesture dispatch
  */
 public class StubMagnificationAccessibilityService extends InstrumentedAccessibilityService {
-
-    public static StubMagnificationAccessibilityService enableSelf(
-            Instrumentation instrumentation) {
-        return InstrumentedAccessibilityService.enableService(
-                instrumentation, StubMagnificationAccessibilityService.class);
-    }
 }
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorationStubAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorationStubAccessibilityService.java
new file mode 100644
index 0000000..9db2478
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorationStubAccessibilityService.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * <p>http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * <p>Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT 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.accessibilityservice.cts;
+
+import static android.view.accessibility.AccessibilityEvent.TYPE_GESTURE_DETECTION_END;
+import static android.view.accessibility.AccessibilityEvent.TYPE_GESTURE_DETECTION_START;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_CLICKED;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_LONG_CLICKED;
+
+import android.view.accessibility.AccessibilityEvent;
+
+/**
+ * This accessibility service stub collects all events relating to touch exploration rather than
+ * just the few collected by GestureDetectionStubAccessibilityService
+ */
+public class TouchExplorationStubAccessibilityService
+        extends GestureDetectionStubAccessibilityService {
+    @Override
+    public void onAccessibilityEvent(AccessibilityEvent event) {
+        synchronized (mLock) {
+            switch (event.getEventType()) {
+                case TYPE_GESTURE_DETECTION_START:
+                case TYPE_GESTURE_DETECTION_END:
+                case TYPE_VIEW_FOCUSED:
+                case TYPE_VIEW_ACCESSIBILITY_FOCUSED:
+                case TYPE_VIEW_CLICKED:
+                case TYPE_VIEW_LONG_CLICKED:
+                    mCollectedEvents.add(event.getEventType());
+            }
+        }
+        super.onAccessibilityEvent(event);
+    }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
new file mode 100644
index 0000000..c29c268
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
@@ -0,0 +1,352 @@
+/*
+ * 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.accessibilityservice.cts;
+
+import static android.accessibilityservice.cts.utils.AsyncUtils.await;
+import static android.accessibilityservice.cts.utils.GestureUtils.IS_ACTION_DOWN;
+import static android.accessibilityservice.cts.utils.GestureUtils.IS_ACTION_MOVE;
+import static android.accessibilityservice.cts.utils.GestureUtils.IS_ACTION_UP;
+import static android.accessibilityservice.cts.utils.GestureUtils.add;
+import static android.accessibilityservice.cts.utils.GestureUtils.ceil;
+import static android.accessibilityservice.cts.utils.GestureUtils.click;
+import static android.accessibilityservice.cts.utils.GestureUtils.diff;
+import static android.accessibilityservice.cts.utils.GestureUtils.dispatchGesture;
+import static android.accessibilityservice.cts.utils.GestureUtils.doubleTap;
+import static android.accessibilityservice.cts.utils.GestureUtils.doubleTapAndHold;
+import static android.accessibilityservice.cts.utils.GestureUtils.isRawAtPoint;
+import static android.accessibilityservice.cts.utils.GestureUtils.swipe;
+import static android.accessibilityservice.cts.utils.GestureUtils.times;
+import static android.view.MotionEvent.ACTION_HOVER_ENTER;
+import static android.view.MotionEvent.ACTION_HOVER_EXIT;
+import static android.view.MotionEvent.ACTION_HOVER_MOVE;
+import static android.view.accessibility.AccessibilityEvent.TYPE_GESTURE_DETECTION_END;
+import static android.view.accessibility.AccessibilityEvent.TYPE_GESTURE_DETECTION_START;
+import static android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END;
+import static android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START;
+import static android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_INTERACTION_END;
+import static android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_INTERACTION_START;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_CLICKED;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_LONG_CLICKED;
+
+import static org.hamcrest.CoreMatchers.both;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
+import android.accessibilityservice.GestureDescription;
+import android.accessibilityservice.GestureDescription.StrokeDescription;
+import android.accessibilityservice.cts.AccessibilityGestureDispatchTest.GestureDispatchActivity;
+import android.accessibilityservice.cts.utils.EventCapturingClickListener;
+import android.accessibilityservice.cts.utils.EventCapturingHoverListener;
+import android.accessibilityservice.cts.utils.EventCapturingLongClickListener;
+import android.accessibilityservice.cts.utils.EventCapturingTouchListener;
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.platform.test.annotations.AppModeFull;
+import android.util.DisplayMetrics;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+/**
+ * A set of tests for testing touch exploration. Each test dispatches a gesture and checks for the
+ * appropriate hover and/or touch events followed by the appropriate accessibility events. Some
+ * tests will then check for events from the view.
+ */
+@RunWith(AndroidJUnit4.class)
+@AppModeFull
+public class TouchExplorerTest {
+    // Constants
+    private static final float GESTURE_LENGTH_INCHES = 1.0f;
+    private static final int SWIPE_TIME_MILLIS = 400;
+    private TouchExplorationStubAccessibilityService mService;
+    private Instrumentation mInstrumentation;
+    private UiAutomation mUiAutomation;
+    private boolean mHasTouchscreen;
+    private boolean mScreenBigEnough;
+    private EventCapturingHoverListener mHoverListener = new EventCapturingHoverListener(false);
+    private EventCapturingTouchListener mTouchListener = new EventCapturingTouchListener(false);
+    private EventCapturingClickListener mClickListener = new EventCapturingClickListener();
+    private EventCapturingLongClickListener mLongClickListener =
+            new EventCapturingLongClickListener();
+
+    private ActivityTestRule<GestureDispatchActivity> mActivityRule =
+            new ActivityTestRule<>(GestureDispatchActivity.class, false);
+
+    private InstrumentedAccessibilityServiceTestRule<TouchExplorationStubAccessibilityService>
+            mServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+                    TouchExplorationStubAccessibilityService.class, false);
+
+    private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
+
+    @Rule
+    public final RuleChain mRuleChain = RuleChain
+            .outerRule(mActivityRule)
+            .around(mServiceRule)
+            .around(mDumpOnFailureRule);
+
+    Point mCenter; // Center of screen. Gestures all start from this point.
+    PointF mTapLocation;
+    float mSwipeDistance;
+    View mView;
+
+    @Before
+    public void setUp() throws Exception {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mUiAutomation = mInstrumentation.getUiAutomation(
+            UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
+        PackageManager pm = mInstrumentation.getContext().getPackageManager();
+        mHasTouchscreen = pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)
+                || pm.hasSystemFeature(PackageManager.FEATURE_FAKETOUCH);
+        // Find screen size, check that it is big enough for gestures.
+        // Gestures will start in the center of the screen, so we need enough horiz/vert space.
+        WindowManager windowManager =
+                (WindowManager)
+                        mInstrumentation.getContext().getSystemService(Context.WINDOW_SERVICE);
+        final DisplayMetrics metrics = new DisplayMetrics();
+        windowManager.getDefaultDisplay().getRealMetrics(metrics);
+        mCenter = new Point((int) metrics.widthPixels / 2, (int) metrics.heightPixels / 2);
+        mTapLocation = new PointF(mCenter);
+        mScreenBigEnough = (metrics.widthPixels / (2 * metrics.xdpi) > GESTURE_LENGTH_INCHES);
+        if (!mHasTouchscreen || !mScreenBigEnough) return;
+        mService = mServiceRule.enableService();
+        mView = mActivityRule.getActivity().findViewById(R.id.full_screen_text_view);
+        mView.setOnHoverListener(mHoverListener);
+        mView.setOnTouchListener(mTouchListener);
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    mSwipeDistance = mView.getWidth() / 4;
+                    mView.setOnClickListener(mClickListener);
+                    mView.setOnLongClickListener(mLongClickListener);
+                });
+    }
+
+    /** Test a slow swipe which should initiate touch exploration. */
+    @Test
+    @AppModeFull
+    public void testSlowSwipe() {
+        if (!mHasTouchscreen || !mScreenBigEnough) return;
+        dispatch(swipe(mTapLocation, add(mTapLocation, mSwipeDistance, 0), SWIPE_TIME_MILLIS));
+        mHoverListener.assertPropagated(ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT);
+        mTouchListener.assertNonePropagated();
+        mService.assertPropagated(
+                TYPE_VIEW_FOCUSED,
+                TYPE_TOUCH_INTERACTION_START,
+                TYPE_TOUCH_EXPLORATION_GESTURE_START,
+                TYPE_TOUCH_EXPLORATION_GESTURE_END,
+                TYPE_TOUCH_INTERACTION_END);
+    }
+
+    /** Test a fast swipe which should not initiate touch exploration. */
+    @Test
+    @AppModeFull
+    public void testFastSwipe() {
+        if (!mHasTouchscreen || !mScreenBigEnough) return;
+        dispatch(swipe(mTapLocation, add(mTapLocation, mSwipeDistance, 0)));
+        mHoverListener.assertNonePropagated();
+        mTouchListener.assertNonePropagated();
+        mService.assertPropagated(
+                TYPE_VIEW_FOCUSED,
+                TYPE_TOUCH_INTERACTION_START,
+                TYPE_GESTURE_DETECTION_START,
+                TYPE_GESTURE_DETECTION_END,
+                TYPE_TOUCH_INTERACTION_END);
+    }
+
+    /**
+     * Test a two finger drag. TouchExplorer would perform a drag gesture when two fingers moving
+     * in the same direction.
+     */
+    @Test
+    @AppModeFull
+    public void testTwoFingerDrag() {
+        if (!mHasTouchscreen || !mScreenBigEnough) return;
+        // A two point moving that are in the same direction can perform a drag gesture by
+        // TouchExplorer while one point moving can not perform a drag gesture. We use two swipes
+        // to emulate a two finger drag gesture.
+        final int twoFingerOffset = (int) mSwipeDistance;
+        final PointF dragStart = mTapLocation;
+        final PointF dragEnd = add(dragStart, 0, mSwipeDistance);
+        final PointF finger1Start = add(dragStart, twoFingerOffset, 0);
+        final PointF finger1End = add(finger1Start, 0, mSwipeDistance);
+        final PointF finger2Start = add(dragStart, -twoFingerOffset, 0);
+        final PointF finger2End = add(finger2Start, 0, mSwipeDistance);
+        dispatch(swipe(finger1Start, finger1End, SWIPE_TIME_MILLIS),
+                swipe(finger2Start, finger2End, SWIPE_TIME_MILLIS));
+        List<MotionEvent> twoFingerPoints = mTouchListener.getRawEvents();
+
+        // Check the drag events performed by a two finger drag. The moving locations would be
+        // adjusted to the middle of two fingers.
+        final int numEvents = twoFingerPoints.size();
+        final int upEventIndex = numEvents - 1;
+        final float intervalFraction = ((float) (twoFingerPoints.get(1).getEventTime()
+                - twoFingerPoints.get(0).getEventTime())) / SWIPE_TIME_MILLIS;
+        for (int i = 0; i < numEvents; i++) {
+            MotionEvent moveEvent = twoFingerPoints.get(i);
+            float fractionOfDrag = intervalFraction * (i + 1);
+            if (i == 0) {
+                PointF downPoint = add(finger2Start,
+                        ceil(times(fractionOfDrag, diff(dragEnd, dragStart))));
+                assertThat(moveEvent,
+                        both(IS_ACTION_DOWN).and(isRawAtPoint(downPoint)));
+            } else if (i == upEventIndex) {
+                assertThat(moveEvent,
+                        both(IS_ACTION_UP).and(isRawAtPoint(finger2End)));
+            } else {
+                PointF intermediatePoint = add(dragStart,
+                        ceil(times(fractionOfDrag, diff(dragEnd, dragStart))));
+                assertThat(moveEvent,
+                        both(IS_ACTION_MOVE).and(isRawAtPoint(intermediatePoint)));
+            }
+        }
+    }
+
+    /** Test a basic single tap which should initiate touch exploration. */
+    @Test
+    @AppModeFull
+    public void testSingleTap() {
+        if (!mHasTouchscreen || !mScreenBigEnough) return;
+        dispatch(click(mTapLocation));
+        mHoverListener.assertPropagated(ACTION_HOVER_ENTER, ACTION_HOVER_EXIT);
+        mTouchListener.assertNonePropagated();
+        mService.assertPropagated(
+                TYPE_VIEW_FOCUSED,
+                TYPE_TOUCH_INTERACTION_START,
+                TYPE_TOUCH_EXPLORATION_GESTURE_START,
+                TYPE_TOUCH_EXPLORATION_GESTURE_END,
+                TYPE_TOUCH_INTERACTION_END);
+    }
+
+    /**
+     * Test the case where we want to click on the item that has accessibility focus by using
+     * AccessibilityNodeInfo.performAction.
+     */
+    @Test
+    @AppModeFull
+    public void testDoubleTapAccessibilityFocus() {
+        if (!mHasTouchscreen || !mScreenBigEnough) return;
+        syncAccessibilityFocusToInputFocus();
+        dispatch(doubleTap(mTapLocation));
+        mHoverListener.assertNonePropagated();
+        // The click should not be delivered via touch events in this case.
+        mTouchListener.assertNonePropagated();
+        mService.assertPropagated(
+                TYPE_VIEW_FOCUSED,
+                TYPE_VIEW_ACCESSIBILITY_FOCUSED,
+                TYPE_TOUCH_INTERACTION_START,
+                TYPE_TOUCH_INTERACTION_END,
+                TYPE_VIEW_CLICKED);
+        mClickListener.assertClicked(mView);
+    }
+
+    /**
+     * Test the case where we double tap but there is no  accessibility focus. Nothing should
+     * happen.
+     */
+    @Test
+    @AppModeFull
+    public void testDoubleTapNoAccessibilityFocus() {
+        if (!mHasTouchscreen || !mScreenBigEnough) return;
+        dispatch(doubleTap(mTapLocation));
+        mHoverListener.assertNonePropagated();
+        mTouchListener.assertNonePropagated();
+        mService.assertPropagated(
+                TYPE_VIEW_FOCUSED, TYPE_TOUCH_INTERACTION_START, TYPE_TOUCH_INTERACTION_END);
+        mService.clearEvents();
+        mClickListener.assertNoneClicked();
+    }
+
+    /** Test the case where we want to long click on the item that has accessibility focus. */
+    @Test
+    @AppModeFull
+    public void testDoubleTapAndHoldAccessibilityFocus() {
+        if (!mHasTouchscreen || !mScreenBigEnough) return;
+        syncAccessibilityFocusToInputFocus();
+        dispatch(doubleTapAndHold(mTapLocation));
+        mHoverListener.assertNonePropagated();
+        // The click should not be delivered via touch events in this case.
+        mTouchListener.assertNonePropagated();
+        mService.assertPropagated(
+                TYPE_VIEW_FOCUSED,
+                TYPE_VIEW_ACCESSIBILITY_FOCUSED,
+                TYPE_TOUCH_INTERACTION_START,
+                TYPE_VIEW_LONG_CLICKED,
+                TYPE_TOUCH_INTERACTION_END);
+        mLongClickListener.assertLongClicked(mView);
+    }
+
+    /**
+     * Test the case where we double tap and hold but there is no accessibility focus.
+     * Nothing should happen.
+     */
+    @Test
+    @AppModeFull
+    public void testDoubleTapAndHoldNoAccessibilityFocus() {
+        if (!mHasTouchscreen || !mScreenBigEnough) return;
+        dispatch(doubleTap(mTapLocation));
+        mHoverListener.assertNonePropagated();
+        mTouchListener.assertNonePropagated();
+        mService.assertPropagated(
+                TYPE_VIEW_FOCUSED, TYPE_TOUCH_INTERACTION_START, TYPE_TOUCH_INTERACTION_END);
+        mService.clearEvents();
+        mLongClickListener.assertNoneLongClicked();
+    }
+
+    public void dispatch(StrokeDescription firstStroke, StrokeDescription... rest) {
+        GestureDescription.Builder builder =
+                new GestureDescription.Builder().addStroke(firstStroke);
+        for (StrokeDescription stroke : rest) {
+            builder.addStroke(stroke);
+        }
+        dispatch(builder.build());
+    }
+
+    public void dispatch(GestureDescription gesture) {
+        await(dispatchGesture(mService, gesture));
+    }
+
+    /** Set the accessibility focus to the element that has input focus. */
+    private void syncAccessibilityFocusToInputFocus() {
+        mService.runOnServiceSync(
+                () -> {
+                    mUiAutomation
+                            .getRootInActiveWindow()
+                            .findFocus(AccessibilityNodeInfo.FOCUS_INPUT)
+                            .performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+                });
+    }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/DisplayUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/DisplayUtils.java
index a65d859..e11c60e 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/DisplayUtils.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/DisplayUtils.java
@@ -14,18 +14,118 @@
 
 package android.accessibilityservice.cts.utils;
 
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
+import static android.view.Display.FLAG_PRIVATE;
+
 import android.app.Activity;
+import android.content.Context;
+import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.ImageReader;
+import android.os.Looper;
+import android.util.DisplayMetrics;
+import android.view.Display;
 import android.view.Window;
+import android.view.WindowManager;
+
+import com.android.compatibility.common.util.TestUtils;
 
 /**
  * Utilities needed when interacting with the display
  */
 public class DisplayUtils {
+    private static final int DISPLAY_ADDED_TIMOUT_MS = 5000;
+
     public static int getStatusBarHeight(Activity activity) {
         final Rect rect = new Rect();
         Window window = activity.getWindow();
         window.getDecorView().getWindowVisibleDisplayFrame(rect);
         return rect.top;
     }
+
+    public static class VirtualDisplaySession implements AutoCloseable {
+        private VirtualDisplay mVirtualDisplay;
+        private ImageReader mReader;
+
+        public Display createDisplay(Context context, int width, int height, int density,
+                boolean isPrivate) {
+            if (mReader != null) {
+                throw new IllegalStateException(
+                        "Only one display can be created during this session.");
+            }
+            mReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888,
+                    1 /* maxImages */);
+            int flags = isPrivate ? 0
+                    :(VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | VIRTUAL_DISPLAY_FLAG_PUBLIC);
+            mVirtualDisplay = context.getSystemService(DisplayManager.class).createVirtualDisplay(
+                    "A11yDisplay", width, height, density, mReader.getSurface(), flags);
+            return mVirtualDisplay.getDisplay();
+        }
+
+        @Override
+        public void close() {
+            if (mVirtualDisplay != null) {
+                mVirtualDisplay.release();
+            }
+            if (mReader != null) {
+                mReader.close();
+            }
+        }
+
+        /**
+         * Creates a virtual display and waits until it's in display list.
+         * @param context
+         * @param isPrivate if this display is a private display.
+         * @return virtual display.
+         *
+         * @throws IllegalStateException if called from main thread.
+         */
+        public Display createDisplayWithDefaultDisplayMetricsAndWait(Context context,
+                boolean isPrivate) {
+            if (Looper.myLooper() == Looper.getMainLooper()) {
+                throw new IllegalStateException("Should not call from main thread");
+            }
+
+            final Object waitObject = new Object();
+            final DisplayManager.DisplayListener listener = new DisplayManager.DisplayListener() {
+                @Override
+                public void onDisplayAdded(int i) {
+                    synchronized (waitObject) {
+                        waitObject.notifyAll();
+                    }
+                }
+
+                @Override
+                public void onDisplayRemoved(int i) {
+                }
+
+                @Override
+                public void onDisplayChanged(int i) {
+                }
+            };
+            final DisplayManager displayManager = (DisplayManager) context.getSystemService(
+                    Context.DISPLAY_SERVICE);
+            displayManager.registerDisplayListener(listener, null);
+
+            final WindowManager windowManager = (WindowManager) context.getSystemService(
+                    Context.WINDOW_SERVICE);
+            final DisplayMetrics metrics = new DisplayMetrics();
+            windowManager.getDefaultDisplay().getRealMetrics(metrics);
+            final Display display = createDisplay(context, metrics.widthPixels,
+                    metrics.heightPixels, metrics.densityDpi, isPrivate);
+
+            try {
+                TestUtils.waitOn(waitObject,
+                        () -> displayManager.getDisplay(display.getDisplayId()) != null,
+                        DISPLAY_ADDED_TIMOUT_MS,
+                        String.format("wait for virtual display %d adding", display.getDisplayId()));
+            } finally {
+                displayManager.unregisterDisplayListener(listener);
+            }
+            return display;
+        }
+    }
 }
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingClickListener.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingClickListener.java
new file mode 100644
index 0000000..116bccf
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingClickListener.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.accessibilityservice.cts.utils;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.view.View;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/** A click event listener that keeps an ordered record of events. */
+public class EventCapturingClickListener implements View.OnClickListener {
+
+    private final BlockingQueue<View> mViews = new LinkedBlockingQueue<>();
+
+    @Override
+    public void onClick(View view) {
+        mViews.offer(view);
+    }
+
+    /** Insure that the specified views have received click events. */
+    public void assertClicked(View... views) {
+        View view;
+        try {
+            for (View v : views) {
+                long waitTime = 5; // seconds
+                view = mViews.poll(waitTime, SECONDS);
+                assertNotNull(
+                        "Expected click event for "
+                                + v.toString()
+                                + " but none present after "
+                                + waitTime
+                                + " seconds",
+                        view);
+                if (v != view) {
+                    fail("Unexpected click event for view" + view.toString());
+                }
+            }
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /** Insure that no click events have been received. */
+    public void assertNoneClicked() {
+        try {
+            long waitTime = 1; // seconds
+            View view = mViews.poll(waitTime, SECONDS);
+            if (view != null) {
+                fail("Unexpected click event for view" + view.toString());
+            }
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingHoverListener.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingHoverListener.java
new file mode 100644
index 0000000..87d46ba
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingHoverListener.java
@@ -0,0 +1,105 @@
+/*
+ * 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.accessibilityservice.cts.utils;
+
+import static android.view.MotionEvent.ACTION_HOVER_MOVE;
+import static java.util.concurrent.TimeUnit.SECONDS;
+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.view.MotionEvent;
+import android.view.View;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/** This Listener listens for and logs hover events so they can be checked later by tests. */
+public class EventCapturingHoverListener implements View.OnHoverListener {
+
+    private boolean shouldConsumeEvents; // whether or not to keep events from propagating to other
+    // listeners
+    private final BlockingQueue<MotionEvent> mEvents = new LinkedBlockingQueue<>();
+
+    public EventCapturingHoverListener(boolean shouldConsumeEvents) {
+        this.shouldConsumeEvents = shouldConsumeEvents;
+    }
+
+    public EventCapturingHoverListener() {
+        this.shouldConsumeEvents = true;
+    }
+
+    @Override
+    public boolean onHover(View view, MotionEvent MotionEvent) {
+        assertTrue(mEvents.offer(MotionEvent.obtain(MotionEvent)));
+        return shouldConsumeEvents;
+    }
+
+    /** Insure that no hover events have been detected. */
+    public void assertNonePropagated() {
+        try {
+            long waitTime = 1; // seconds
+            MotionEvent event = mEvents.poll(waitTime, SECONDS);
+            if (event != null) {
+                fail("Unexpected touch event " + event.toString());
+            }
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Check for the specified hover events. Note that specifying ACTION_HOVER_MOVE will match one
+     * or more consecutive ACTION_HOVER_MOVE events.
+     */
+    public void assertPropagated(int... eventTypes) {
+        MotionEvent ev;
+        long waitTime = 5; // seconds
+        try {
+            List<String> expected = new ArrayList<>();
+            List<String> received = new ArrayList<>();
+            for (int e : eventTypes) {
+                expected.add(MotionEvent.actionToString(e));
+            }
+            ev = mEvents.poll(waitTime, SECONDS);
+            assertNotNull(
+                    "Expected " + expected + " but none present after " + waitTime + " seconds",
+                    ev);
+            // By this point there is at least one received event.
+            received.add(MotionEvent.actionToString(ev.getActionMasked()));
+            ev = mEvents.poll(waitTime, SECONDS);
+            while (ev != null) {
+                int action = ev.getActionMasked();
+                if (action != ACTION_HOVER_MOVE) {
+                    received.add(MotionEvent.actionToString(action));
+                } else {
+                    // Add the current event if the previous received event was not ACTION_MOVE
+                    String prev = received.get(received.size() - 1);
+                    if (!prev.equals(MotionEvent.actionToString(ACTION_HOVER_MOVE))) {
+                        received.add(MotionEvent.actionToString(action));
+                    }
+                }
+                ev = mEvents.poll(waitTime, SECONDS);
+            }
+            assertEquals(expected, received);
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingLongClickListener.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingLongClickListener.java
new file mode 100644
index 0000000..5daf89e
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingLongClickListener.java
@@ -0,0 +1,87 @@
+/*
+ * 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.accessibilityservice.cts.utils;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.view.View;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/** A click event listener that keeps an ordered record of events. */
+public class EventCapturingLongClickListener implements View.OnLongClickListener {
+
+    // whether or not to keep events from propagating to other listeners.
+    // Note that setting this to false means that the accessibility service will see both a long
+    // click and a click event when you perform a double tap and hold gesture
+    private boolean shouldConsumeEvents;
+    private final BlockingQueue<View> mViews = new LinkedBlockingQueue<>();
+
+    public EventCapturingLongClickListener(boolean shouldConsumeEvents) {
+        this.shouldConsumeEvents = shouldConsumeEvents;
+    }
+
+    public EventCapturingLongClickListener() {
+        this.shouldConsumeEvents = true;
+    }
+
+    @Override
+    public boolean onLongClick(View view) {
+        mViews.offer(view);
+        return true;
+    }
+
+    /** Insure that the specified views have received long click events. */
+    public void assertLongClicked(View... views) {
+        View view;
+        try {
+            for (View v : views) {
+                long waitTime = 5; // seconds
+                view = mViews.poll(waitTime, SECONDS);
+                assertNotNull(
+                        "Expected long click event for "
+                                + v.toString()
+                                + " but none present after "
+                                + waitTime
+                                + " seconds",
+                        view);
+                if (v != view) {
+                    fail("Unexpected long click event for view" + view.toString());
+                }
+            }
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /** Insure that no click events have been received. */
+    public void assertNoneLongClicked() {
+        try {
+            long waitTime = 1; // seconds
+            View view = mViews.poll(waitTime, SECONDS);
+            if (view != null) {
+                fail("Unexpected long click event for view" + view.toString());
+            }
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingTouchListener.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingTouchListener.java
index d7ae4592..3997ebe 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingTouchListener.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingTouchListener.java
@@ -16,22 +16,109 @@
 
 package android.accessibilityservice.cts.utils;
 
+import static android.view.MotionEvent.ACTION_MOVE;
 
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+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.view.MotionEvent;
 import android.view.View;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
 
 public class EventCapturingTouchListener implements View.OnTouchListener {
+    private static final long WAIT_TIME_SECONDS = 5;
+    private static final long MIN_WAIT_TIME_SECONDS = 2;
+    // whether or not to keep events from propagating to other listeners
+    private boolean shouldConsumeEvents;
+    private final BlockingQueue<MotionEvent> events = new LinkedBlockingQueue<>();
 
-    public final BlockingQueue<MotionEvent> events = new LinkedBlockingQueue<>();
+    public EventCapturingTouchListener(boolean shouldConsumeEvents) {
+        this.shouldConsumeEvents = shouldConsumeEvents;
+    }
+
+    public EventCapturingTouchListener() {
+        this.shouldConsumeEvents = true;
+    }
 
     @Override
     public boolean onTouch(View view, MotionEvent motionEvent) {
         assertTrue(events.offer(MotionEvent.obtain(motionEvent)));
-        return true;
+        return shouldConsumeEvents;
+    }
+
+    /** Insure that no touch events have been detected. */
+    public void assertNonePropagated() {
+        try {
+            MotionEvent event = events.poll(MIN_WAIT_TIME_SECONDS, SECONDS);
+            if (event != null) {
+                fail("Unexpected touch event " + event.toString());
+            }
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Check for the specified touch events. Note that specifying ACTION_MOVE will match one or more
+     * consecutive ACTION_MOVE events.
+     */
+    public void assertPropagated(int... eventTypes) {
+        MotionEvent ev;
+        try {
+            List<String> expected = new ArrayList<>();
+            List<String> received = new ArrayList<>();
+            for (int e : eventTypes) {
+                expected.add(MotionEvent.actionToString(e));
+            }
+            ev = events.poll(WAIT_TIME_SECONDS, SECONDS);
+            assertNotNull(
+                    "Expected " + expected + " but none present after " + WAIT_TIME_SECONDS
+                            + " seconds",
+                    ev);
+            // By this point there is at least one received event.
+            received.add(MotionEvent.actionToString(ev.getActionMasked()));
+            ev = events.poll(WAIT_TIME_SECONDS, SECONDS);
+            while (ev != null) {
+                int action = ev.getActionMasked();
+                if (action != ACTION_MOVE) {
+                    received.add(MotionEvent.actionToString(action));
+                } else {
+                    // Add the current event if the previous received event was not ACTION_MOVE
+                    String prev = received.get(received.size() - 1);
+                    if (!prev.equals(MotionEvent.actionToString(ACTION_MOVE))) {
+                        received.add(MotionEvent.actionToString(action));
+                    }
+                }
+                ev = events.poll(WAIT_TIME_SECONDS, SECONDS);
+            }
+            assertEquals(expected, received);
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public List<MotionEvent> getRawEvents() {
+        List<MotionEvent> motionEvents = new ArrayList<>();
+        MotionEvent ev;
+        try {
+            ev = events.poll(WAIT_TIME_SECONDS, SECONDS);
+            while (ev != null) {
+                motionEvents.add(ev);
+                ev = events.poll(WAIT_TIME_SECONDS, SECONDS);
+            }
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+        assertFalse(motionEvents.isEmpty());
+        return motionEvents;
     }
 }
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/GestureUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/GestureUtils.java
index 99fa727..58716ee 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/GestureUtils.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/GestureUtils.java
@@ -22,12 +22,30 @@
 import android.accessibilityservice.GestureDescription.StrokeDescription;
 import android.graphics.Path;
 import android.graphics.PointF;
+import android.view.MotionEvent;
 import android.view.ViewConfiguration;
 
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
 import java.util.concurrent.CompletableFuture;
 
 public class GestureUtils {
 
+    public static final Matcher<MotionEvent> IS_ACTION_DOWN =
+            new MotionEventActionMatcher(MotionEvent.ACTION_DOWN);
+    public static final Matcher<MotionEvent> IS_ACTION_POINTER_DOWN =
+            new MotionEventActionMatcher(MotionEvent.ACTION_POINTER_DOWN);
+    public static final Matcher<MotionEvent> IS_ACTION_UP =
+            new MotionEventActionMatcher(MotionEvent.ACTION_UP);
+    public static final Matcher<MotionEvent> IS_ACTION_POINTER_UP =
+            new MotionEventActionMatcher(MotionEvent.ACTION_POINTER_UP);
+    public static final Matcher<MotionEvent> IS_ACTION_CANCEL =
+            new MotionEventActionMatcher(MotionEvent.ACTION_CANCEL);
+    public static final Matcher<MotionEvent> IS_ACTION_MOVE =
+            new MotionEventActionMatcher(MotionEvent.ACTION_MOVE);
+
     private GestureUtils() {}
 
     public static CompletableFuture<Void> dispatchGesture(
@@ -73,7 +91,7 @@
 
     public static StrokeDescription longClick(PointF point) {
         return new StrokeDescription(path(point), 0,
-                ViewConfiguration.getLongPressTimeout() * 3 / 2);
+                ViewConfiguration.getLongPressTimeout() * 3);
     }
 
     public static StrokeDescription swipe(PointF from, PointF to) {
@@ -141,4 +159,87 @@
     public static PointF ceil(PointF p) {
         return new PointF((float) Math.ceil(p.x), (float) Math.ceil(p.y));
     }
-}
+
+    public static GestureDescription doubleTap(PointF point) {
+        return multiTap(point, 2);
+    }
+
+    public static GestureDescription tripleTap(PointF point) {
+      return multiTap(point, 3);
+    }
+
+    public static GestureDescription multiTap(PointF point, int taps) {
+        GestureDescription.Builder builder = new GestureDescription.Builder();
+        long time = 0;
+        for (int i = 0; i < taps; i++) {
+            StrokeDescription stroke = click(point);
+            builder.addStroke(startingAt(time, stroke));
+            time += stroke.getDuration() + 40;
+        }
+        return builder.build();
+    }
+
+    public static GestureDescription doubleTapAndHold(PointF point) {
+        GestureDescription.Builder builder = new GestureDescription.Builder();
+        StrokeDescription tap1 = click(point);
+        StrokeDescription tap2 = startingAt(endTimeOf(tap1) + 40, longClick(point));
+        builder.addStroke(tap1);
+        builder.addStroke(tap2);
+        return builder.build();
+    }
+
+    private static class MotionEventActionMatcher extends TypeSafeMatcher<MotionEvent> {
+        int mAction;
+
+        MotionEventActionMatcher(int action) {
+            super();
+            mAction = action;
+        }
+
+        @Override
+        protected boolean matchesSafely(MotionEvent motionEvent) {
+            return motionEvent.getActionMasked() == mAction;
+        }
+
+        @Override
+        public void describeTo(Description description) {
+            description.appendText("Matching to action " + MotionEvent.actionToString(mAction));
+        }
+    }
+
+    public static Matcher<MotionEvent> isAtPoint(final PointF point) {
+        return isAtPoint(point, 0.01f);
+    }
+
+    public static Matcher<MotionEvent> isAtPoint(final PointF point, final float tol) {
+        return new TypeSafeMatcher<MotionEvent>() {
+            @Override
+            protected boolean matchesSafely(MotionEvent event) {
+                return Math.hypot(event.getX() - point.x, event.getY() - point.y) < tol;
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("Matching to point " + point);
+            }
+        };
+    }
+
+    public static Matcher<MotionEvent> isRawAtPoint(final PointF point) {
+        return isRawAtPoint(point, 0.01f);
+    }
+
+    public static Matcher<MotionEvent> isRawAtPoint(final PointF point, final float tol) {
+        return new TypeSafeMatcher<MotionEvent>() {
+            @Override
+            protected boolean matchesSafely(MotionEvent event) {
+                return Math.hypot(event.getRawX() - point.x, event.getRawY() - point.y) < tol;
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("Matching to point " + point);
+            }
+        };
+    }
+}
\ No newline at end of file
diff --git a/tests/admin/OWNERS b/tests/admin/OWNERS
new file mode 100644
index 0000000..58d02a3
--- /dev/null
+++ b/tests/admin/OWNERS
@@ -0,0 +1,9 @@
+# Bug component: 100560
+sandness@google.com
+rubinxu@google.com
+eranm@google.com
+kholoudm@google.com
+pgrafov@google.com
+alexkershaw@google.com
+arangelov@google.com
+scottjonathan@google.com
diff --git a/tests/app/AndroidTest.xml b/tests/app/AndroidTest.xml
index 461a929..76e6d54 100644
--- a/tests/app/AndroidTest.xml
+++ b/tests/app/AndroidTest.xml
@@ -18,6 +18,7 @@
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="force-install-mode" value="FULL"/>
diff --git a/tests/app/NotificationDelegator/AndroidManifest.xml b/tests/app/NotificationDelegator/AndroidManifest.xml
index 54d1f05..fbdf219 100644
--- a/tests/app/NotificationDelegator/AndroidManifest.xml
+++ b/tests/app/NotificationDelegator/AndroidManifest.xml
@@ -30,5 +30,13 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
+
+        <activity android:name="com.android.test.notificationdelegator.NotificationDelegateAndPost">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
     </application>
 </manifest>
diff --git a/tests/app/NotificationDelegator/src/com/android/test/notificationdelegator/NotificationDelegateAndPost.java b/tests/app/NotificationDelegator/src/com/android/test/notificationdelegator/NotificationDelegateAndPost.java
new file mode 100644
index 0000000..521adc5
--- /dev/null
+++ b/tests/app/NotificationDelegator/src/com/android/test/notificationdelegator/NotificationDelegateAndPost.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 com.android.test.notificationdelegator;
+
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.os.Bundle;
+import android.util.Log;
+
+public class NotificationDelegateAndPost extends Activity {
+    private static final String TAG = "DelegateAndPost";
+    private static final String DELEGATE = "android.app.stubs";
+    private static final String CHANNEL = "channel";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity);
+
+        NotificationManager nm = getSystemService(NotificationManager.class);
+
+        nm.createNotificationChannel(new NotificationChannel(CHANNEL, CHANNEL, IMPORTANCE_LOW));
+        nm.setNotificationDelegate(DELEGATE);
+        Log.d(TAG, "Set delegate: " + nm.getNotificationDelegate());
+
+        Log.d(TAG, "Posting notification with id 9");
+
+        Notification n = new Notification.Builder(this, CHANNEL)
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setContentTitle("posted by delegator")
+                .build();
+
+        nm.notify(9, n);
+
+        finish();
+    }
+}
diff --git a/tests/app/OWNERS b/tests/app/OWNERS
new file mode 100644
index 0000000..8446f2e
--- /dev/null
+++ b/tests/app/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 316234
+yamasani@google.com
diff --git a/tests/app/TEST_MAPPING b/tests/app/TEST_MAPPING
index 60d71d3..ca2dd6c 100644
--- a/tests/app/TEST_MAPPING
+++ b/tests/app/TEST_MAPPING
@@ -11,5 +11,10 @@
         }
       ]
     }
+  ],
+  "postsubmit": [
+    {
+      "name": "CtsAppTestCases"
+    }
   ]
 }
diff --git a/tests/app/app/AndroidManifest.xml b/tests/app/app/AndroidManifest.xml
index 7d34da0..ddd8910 100644
--- a/tests/app/app/AndroidManifest.xml
+++ b/tests/app/app/AndroidManifest.xml
@@ -45,6 +45,7 @@
     <uses-permission android:name="android.permission.SET_WALLPAPER" />
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
 
     <application android:label="Android TestCase"
                 android:icon="@drawable/size_48x48"
@@ -419,6 +420,8 @@
                  android:label="BubblesTestsService"
                  android:exported="true">
         </service>
+
+        <service android:name="android.app.stubs.LocalAlertService" />
     </application>
 
 </manifest>
diff --git a/tests/app/app/src/android/app/stubs/CommandReceiver.java b/tests/app/app/src/android/app/stubs/CommandReceiver.java
index bc2b4d4..674fd41 100644
--- a/tests/app/app/src/android/app/stubs/CommandReceiver.java
+++ b/tests/app/app/src/android/app/stubs/CommandReceiver.java
@@ -40,6 +40,8 @@
     public static final int COMMAND_STOP_FOREGROUND_SERVICE = 4;
     public static final int COMMAND_START_FOREGROUND_SERVICE_LOCATION = 5;
     public static final int COMMAND_STOP_FOREGROUND_SERVICE_LOCATION = 6;
+    public static final int COMMAND_START_ALERT_SERVICE = 7;
+    public static final int COMMAND_STOP_ALERT_SERVICE = 8;
 
     public static final String EXTRA_COMMAND = "android.app.stubs.extra.COMMAND";
     public static final String EXTRA_TARGET_PACKAGE = "android.app.stubs.extra.TARGET_PACKAGE";
@@ -84,6 +86,12 @@
             case COMMAND_STOP_FOREGROUND_SERVICE_LOCATION:
                 doStopForegroundService(context, LocalForegroundServiceLocation.class);
                 break;
+            case COMMAND_START_ALERT_SERVICE:
+                doStartAlertService(context);
+                break;
+            case COMMAND_STOP_ALERT_SERVICE:
+                doStopAlertService(context);
+                break;
         }
     }
 
@@ -95,13 +103,13 @@
         bindIntent.setComponent(new ComponentName(targetPackage, SERVICE_NAME));
 
         ServiceConnection connection = addServiceConnection(targetPackage);
+
         context.bindService(bindIntent, connection, flags | Context.BIND_AUTO_CREATE);
     }
 
     private void doUnbindService(Context context, Intent commandIntent) {
         String targetPackage = getTargetPackage(commandIntent);
-        ServiceConnection connection = sServiceMap.remove(targetPackage);
-        context.unbindService(connection);
+        context.unbindService(sServiceMap.remove(targetPackage));
     }
 
     private void doStartForegroundService(Context context, Class cls) {
@@ -124,6 +132,18 @@
         context.stopService(fgsIntent);
     }
 
+    private void doStartAlertService(Context context) {
+        Intent intent = new Intent(context, LocalAlertService.class);
+        intent.setAction(LocalAlertService.COMMAND_SHOW_ALERT);
+        context.startService(intent);
+    }
+
+    private void doStopAlertService(Context context) {
+        Intent intent = new Intent(context, LocalAlertService.class);
+        intent.setAction(LocalAlertService.COMMAND_HIDE_ALERT);
+        context.startService(intent);
+    }
+
     private String getTargetPackage(Intent intent) {
         return intent.getStringExtra(EXTRA_TARGET_PACKAGE);
     }
diff --git a/tests/app/app/src/android/app/stubs/LocalAlertService.java b/tests/app/app/src/android/app/stubs/LocalAlertService.java
new file mode 100644
index 0000000..52dbc58
--- /dev/null
+++ b/tests/app/app/src/android/app/stubs/LocalAlertService.java
@@ -0,0 +1,84 @@
+/*
+ * 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.Service;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import static android.view.Gravity.LEFT;
+import static android.view.Gravity.TOP;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+
+public class LocalAlertService extends Service {
+    public static final String COMMAND_SHOW_ALERT = "show";
+    public static final String COMMAND_HIDE_ALERT = "hide";
+
+    private static View mAlertWindow = null;
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        String action = intent.getAction();
+
+        if (COMMAND_SHOW_ALERT.equals(action)) {
+            mAlertWindow = showAlertWindow(getPackageName());
+        } else if (COMMAND_HIDE_ALERT.equals(action)) {
+            hideAlertWindow(mAlertWindow);
+            mAlertWindow = null;
+        }
+        return START_NOT_STICKY;
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    private View showAlertWindow(String windowName) {
+        final Point size = new Point();
+        final WindowManager wm = getSystemService(WindowManager.class);
+        wm.getDefaultDisplay().getSize(size);
+
+        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+                TYPE_APPLICATION_OVERLAY, FLAG_NOT_FOCUSABLE | FLAG_WATCH_OUTSIDE_TOUCH |
+                FLAG_NOT_TOUCHABLE);
+        params.width = size.x / 3;
+        params.height = size.y / 3;
+        params.gravity = TOP | LEFT;
+        params.setTitle(windowName);
+
+        final TextView view = new TextView(this);
+        view.setText(windowName);
+        view.setBackgroundColor(Color.RED);
+        wm.addView(view, params);
+        return view;
+    }
+
+    private void hideAlertWindow(View window) {
+        final WindowManager wm = getSystemService(WindowManager.class);
+        wm.removeViewImmediate(window);
+    }
+}
diff --git a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
index 2a0e096..c3fc069 100644
--- a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
@@ -61,7 +61,6 @@
 
 import androidx.test.InstrumentationRegistry;
 
-import com.android.compatibility.common.util.CommonTestUtils;
 import com.android.compatibility.common.util.SystemUtil;
 
 public class ActivityManagerProcessStateTest extends InstrumentationTestCase {
@@ -159,18 +158,6 @@
     private void turnScreenOn() throws Exception {
         executeShellCmd("input keyevent KEYCODE_WAKEUP");
         executeShellCmd("wm dismiss-keyguard");
-        /*
-        Wait until the screen becomes interactive to start the test cases.
-        Otherwise the procstat may start in TOP_SLEEPING state, and this
-        causes test case testBackgroundCheckActivityService to fail.
-        Note: There could still a small chance the procstat is TOP_SLEEPING
-        when the predicate returns true. 
-        */
-        CommonTestUtils.waitUntil("Device does not wake up after 5 seconds",
-            5,
-            () ->  {
-                return isScreenInteractive() && !isKeyguardLocked();
-            });
     }
 
     private void removeTestAppFromWhitelists() throws Exception {
@@ -1477,9 +1464,9 @@
 
             // Clean up: unbind services to avoid from interferences with other tests
             CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
-                PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null);
+                    PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null);
             CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
-                PACKAGE_NAME_APP1, PACKAGE_NAME_APP3, 0, null);
+                    PACKAGE_NAME_APP1, PACKAGE_NAME_APP3, 0, null);
         } finally {
             uid1Watcher.finish();
             uid3Watcher.finish();
@@ -1558,12 +1545,12 @@
 
             // Clean up: unbind services to avoid from interferences with other tests
             CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
-                PACKAGE_NAME_APP2, PACKAGE_NAME_APP3, 0, null);
+                    PACKAGE_NAME_APP2, PACKAGE_NAME_APP3, 0, null);
             CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
-                PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null);
+                    PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null);
             // Stop the foreground service
             CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_STOP_FOREGROUND_SERVICE,
-                PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null);
+                    PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null);
         } finally {
             uid1Watcher.finish();
             uid2Watcher.finish();
@@ -1634,15 +1621,15 @@
 
             // Clean up: unbind services to avoid from interferences with other tests
             CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
-                PACKAGE_NAME_APP1, PACKAGE_NAME_APP2, 0, null);
+                    PACKAGE_NAME_APP1, PACKAGE_NAME_APP2, 0, null);
             CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
-                PACKAGE_NAME_APP1, PACKAGE_NAME_APP3, 0, null);
+                    PACKAGE_NAME_APP1, PACKAGE_NAME_APP3, 0, null);
             CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
-                PACKAGE_NAME_APP2, PACKAGE_NAME_APP3, 0, null);
+                    PACKAGE_NAME_APP2, PACKAGE_NAME_APP3, 0, null);
             CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
-                PACKAGE_NAME_APP3, PACKAGE_NAME_APP2, 0, null);
+                    PACKAGE_NAME_APP3, PACKAGE_NAME_APP2, 0, null);
             CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
-                PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null);
+                    PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null);
         } finally {
             uid1Watcher.finish();
             uid2Watcher.finish();
@@ -1757,9 +1744,9 @@
 
             // Clean up: unbind services to avoid from interferences with other tests
             CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
-                mAppInfo[0].packageName, mAppInfo[1].packageName, 0, null);
+                    mAppInfo[0].packageName, mAppInfo[1].packageName, 0, null);
             CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
-                mAppInfo[0].packageName, mAppInfo[2].packageName, 0, bundle);
+                    mAppInfo[0].packageName, mAppInfo[2].packageName, 0, bundle);
         } finally {
             shutdownWatchers();
         }
@@ -1794,9 +1781,9 @@
 
             // Clean up: unbind services to avoid from interferences with other tests
             CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
-                STUB_PACKAGE_NAME, mAppInfo[0].packageName, 0, null);
+                    STUB_PACKAGE_NAME, mAppInfo[0].packageName, 0, null);
             CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
-                STUB_PACKAGE_NAME, mAppInfo[1].packageName, 0, bundle);
+                    STUB_PACKAGE_NAME, mAppInfo[1].packageName, 0, bundle);
         } finally {
             shutdownWatchers();
             if (activity != null) {
@@ -1921,11 +1908,11 @@
 
             // Clean up: unbind services to avoid from interferences with other tests
             CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
-                PACKAGE_NAME_APP1, PACKAGE_NAME_APP2, 0, null);
+                    PACKAGE_NAME_APP1, PACKAGE_NAME_APP2, 0, null);
             CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
-                PACKAGE_NAME_APP2, PACKAGE_NAME_APP3, 0, null);
+                    PACKAGE_NAME_APP2, PACKAGE_NAME_APP3, 0, null);
             CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
-                PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null);
+                    PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null);
         } finally {
             uid1Listener.unregister();
             uid1ServiceListener.unregister();
@@ -1937,4 +1924,114 @@
             }
         }
     }
+
+    public void testCycleFgAppAndAlert() throws Exception {
+        ApplicationInfo stubInfo = mContext.getPackageManager().getApplicationInfo(
+                STUB_PACKAGE_NAME, 0);
+        ApplicationInfo app1Info = mContext.getPackageManager().getApplicationInfo(
+                PACKAGE_NAME_APP1, 0);
+        ApplicationInfo app2Info = mContext.getPackageManager().getApplicationInfo(
+                PACKAGE_NAME_APP2, 0);
+        ApplicationInfo app3Info = mContext.getPackageManager().getApplicationInfo(
+                PACKAGE_NAME_APP3, 0);
+
+        UidImportanceListener stubListener = new UidImportanceListener(mContext,
+                stubInfo.uid, ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE,
+                WAITFOR_MSEC);
+        stubListener.register();
+
+        UidImportanceListener uid1Listener = new UidImportanceListener(mContext,
+                app1Info.uid, IMPORTANCE_VISIBLE,
+                WAITFOR_MSEC);
+        uid1Listener.register();
+
+        UidImportanceListener uid2Listener = new UidImportanceListener(mContext,
+                app2Info.uid, IMPORTANCE_VISIBLE,
+                WAITFOR_MSEC);
+        uid2Listener.register();
+
+        UidImportanceListener uid3Listener = new UidImportanceListener(mContext,
+                app3Info.uid, IMPORTANCE_VISIBLE,
+                WAITFOR_MSEC);
+        uid3Listener.register();
+
+        WatchUidRunner uid1Watcher = new WatchUidRunner(mInstrumentation, app1Info.uid,
+                WAITFOR_MSEC);
+        WatchUidRunner uid2Watcher = new WatchUidRunner(mInstrumentation, app2Info.uid,
+                WAITFOR_MSEC);
+        WatchUidRunner uid3Watcher = new WatchUidRunner(mInstrumentation, app3Info.uid,
+                WAITFOR_MSEC);
+
+        try {
+            // Stub app should have been in foreground since it's being instrumented.
+
+            // Show an alert on app0
+            CommandReceiver.sendCommand(mContext,
+                    CommandReceiver.COMMAND_START_ALERT_SERVICE, STUB_PACKAGE_NAME,
+                    STUB_PACKAGE_NAME, 0, null);
+
+            // Start a FGS in app2
+            CommandReceiver.sendCommand(mContext,
+                    CommandReceiver.COMMAND_START_FOREGROUND_SERVICE, PACKAGE_NAME_APP2,
+                    PACKAGE_NAME_APP2, 0, null);
+
+            uid2Listener.waitForValue(IMPORTANCE_FOREGROUND_SERVICE,
+                    IMPORTANCE_FOREGROUND_SERVICE);
+
+            // Bind from app0 to a service in app1
+            CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE,
+                    STUB_PACKAGE_NAME, PACKAGE_NAME_APP1, 0, null);
+
+            // Bind from app2 to a service in app1
+            CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE,
+                    PACKAGE_NAME_APP2, PACKAGE_NAME_APP1, 0, null);
+
+            // Bind from app3 to a service in app1
+            CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE,
+                    PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null);
+
+            // Create a cycle
+            CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE,
+                    PACKAGE_NAME_APP1, PACKAGE_NAME_APP3, 0, null);
+
+            uid1Listener.waitForValue(IMPORTANCE_FOREGROUND_SERVICE,
+                    IMPORTANCE_FOREGROUND_SERVICE);
+            uid3Listener.waitForValue(IMPORTANCE_FOREGROUND_SERVICE,
+                    IMPORTANCE_FOREGROUND_SERVICE);
+
+            CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
+                    STUB_PACKAGE_NAME, PACKAGE_NAME_APP1, 0, null);
+
+            CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
+                    PACKAGE_NAME_APP2, PACKAGE_NAME_APP1, 0, null);
+
+            CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
+                    PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null);
+
+            CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
+                    PACKAGE_NAME_APP1, PACKAGE_NAME_APP3, 0, null);
+
+            // Stop the foreground service
+            CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_STOP_FOREGROUND_SERVICE,
+                    PACKAGE_NAME_APP2, PACKAGE_NAME_APP2, 0, null);
+
+            // hide the alert
+            CommandReceiver.sendCommand(mContext,
+                    CommandReceiver.COMMAND_STOP_ALERT_SERVICE, STUB_PACKAGE_NAME,
+                    STUB_PACKAGE_NAME, 0, null);
+
+            // Check that the apps' proc state has fallen
+            uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
+            uid2Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
+            uid3Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
+        } finally {
+            stubListener.unregister();
+            uid1Listener.unregister();
+            uid2Listener.unregister();
+            uid3Listener.unregister();
+            uid1Watcher.finish();
+            uid2Watcher.finish();
+            uid3Watcher.finish();
+        }
+    }
 }
diff --git a/tests/app/src/android/app/cts/NotificationChannelTest.java b/tests/app/src/android/app/cts/NotificationChannelTest.java
index cff8dcb..6f3e1f1 100644
--- a/tests/app/src/android/app/cts/NotificationChannelTest.java
+++ b/tests/app/src/android/app/cts/NotificationChannelTest.java
@@ -18,6 +18,7 @@
 
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
 
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -60,11 +61,25 @@
         assertTrue(channel.getLightColor() == 0);
         assertTrue(channel.canBubble());
         assertFalse(channel.isImportanceLockedByOEM());
+        assertEquals(IMPORTANCE_UNSPECIFIED, channel.getOriginalImportance());
     }
 
     public void testWriteToParcel() {
         NotificationChannel channel =
                 new NotificationChannel("1", "one", IMPORTANCE_DEFAULT);
+        channel.setBypassDnd(true);
+        channel.setOriginalImportance(IMPORTANCE_HIGH);
+        channel.setShowBadge(false);
+        channel.setAllowBubbles(false);
+        channel.setGroup("a thing");
+        channel.setSound(Uri.fromParts("a", "b", "c"),
+                new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION_EVENT)
+                        .build());
+        channel.setLightColor(Color.RED);
+        channel.setDeleted(true);
+        channel.setFgServiceShown(true);
+        channel.setVibrationPattern(new long[] {299, 4562});
+        channel.setBlockableSystem(true);
         Parcel parcel = Parcel.obtain();
         channel.writeToParcel(parcel, 0);
         parcel.setDataPosition(0);
@@ -172,4 +187,17 @@
         channel.setImportanceLockedByOEM(true);
         assertTrue(channel.isImportanceLockedByOEM());
     }
+
+    public void testSystemBlockable() {
+        NotificationChannel channel = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
+        assertEquals(false, channel.isBlockableSystem());
+        channel.setBlockableSystem(true);
+        assertEquals(true, channel.isBlockableSystem());
+    }
+
+    public void testOriginalImportance() {
+        NotificationChannel channel = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
+        channel.setOriginalImportance(IMPORTANCE_HIGH);
+        assertEquals(IMPORTANCE_HIGH, channel.getOriginalImportance());
+    }
 }
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index 6e77fea..39addcd 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -18,6 +18,8 @@
 
 import static android.app.Notification.CATEGORY_CALL;
 import static android.app.Notification.FLAG_BUBBLE;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
 import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
@@ -91,9 +93,6 @@
 import android.util.Log;
 import android.widget.RemoteViews;
 
-import androidx.test.filters.FlakyTest;
-import androidx.test.InstrumentationRegistry;
-
 import com.android.compatibility.common.util.SystemUtil;
 
 import junit.framework.Assert;
@@ -111,6 +110,8 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+import androidx.test.InstrumentationRegistry;
+
 /* This tests NotificationListenerService together with NotificationManager, as you need to have
  * notifications to manipulate in order to test the listener service. */
 public class NotificationManagerTest extends AndroidTestCase {
@@ -119,6 +120,7 @@
     final String NOTIFICATION_CHANNEL_ID = "NotificationManagerTest";
 
     private static final String DELEGATOR = "com.android.test.notificationdelegator";
+    private static final String DELEGATE_POST_CLASS = DELEGATOR + ".NotificationDelegateAndPost";
     private static final String REVOKE_CLASS = DELEGATOR + ".NotificationRevoker";
     private static final int WAIT_TIME = 2000;
 
@@ -138,12 +140,21 @@
                 Context.NOTIFICATION_SERVICE);
         // clear the deck so that our getActiveNotifications results are predictable
         mNotificationManager.cancelAll();
+
+        assertEquals("Previous test left system in a bad state",
+                0, mNotificationManager.getActiveNotifications().length);
+
         mNotificationManager.createNotificationChannel(new NotificationChannel(
                 NOTIFICATION_CHANNEL_ID, "name", NotificationManager.IMPORTANCE_DEFAULT));
         mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
         mPackageManager = mContext.getPackageManager();
         mRuleIds = new ArrayList<>();
 
+        toggleNotificationPolicyAccess(mContext.getPackageName(),
+                InstrumentationRegistry.getInstrumentation(), true);
+        mNotificationManager.setInterruptionFilter(INTERRUPTION_FILTER_ALL);
+        toggleNotificationPolicyAccess(mContext.getPackageName(),
+                InstrumentationRegistry.getInstrumentation(), false);
         // delay between tests so notifications aren't dropped by the rate limiter
         try {
             Thread.sleep(500);
@@ -257,14 +268,13 @@
         return null;
     }
 
-
-    private StatusBarNotification findPostedNotification(int id) {
+    private StatusBarNotification findPostedNotification(int id, boolean all) {
         // notification is a bit asynchronous so it may take a few ms to appear in
         // getActiveNotifications()
         // we will check for it for up to 300ms before giving up
         StatusBarNotification n = null;
         for (int tries = 3; tries-- > 0; ) {
-            final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
+            final StatusBarNotification[] sbns = getActiveNotifications(all);
             for (StatusBarNotification sbn : sbns) {
                 Log.d(TAG, "Found " + sbn.getKey());
                 if (sbn.getId() == id) {
@@ -282,6 +292,14 @@
         return n;
     }
 
+    private StatusBarNotification[] getActiveNotifications(boolean all) {
+        if (all) {
+            return mListener.getActiveNotifications();
+        } else {
+            return mNotificationManager.getActiveNotifications();
+        }
+    }
+
     private PendingIntent getPendingIntent() {
         return PendingIntent.getActivity(
                 getContext(), 0, new Intent(getContext(), this.getClass()), 0);
@@ -1292,7 +1310,7 @@
         NotificationListenerService.Ranking outRanking =
                 new NotificationListenerService.Ranking();
 
-        StatusBarNotification sbn = findPostedNotification(notificationId);
+        StatusBarNotification sbn = findPostedNotification(notificationId, false);
 
         // check that the key and channel ids are the same in the ranking as the posted notification
         for (String key : rankingMap.getOrderedKeys()) {
@@ -1467,12 +1485,12 @@
         }
     }
 
-    public void testMediaStyle_empty() throws Exception {
+    public void testMediaStyle_empty() {
         Notification.MediaStyle style = new Notification.MediaStyle();
         assertNotNull(style);
     }
 
-    public void testMediaStyle() throws Exception {
+    public void testMediaStyle() {
         mNotificationManager.cancelAll();
         final int id = 99;
         MediaSession session = new MediaSession(getContext(), "media");
@@ -1499,7 +1517,7 @@
         }
     }
 
-    public void testInboxStyle() throws Exception {
+    public void testInboxStyle() {
         final int id = 100;
 
         final Notification notification =
@@ -1523,7 +1541,7 @@
         }
     }
 
-    public void testBigTextStyle() throws Exception {
+    public void testBigTextStyle() {
         final int id = 101;
 
         final Notification notification =
@@ -1549,7 +1567,7 @@
         }
     }
 
-    public void testBigPictureStyle() throws Exception {
+    public void testBigPictureStyle() {
         final int id = 102;
 
         final Notification notification =
@@ -1577,59 +1595,59 @@
     }
 
     public void testAutogrouping() throws Exception {
-        sendNotification(1, R.drawable.black);
-        sendNotification(2, R.drawable.blue);
-        sendNotification(3, R.drawable.yellow);
-        sendNotification(4, R.drawable.yellow);
+        sendNotification(801, R.drawable.black);
+        sendNotification(802, R.drawable.blue);
+        sendNotification(803, R.drawable.yellow);
+        sendNotification(804, R.drawable.yellow);
 
         assertNotificationCount(5);
         assertAllPostedNotificationsAutogrouped();
     }
 
     public void testAutogrouping_autogroupStaysUntilAllNotificationsCanceled() throws Exception {
-        sendNotification(1, R.drawable.black);
-        sendNotification(2, R.drawable.blue);
-        sendNotification(3, R.drawable.yellow);
-        sendNotification(4, R.drawable.yellow);
+        sendNotification(701, R.drawable.black);
+        sendNotification(702, R.drawable.blue);
+        sendNotification(703, R.drawable.yellow);
+        sendNotification(704, R.drawable.yellow);
 
         assertNotificationCount(5);
         assertAllPostedNotificationsAutogrouped();
 
         // Assert all notis stay in the same autogroup until all children are canceled
-        for (int i = 4; i > 1; i--) {
+        for (int i = 704; i > 701; i--) {
             cancelAndPoll(i);
-            assertNotificationCount(i);
+            assertNotificationCount(i - 700);
             assertAllPostedNotificationsAutogrouped();
         }
-        cancelAndPoll(1);
+        cancelAndPoll(701);
         assertNotificationCount(0);
     }
 
     public void testAutogrouping_autogroupStaysUntilAllNotificationsAddedToGroup()
             throws Exception {
         String newGroup = "new!";
-        sendNotification(1, R.drawable.black);
-        sendNotification(2, R.drawable.blue);
-        sendNotification(3, R.drawable.yellow);
-        sendNotification(4, R.drawable.yellow);
+        sendNotification(901, R.drawable.black);
+        sendNotification(902, R.drawable.blue);
+        sendNotification(903, R.drawable.yellow);
+        sendNotification(904, R.drawable.yellow);
 
         List<Integer> postedIds = new ArrayList<>();
-        postedIds.add(1);
-        postedIds.add(2);
-        postedIds.add(3);
-        postedIds.add(4);
+        postedIds.add(901);
+        postedIds.add(902);
+        postedIds.add(903);
+        postedIds.add(904);
 
         assertNotificationCount(5);
         assertAllPostedNotificationsAutogrouped();
 
         // Assert all notis stay in the same autogroup until all children are canceled
-        for (int i = 4; i > 1; i--) {
+        for (int i = 904; i > 901; i--) {
             sendNotification(i, newGroup, R.drawable.blue);
             postedIds.remove(postedIds.size() - 1);
             assertNotificationCount(5);
             assertOnlySomeNotificationsAutogrouped(postedIds);
         }
-        sendNotification(1, newGroup, R.drawable.blue);
+        sendNotification(901, newGroup, R.drawable.blue);
         assertNotificationCount(4); // no more autogroup summary
         postedIds.remove(0);
         assertOnlySomeNotificationsAutogrouped(postedIds);
@@ -1638,16 +1656,16 @@
     public void testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled()
         throws Exception {
         String newGroup = "new!";
-        sendNotification(10, R.drawable.black);
-        sendNotification(20, R.drawable.blue);
-        sendNotification(30, R.drawable.yellow);
-        sendNotification(40, R.drawable.yellow);
+        sendNotification(910, R.drawable.black);
+        sendNotification(920, R.drawable.blue);
+        sendNotification(930, R.drawable.yellow);
+        sendNotification(940, R.drawable.yellow);
 
         List<Integer> postedIds = new ArrayList<>();
-        postedIds.add(10);
-        postedIds.add(20);
-        postedIds.add(30);
-        postedIds.add(40);
+        postedIds.add(910);
+        postedIds.add(920);
+        postedIds.add(930);
+        postedIds.add(940);
 
         assertNotificationCount(5);
         assertAllPostedNotificationsAutogrouped();
@@ -1667,8 +1685,8 @@
 
         // send a new non-grouped notification. since the autogroup summary still exists,
         // the notification should be added to it
-        sendNotification(50, R.drawable.blue);
-        postedIds.add(50);
+        sendNotification(950, R.drawable.blue);
+        postedIds.add(950);
         try {
             Thread.sleep(200);
         } catch (InterruptedException ex) {
@@ -1813,7 +1831,6 @@
         assertExpectedDndState(INTERRUPTION_FILTER_ALL);
     }
 
-    @FlakyTest
     public void testSetAutomaticZenRuleState_multipleRules() throws Exception {
         if (mActivityManager.isLowRamDevice()) {
             return;
@@ -1932,7 +1949,7 @@
                         .build();
         mNotificationManager.notify(id, notification);
 
-        StatusBarNotification n = findPostedNotification(id);
+        StatusBarNotification n = findPostedNotification(id, false);
         assertNotNull(n);
         assertEquals(notification.fullScreenIntent, n.getNotification().fullScreenIntent);
     }
@@ -1986,7 +2003,77 @@
                 .build();
         mNotificationManager.notifyAsPackage(DELEGATOR, "tag", 0, n);
 
-        findPostedNotification(0);
+        assertNotNull(findPostedNotification(0, false));
+
+        final Intent revokeIntent = new Intent();
+        revokeIntent.setClassName(DELEGATOR, REVOKE_CLASS);
+        revokeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(revokeIntent);
+        Thread.sleep(1000);
+    }
+
+    public void testNotificationDelegate_grantAndPostAndCancel() throws Exception {
+        // grant this test permission to post
+        final Intent activityIntent = new Intent();
+        activityIntent.setPackage(DELEGATOR);
+        activityIntent.setAction(Intent.ACTION_MAIN);
+        activityIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+        activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        // wait for the activity to launch and finish
+        mContext.startActivity(activityIntent);
+        Thread.sleep(1000);
+
+        // send notification
+        Notification n = new Notification.Builder(mContext, "channel")
+                .setSmallIcon(android.R.id.icon)
+                .build();
+        mNotificationManager.notifyAsPackage(DELEGATOR, "toBeCanceled", 10000, n);
+
+        assertNotNull(findPostedNotification(10000, false));
+
+        mNotificationManager.cancelAsPackage(DELEGATOR, "toBeCanceled", 10000);
+
+        assertNull(findPostedNotification(10000, false));
+
+        final Intent revokeIntent = new Intent();
+        revokeIntent.setClassName(DELEGATOR, REVOKE_CLASS);
+        revokeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(revokeIntent);
+        Thread.sleep(1000);
+    }
+
+    public void testNotificationDelegate_cannotCancelNotificationsPostedByDelegator()
+            throws Exception {
+        if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
+            return;
+        }
+
+        toggleListenerAccess(TestNotificationListener.getId(),
+                InstrumentationRegistry.getInstrumentation(), true);
+        Thread.sleep(500); // wait for listener to be allowed
+
+        mListener = TestNotificationListener.getInstance();
+        assertNotNull(mListener);
+
+        // grant this test permission to post
+        final Intent activityIntent = new Intent();
+        activityIntent.setClassName(DELEGATOR, DELEGATE_POST_CLASS);
+        activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        mContext.startActivity(activityIntent);
+
+        assertNotNull(findPostedNotification(9, true));
+
+        try {
+            mNotificationManager.cancelAsPackage(DELEGATOR, null, 9);
+            fail ("Delegate should not be able to cancel notification they did not post");
+        } catch (SecurityException e) {
+            // yay
+        }
+
+        // double check that the notification does still exist
+        assertNotNull(findPostedNotification(9, true));
 
         final Intent revokeIntent = new Intent();
         revokeIntent.setClassName(DELEGATOR, REVOKE_CLASS);
@@ -2105,7 +2192,7 @@
                         .build();
         mNotificationManager.notify(id, notification);
 
-        StatusBarNotification n = findPostedNotification(id);
+        StatusBarNotification n = findPostedNotification(id, false);
         assertNotNull(n);
     }
 
@@ -2192,15 +2279,15 @@
 
         mListener = TestNotificationListener.getInstance();
         assertNotNull(mListener);
-        final int notificationId1 = 1;
-        final int notificationId2 = 2;
+        final int notificationId1 = 1003;
+        final int notificationId2 = 1004;
 
         sendNotification(notificationId1, R.drawable.black);
         sendNotification(notificationId2, R.drawable.black);
         Thread.sleep(500); // wait for notification listener to receive notification
 
-        StatusBarNotification sbn1 = findPostedNotification(notificationId1);
-        StatusBarNotification sbn2 = findPostedNotification(notificationId2);
+        StatusBarNotification sbn1 = findPostedNotification(notificationId1, false);
+        StatusBarNotification sbn2 = findPostedNotification(notificationId2, false);
         mListener.setNotificationsShown(new String[]{ sbn1.getKey() });
 
         toggleListenerAccess(TestNotificationListener.getId(),
@@ -2288,15 +2375,15 @@
 
         mListener = TestNotificationListener.getInstance();
         assertNotNull(mListener);
-        final int notificationId1 = 1;
-        final int notificationId2 = 2;
+        final int notificationId1 = 1001;
+        final int notificationId2 = 1002;
 
         sendNotification(notificationId1, R.drawable.black);
         sendNotification(notificationId2, R.drawable.black);
         Thread.sleep(500); // wait for notification listener to receive notification
 
-        StatusBarNotification sbn1 = findPostedNotification(notificationId1);
-        StatusBarNotification sbn2 = findPostedNotification(notificationId2);
+        StatusBarNotification sbn1 = findPostedNotification(notificationId1, false);
+        StatusBarNotification sbn2 = findPostedNotification(notificationId2, false);
         StatusBarNotification[] notifs =
                 mListener.getActiveNotifications(new String[]{ sbn2.getKey(), sbn1.getKey() });
         assertEquals(sbn2.getKey(), notifs[0].getKey());
@@ -2338,12 +2425,12 @@
 
         mListener = TestNotificationListener.getInstance();
         assertNotNull(mListener);
-        final int notificationId = 1;
+        final int notificationId = 1006;
 
         sendNotification(notificationId, R.drawable.black);
         Thread.sleep(500); // wait for notification listener to receive notification
 
-        StatusBarNotification sbn = findPostedNotification(notificationId);
+        StatusBarNotification sbn = findPostedNotification(notificationId, false);
 
         mListener.cancelNotification(sbn.getPackageName(), sbn.getTag(), sbn.getId());
         if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
@@ -2376,7 +2463,8 @@
                 lengthGreaterThanZero);
 
         String badNumberString = NotificationManager.Policy.priorityCategoriesToString(1234567);
-        assertNotNull("priorityCategories with a non-relevant int returns a string", oneString);
+        assertNotNull("priorityCategories with a non-relevant int returns a string",
+                badNumberString);
     }
 
     public void testNotificationManagerPolicy_prioritySendersToString() {
@@ -2629,7 +2717,7 @@
             }
 
             // Should be foreground now
-            a.sendBubble(1);
+            a.sendBubble(4000);
 
             boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
 
@@ -2643,7 +2731,7 @@
             homeHelper.goHome();
 
             // The notif should be allowed to update as a bubble
-            a.sendBubble(2);
+            a.sendBubble(4001);
 
             if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
                     true /* shouldExist */, shouldBeBubble)) {
@@ -2654,7 +2742,7 @@
             cancelAndPoll(BUBBLE_NOTIF_ID);
 
             // Send it again when not foreground, this should not be a bubble & just be a notif
-            a.sendBubble(3);
+            a.sendBubble(4002);
             if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
                     true /* shouldExist */, false /* shouldBeBubble */)) {
                 fail("couldn't find posted notification with id=" + BUBBLE_NOTIF_ID
@@ -2669,7 +2757,7 @@
         }
     }
 
-    public void testNotificationManagerBubblePolicy_noFlag_notEmbeddable() throws Exception {
+    public void testNotificationManagerBubblePolicy_noFlag_notEmbeddable() {
         Person person = new Person.Builder()
                 .setName("bubblebot")
                 .build();
@@ -2705,7 +2793,7 @@
         sendAndVerifyBubble(1, nb, metadataBuilder.build(), false);
     }
 
-    public void testNotificationManagerBubblePolicy_noFlag_notDocumentLaunchModeAlways() throws Exception {
+    public void testNotificationManagerBubblePolicy_noFlag_notDocumentLaunchModeAlways() {
         Person person = new Person.Builder()
                 .setName("bubblebot")
                 .build();
@@ -2740,4 +2828,24 @@
 
         sendAndVerifyBubble(1, nb, metadataBuilder.build(), false);
     }
+
+    public void testOriginalChannelImportance() {
+        NotificationChannel channel = new NotificationChannel(
+                "my channel", "my channel", IMPORTANCE_HIGH);
+
+        mNotificationManager.createNotificationChannel(channel);
+
+        NotificationChannel actual = mNotificationManager.getNotificationChannel(channel.getId());
+        assertEquals(IMPORTANCE_HIGH, actual.getImportance());
+        assertEquals(IMPORTANCE_HIGH, actual.getOriginalImportance());
+
+        // Apps are allowed to downgrade channel importance if the user has not changed any
+        // fields on this channel yet.
+        channel.setImportance(IMPORTANCE_DEFAULT);
+        mNotificationManager.createNotificationChannel(channel);
+
+        actual = mNotificationManager.getNotificationChannel(channel.getId());
+        assertEquals(IMPORTANCE_DEFAULT, actual.getImportance());
+        assertEquals(IMPORTANCE_HIGH, actual.getOriginalImportance());
+    }
 }
diff --git a/tests/app/src/android/app/cts/WallpaperManagerTest.java b/tests/app/src/android/app/cts/WallpaperManagerTest.java
index fe5f183..3b9934a 100644
--- a/tests/app/src/android/app/cts/WallpaperManagerTest.java
+++ b/tests/app/src/android/app/cts/WallpaperManagerTest.java
@@ -16,6 +16,7 @@
 
 package android.app.cts;
 
+import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.nullable;
@@ -72,6 +73,7 @@
         MockitoAnnotations.initMocks(this);
         mContext = InstrumentationRegistry.getTargetContext();
         mWallpaperManager = WallpaperManager.getInstance(mContext);
+        assumeTrue("Device does not support wallpapers", mWallpaperManager.isWallpaperSupported());
         final HandlerThread handlerThread = new HandlerThread("TestCallbacks");
         handlerThread.start();
         mHandler = new Handler(handlerThread.getLooper());
@@ -477,4 +479,4 @@
         public void onColorsChanged(WallpaperColors colors, int which) {
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/aslr/OWNERS b/tests/aslr/OWNERS
new file mode 100644
index 0000000..94522e3
--- /dev/null
+++ b/tests/aslr/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 36824
+include /tests/tests/security/OWNERS
diff --git a/tests/attentionservice/OWNERS b/tests/attentionservice/OWNERS
new file mode 100644
index 0000000..dcfd7dd
--- /dev/null
+++ b/tests/attentionservice/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 557553
+aarli@google.com
+asalo@google.com
+eejiang@google.com
+payamp@google.com
diff --git a/tests/autofillservice/res/layout/login_with_strings_activity.xml b/tests/autofillservice/res/layout/login_with_strings_activity.xml
index 972f53f..0837129 100644
--- a/tests/autofillservice/res/layout/login_with_strings_activity.xml
+++ b/tests/autofillservice/res/layout/login_with_strings_activity.xml
@@ -38,7 +38,8 @@
         <EditText
             android:id="@+id/username"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content" />
+            android:layout_height="wrap_content"
+            android:hint="@string/username_hint" />
     </LinearLayout>
 
     <LinearLayout
@@ -56,7 +57,8 @@
             android:id="@+id/password"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:inputType="textPassword"/>
+            android:inputType="textPassword"
+            android:hint="@string/password_hint" />
     </LinearLayout>
 
     <LinearLayout
diff --git a/tests/autofillservice/res/values/strings.xml b/tests/autofillservice/res/values/strings.xml
index 785c7a5..54ce11f 100644
--- a/tests/autofillservice/res/values/strings.xml
+++ b/tests/autofillservice/res/values/strings.xml
@@ -29,4 +29,7 @@
     <string name="username_string">Username</string>
     <string name="password_string">Password</string>
 
+    <string name="username_hint">Hint for username</string>
+    <string name="password_hint">Hint for password</string>
+
 </resources>
\ No newline at end of file
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java b/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java
index 4aa0858..5446b30 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java
@@ -303,10 +303,7 @@
         startAutoFill(mSpinner);
 
         // Autofill it.
-        mUiBot.selectDataset("dataset");
-
-        // TODO(b/137856201): Fix race condition in getSelectedItemPosition().
-        Thread.sleep(1000);
+        mUiBot.selectDatasetSync("dataset");
 
         if (expectAutoFill) {
             // Check the results.
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DatasetFilteringTest.java b/tests/autofillservice/src/android/autofillservice/cts/DatasetFilteringTest.java
index 86a8c7d..10df1cd 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DatasetFilteringTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DatasetFilteringTest.java
@@ -25,6 +25,8 @@
 import static com.android.cts.mockime.ImeEventStreamTestUtils.expectCommand;
 import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
 
+import static org.junit.Assume.assumeTrue;
+
 import android.autofillservice.cts.CannedFillResponse.CannedDataset;
 import android.content.IntentSender;
 import android.os.Process;
@@ -172,12 +174,13 @@
 
     @Test
     public void testFilter_usingKeyboard() throws Exception {
+        final MockImeSession mockImeSession = sMockImeSessionRule.getMockImeSession();
+        assumeTrue("MockIME not available", mockImeSession != null);
+
         final String aa = "Two A's";
         final String ab = "A and B";
         final String b = "Only B";
 
-        final MockImeSession mockImeSession = sMockImeSessionRule.getMockImeSession();
-
         enableService();
 
         // Set expectations.
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java b/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java
index 6e0914d..238ab45 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java
@@ -26,6 +26,7 @@
 import static android.autofillservice.cts.Helper.assertFillEventForAuthenticationSelected;
 import static android.autofillservice.cts.Helper.assertFillEventForDatasetAuthenticationSelected;
 import static android.autofillservice.cts.Helper.assertFillEventForDatasetSelected;
+import static android.autofillservice.cts.Helper.assertFillEventForDatasetShown;
 import static android.autofillservice.cts.Helper.assertFillEventForSaveShown;
 import static android.autofillservice.cts.Helper.assertNoDeprecatedClientState;
 import static android.autofillservice.cts.Helper.findAutofillIdByResourceId;
@@ -104,8 +105,9 @@
         mActivity.assertAutoFilled();
 
         // Verify fill selection
-        final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
-        assertFillEventForDatasetAuthenticationSelected(events.get(0), "name",
+        final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+        assertFillEventForDatasetShown(events.get(0), "clientStateKey", "clientStateValue");
+        assertFillEventForDatasetAuthenticationSelected(events.get(1), "name",
                 "clientStateKey", "clientStateValue");
     }
 
@@ -141,11 +143,13 @@
         mUiBot.assertDatasets("dataset");
 
         // Verify fill selection
-        final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1);
+        final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(3);
         assertDeprecatedClientState(selection, "clientStateKey", "clientStateValue");
         List<Event> events = selection.getEvents();
-        assertFillEventForAuthenticationSelected(events.get(0), NULL_DATASET_ID,
+        assertFillEventForDatasetShown(events.get(0), "clientStateKey", "clientStateValue");
+        assertFillEventForAuthenticationSelected(events.get(1), NULL_DATASET_ID,
                 "clientStateKey", "clientStateValue");
+        assertFillEventForDatasetShown(events.get(2), "clientStateKey", "clientStateValue");
     }
 
     @Test
@@ -174,10 +178,11 @@
 
         {
             // Verify fill selection
-            final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1);
+            final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(2);
             assertDeprecatedClientState(selection, "clientStateKey", "Value1");
             final List<Event> events = selection.getEvents();
-            assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID,
+            assertFillEventForDatasetShown(events.get(0), "clientStateKey", "Value1");
+            assertFillEventForDatasetSelected(events.get(1), NULL_DATASET_ID,
                     "clientStateKey", "Value1");
         }
 
@@ -210,10 +215,11 @@
 
         {
             // Verify fill selection
-            final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1);
+            final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(2);
             assertDeprecatedClientState(selection, "clientStateKey", "Value2");
             final List<Event> events = selection.getEvents();
-            assertFillEventForDatasetSelected(events.get(0), "name3",
+            assertFillEventForDatasetShown(events.get(0), "clientStateKey", "Value2");
+            assertFillEventForDatasetSelected(events.get(1), "name3",
                     "clientStateKey", "Value2");
         }
 
@@ -223,13 +229,15 @@
 
         {
             // Verify fill selection
-            final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(2);
+            final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(4);
             assertDeprecatedClientState(selection, "clientStateKey", "Value2");
 
             final List<Event> events = selection.getEvents();
-            assertFillEventForDatasetSelected(events.get(0), "name3",
+            assertFillEventForDatasetShown(events.get(0), "clientStateKey", "Value2");
+            assertFillEventForDatasetSelected(events.get(1), "name3",
                     "clientStateKey", "Value2");
-            assertFillEventForSaveShown(events.get(1), NULL_DATASET_ID,
+            assertFillEventForDatasetShown(events.get(2), "clientStateKey", "Value2");
+            assertFillEventForSaveShown(events.get(3), NULL_DATASET_ID,
                     "clientStateKey", "Value2");
         }
     }
@@ -255,10 +263,11 @@
 
         {
             // Verify fill selection
-            final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1);
+            final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(2);
             assertNoDeprecatedClientState(selection);
             final List<Event> events = selection.getEvents();
-            assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+            assertFillEventForDatasetShown(events.get(0));
+            assertFillEventForDatasetSelected(events.get(1), NULL_DATASET_ID);
         }
 
         // Second request
@@ -292,10 +301,11 @@
 
         {
             // Verify fill selection
-            final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1);
+            final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(2);
             assertNoDeprecatedClientState(selection);
             final List<Event> events = selection.getEvents();
-            assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+            assertFillEventForDatasetShown(events.get(0));
+            assertFillEventForDatasetSelected(events.get(1), NULL_DATASET_ID);
         }
 
         // Second request
@@ -329,10 +339,11 @@
 
         {
             // Verify fill selection
-            final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1);
+            final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(2);
             assertNoDeprecatedClientState(selection);
             final List<Event> events = selection.getEvents();
-            assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+            assertFillEventForDatasetShown(events.get(0));
+            assertFillEventForDatasetSelected(events.get(1), NULL_DATASET_ID);
         }
 
         // Second request
@@ -398,8 +409,9 @@
         sReplier.getNextFillRequest();
 
         // Verify fill selection for Activity B
-        final FillEventHistory selectionB = InstrumentedAutoFillService.getFillEventHistory(0);
+        final FillEventHistory selectionB = InstrumentedAutoFillService.getFillEventHistory(1);
         assertDeprecatedClientState(selectionB, "activity", "B");
+        assertFillEventForDatasetShown(selectionB.getEvents().get(0), "activity", "B");
 
         // Now switch back to A...
         mUiBot.pressBack(); // dismiss autofill
@@ -424,8 +436,9 @@
         sReplier.getNextSaveRequest();
 
         // Finally, make sure history is right
-        final FillEventHistory finalSelection = InstrumentedAutoFillService.getFillEventHistory(0);
+        final FillEventHistory finalSelection = InstrumentedAutoFillService.getFillEventHistory(1);
         assertDeprecatedClientState(finalSelection, "activity", "B");
+        assertFillEventForDatasetShown(finalSelection.getEvents().get(0), "activity", "B");
     }
 
     @Test
@@ -498,11 +511,12 @@
         mActivity.assertAutoFilled();
         // Verify fill history
         {
-            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
-            assertFillEventForDatasetSelected(events.get(0), "id1");
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+            assertFillEventForDatasetShown(events.get(0));
+            assertFillEventForDatasetSelected(events.get(1), "id1");
         }
 
-        // Trigger 2st autofill request (which will clear the fill event history)
+        // Trigger 2nd autofill request (which will clear the fill event history)
         sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
                 new CannedDataset.Builder()
                         .setId("id2")
@@ -518,8 +532,9 @@
         mActivity.assertAutoFilled();
         // Verify fill history
         {
-            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
-            assertFillEventForDatasetSelected(events.get(0), "id2");
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+            assertFillEventForDatasetShown(events.get(0));
+            assertFillEventForDatasetSelected(events.get(1), "id2");
         }
 
         // Finish the context by login in
@@ -530,9 +545,9 @@
 
         {
             // Verify fill history
-            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
-
-            assertFillEventForDatasetSelected(events.get(0), "id2");
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+            assertFillEventForDatasetShown(events.get(0));
+            assertFillEventForDatasetSelected(events.get(1), "id2");
         }
     }
 
@@ -565,8 +580,9 @@
 
         // Verify dataset selection
         {
-            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
-            assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+            assertFillEventForDatasetShown(events.get(0));
+            assertFillEventForDatasetSelected(events.get(1), NULL_DATASET_ID);
         }
 
         // Finish the context by login in
@@ -580,8 +596,10 @@
 
         // ...and check again
         {
-            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
-            assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(3);
+            assertFillEventForDatasetShown(events.get(0));
+            assertFillEventForDatasetSelected(events.get(1), NULL_DATASET_ID);
+            assertFillEventForDatasetShown(events.get(2));
         }
     }
 
@@ -616,8 +634,9 @@
 
         // Verify dataset selection
         {
-            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
-            assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+            assertFillEventForDatasetShown(events.get(0));
+            assertFillEventForDatasetSelected(events.get(1), NULL_DATASET_ID);
         }
 
         // Finish the context by login in
@@ -630,10 +649,11 @@
 
         // ...and check again
         {
-            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
-            assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(3);
+            assertFillEventForDatasetShown(events.get(0));
+            assertFillEventForDatasetSelected(events.get(1), NULL_DATASET_ID);
 
-            FillEventHistory.Event event2 = events.get(1);
+            FillEventHistory.Event event2 = events.get(2);
             assertThat(event2.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
             assertThat(event2.getDatasetId()).isNull();
             assertThat(event2.getClientState()).isNull();
@@ -678,8 +698,9 @@
 
         // Verify dataset selection
         {
-            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
-            assertFillEventForDatasetSelected(events.get(0), "id2");
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+            assertFillEventForDatasetShown(events.get(0));
+            assertFillEventForDatasetSelected(events.get(1), "id2");
         }
 
         // Finish the context by login in
@@ -692,10 +713,11 @@
 
         // ...and check again
         {
-            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
-            assertFillEventForDatasetSelected(events.get(0), "id2");
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(3);
+            assertFillEventForDatasetShown(events.get(0));
+            assertFillEventForDatasetSelected(events.get(1), "id2");
 
-            final FillEventHistory.Event event2 = events.get(1);
+            final FillEventHistory.Event event2 = events.get(2);
             assertThat(event2.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
             assertThat(event2.getDatasetId()).isNull();
             assertThat(event2.getClientState()).isNull();
@@ -738,8 +760,10 @@
         mUiBot.assertDatasets("dataset1", "dataset2");
 
         // Verify history
-        InstrumentedAutoFillService.getFillEventHistory(0);
-
+        {
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+            assertFillEventForDatasetShown(events.get(0));
+        }
         // Enter values not present at the datasets
         mActivity.onUsername((v) -> v.setText("USERNAME"));
         mActivity.onPassword((v) -> v.setText("USERNAME"));
@@ -752,8 +776,9 @@
 
         // Verify history again
         {
-            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
-            final Event event = events.get(0);
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+            assertFillEventForDatasetShown(events.get(0));
+            final Event event = events.get(1);
             assertThat(event.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
             assertThat(event.getDatasetId()).isNull();
             assertThat(event.getClientState()).isNull();
@@ -798,8 +823,9 @@
 
         // Verify dataset selection
         {
-            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
-            assertFillEventForDatasetSelected(events.get(0), "id1");
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+            assertFillEventForDatasetShown(events.get(0));
+            assertFillEventForDatasetSelected(events.get(1), "id1");
         }
 
         // Finish the context by login in
@@ -813,10 +839,12 @@
 
         // ...and check again
         {
-            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
-            assertFillEventForDatasetSelected(events.get(0), "id1");
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(4);
+            assertFillEventForDatasetShown(events.get(0));
+            assertFillEventForDatasetSelected(events.get(1), "id1");
 
-            final FillEventHistory.Event event2 = events.get(1);
+            assertFillEventForDatasetShown(events.get(2));
+            final FillEventHistory.Event event2 = events.get(3);
             assertThat(event2.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
             assertThat(event2.getDatasetId()).isNull();
             assertThat(event2.getClientState()).isNull();
@@ -865,8 +893,9 @@
         mActivity.assertAutoFilled();
         {
             // Verify fill history
-            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
-            assertFillEventForDatasetSelected(events.get(0), "id1");
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+            assertFillEventForDatasetShown(events.get(0));
+            assertFillEventForDatasetSelected(events.get(1), "id1");
         }
 
         // Autofill password
@@ -878,10 +907,12 @@
 
         {
             // Verify fill history
-            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(4);
 
-            assertFillEventForDatasetSelected(events.get(0), "id1");
-            assertFillEventForDatasetSelected(events.get(1), "id2");
+            assertFillEventForDatasetShown(events.get(0));
+            assertFillEventForDatasetSelected(events.get(1), "id1");
+            assertFillEventForDatasetShown(events.get(2));
+            assertFillEventForDatasetSelected(events.get(3), "id2");
         }
 
         // Finish the context by login in
@@ -894,12 +925,15 @@
 
         {
             // Verify fill history
-            final List<Event> events = InstrumentedAutoFillService.getFillEvents(3);
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(6);
 
-            assertFillEventForDatasetSelected(events.get(0), "id1");
-            assertFillEventForDatasetSelected(events.get(1), "id2");
+            assertFillEventForDatasetShown(events.get(0));
+            assertFillEventForDatasetSelected(events.get(1), "id1");
+            assertFillEventForDatasetShown(events.get(2));
+            assertFillEventForDatasetSelected(events.get(3), "id2");
 
-            final FillEventHistory.Event event3 = events.get(2);
+            assertFillEventForDatasetShown(events.get(4));
+            final FillEventHistory.Event event3 = events.get(5);
             assertThat(event3.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
             assertThat(event3.getDatasetId()).isNull();
             assertThat(event3.getClientState()).isNull();
@@ -945,8 +979,9 @@
         mActivity.assertAutoFilled();
         {
             // Verify fill history
-            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
-            assertFillEventForDatasetSelected(events.get(0), "id1");
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+            assertFillEventForDatasetShown(events.get(0));
+            assertFillEventForDatasetSelected(events.get(1), "id1");
         }
 
         // Autofill password
@@ -958,10 +993,12 @@
 
         {
             // Verify fill history
-            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(4);
 
-            assertFillEventForDatasetSelected(events.get(0), "id1");
-            assertFillEventForDatasetSelected(events.get(1), "id2");
+            assertFillEventForDatasetShown(events.get(0));
+            assertFillEventForDatasetSelected(events.get(1), "id1");
+            assertFillEventForDatasetShown(events.get(2));
+            assertFillEventForDatasetSelected(events.get(3), "id2");
         }
 
         // Finish the context by login in
@@ -972,12 +1009,14 @@
 
         {
             // Verify fill history
-            final List<Event> events = InstrumentedAutoFillService.getFillEvents(3);
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(5);
 
-            assertFillEventForDatasetSelected(events.get(0), "id1");
-            assertFillEventForDatasetSelected(events.get(1), "id2");
+            assertFillEventForDatasetShown(events.get(0));
+            assertFillEventForDatasetSelected(events.get(1), "id1");
+            assertFillEventForDatasetShown(events.get(2));
+            assertFillEventForDatasetSelected(events.get(3), "id2");
 
-            final FillEventHistory.Event event3 = events.get(2);
+            final FillEventHistory.Event event3 = events.get(4);
             assertThat(event3.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
             assertThat(event3.getDatasetId()).isNull();
             assertThat(event3.getClientState()).isNull();
@@ -1018,11 +1057,12 @@
 
         // Verify dataset selection
         {
-            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
-            assertFillEventForDatasetSelected(events.get(0), "id1");
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+            assertFillEventForDatasetShown(events.get(0));
+            assertFillEventForDatasetSelected(events.get(1), "id1");
         }
 
-        // Change the fields to different values from datasets
+        // Change the fields to different values from0 datasets
         mActivity.onUsername((v) -> v.setText("USERNAME"));
         mActivity.onPassword((v) -> v.setText("USERNAME"));
 
@@ -1037,17 +1077,19 @@
 
         // ...and check again
         {
-            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
-            assertFillEventForDatasetSelected(events.get(0), "id1");
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(4);
+            assertFillEventForDatasetShown(events.get(0));
+            assertFillEventForDatasetSelected(events.get(1), "id1");
+            assertFillEventForDatasetShown(events.get(2));
 
-            FillEventHistory.Event event2 = events.get(1);
-            assertThat(event2.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
-            assertThat(event2.getDatasetId()).isNull();
-            assertThat(event2.getClientState()).isNull();
-            assertThat(event2.getSelectedDatasetIds()).containsExactly("id1");
-            assertThat(event2.getIgnoredDatasetIds()).isEmpty();
-            assertThat(event2.getChangedFields()).isEmpty();
-            assertThat(event2.getManuallyEnteredField()).isEmpty();
+            FillEventHistory.Event event4 = events.get(3);
+            assertThat(event4.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
+            assertThat(event4.getDatasetId()).isNull();
+            assertThat(event4.getClientState()).isNull();
+            assertThat(event4.getSelectedDatasetIds()).containsExactly("id1");
+            assertThat(event4.getIgnoredDatasetIds()).isEmpty();
+            assertThat(event4.getChangedFields()).isEmpty();
+            assertThat(event4.getManuallyEnteredField()).isEmpty();
         }
     }
 
@@ -1081,7 +1123,10 @@
         mUiBot.assertDatasets("dataset1", "dataset2");
 
         // Verify history
-        InstrumentedAutoFillService.getFillEventHistory(0);
+        {
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+            assertFillEventForDatasetShown(events.get(0));
+        }
 
         // Enter values present at the datasets
         mActivity.onUsername((v) -> v.setText(BACKDOOR_USERNAME));
@@ -1095,8 +1140,9 @@
 
         // Verify history
         {
-            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
-            FillEventHistory.Event event = events.get(0);
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+            assertFillEventForDatasetShown(events.get(0));
+            FillEventHistory.Event event = events.get(1);
             assertThat(event.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
             assertThat(event.getDatasetId()).isNull();
             assertThat(event.getClientState()).isNull();
@@ -1153,7 +1199,10 @@
         mUiBot.assertDatasets("dataset1", "dataset2", "dataset3");
 
         // Verify history
-        InstrumentedAutoFillService.getFillEventHistory(0);
+        {
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+            assertFillEventForDatasetShown(events.get(0));
+        }
 
         // Enter values present at the datasets
         mActivity.onUsername((v) -> v.setText(BACKDOOR_USERNAME));
@@ -1167,9 +1216,10 @@
 
         // Verify history
         {
-            final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+            final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+            assertFillEventForDatasetShown(events.get(0));
 
-            final FillEventHistory.Event event = events.get(0);
+            final FillEventHistory.Event event = events.get(1);
             assertThat(event.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
             assertThat(event.getDatasetId()).isNull();
             assertThat(event.getClientState()).isNull();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
index 06c0e00..f0bfd39 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
@@ -21,6 +21,7 @@
 import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
 import static android.service.autofill.FillEventHistory.Event.TYPE_AUTHENTICATION_SELECTED;
 import static android.service.autofill.FillEventHistory.Event.TYPE_CONTEXT_COMMITTED;
+import static android.service.autofill.FillEventHistory.Event.TYPE_DATASETS_SHOWN;
 import static android.service.autofill.FillEventHistory.Event.TYPE_DATASET_AUTHENTICATION_SELECTED;
 import static android.service.autofill.FillEventHistory.Event.TYPE_DATASET_SELECTED;
 import static android.service.autofill.FillEventHistory.Event.TYPE_SAVE_SHOWN;
@@ -548,7 +549,7 @@
     /**
      * Asserts the values of a text-based node whose string come from resoruces.
      */
-    public static ViewNode assertTextFromResouces(AssistStructure structure, String resourceId,
+    public static ViewNode assertTextFromResources(AssistStructure structure, String resourceId,
             String expectedValue, boolean isAutofillable, String expectedTextIdEntry) {
         final ViewNode node = findNodeByResourceId(structure, resourceId);
         assertText(node, expectedValue, isAutofillable);
@@ -556,6 +557,14 @@
         return node;
     }
 
+    public static ViewNode assertHintFromResources(AssistStructure structure, String resourceId,
+            String expectedValue, String expectedHintIdEntry) {
+        final ViewNode node = findNodeByResourceId(structure, resourceId);
+        assertThat(node.getHint()).isEqualTo(expectedValue);
+        assertThat(node.getHintIdEntry()).isEqualTo(expectedHintIdEntry);
+        return node;
+    }
+
     private static void assertText(ViewNode node, String expectedValue, boolean isAutofillable) {
         assertWithMessage("wrong text on %s", node.getAutofillId()).that(node.getText().toString())
                 .isEqualTo(expectedValue);
@@ -1150,7 +1159,7 @@
      * @param value the only value expected in the client state bundle
      */
     public static void assertFillEventForSaveShown(@NonNull FillEventHistory.Event event,
-            @NonNull String datasetId, @NonNull String key, @NonNull String value) {
+            @Nullable String datasetId, @NonNull String key, @NonNull String value) {
         assertFillEvent(event, TYPE_SAVE_SHOWN, datasetId, key, value, null);
     }
 
@@ -1162,12 +1171,35 @@
      * @param datasetId dataset set id expected in the event
      */
     public static void assertFillEventForSaveShown(@NonNull FillEventHistory.Event event,
-            @NonNull String datasetId) {
+            @Nullable String datasetId) {
         assertFillEvent(event, TYPE_SAVE_SHOWN, datasetId, null, null, null);
     }
 
     /**
      * Asserts the content of a
+     * {@link android.service.autofill.FillEventHistory.Event#TYPE_DATASETS_SHOWN} event.
+     *
+     * @param event event to be asserted
+     * @param key the only key expected in the client state bundle
+     * @param value the only value expected in the client state bundle
+     */
+    public static void assertFillEventForDatasetShown(@NonNull FillEventHistory.Event event,
+            @NonNull String key, @NonNull String value) {
+        assertFillEvent(event, TYPE_DATASETS_SHOWN, NULL_DATASET_ID, key, value, null);
+    }
+
+    /**
+     * Asserts the content of a
+     * {@link android.service.autofill.FillEventHistory.Event#TYPE_DATASETS_SHOWN} event.
+     *
+     * @param event event to be asserted
+     */
+    public static void assertFillEventForDatasetShown(@NonNull FillEventHistory.Event event) {
+        assertFillEvent(event, TYPE_DATASETS_SHOWN, NULL_DATASET_ID, null, null, null);
+    }
+
+    /**
+     * Asserts the content of a
      * {@link android.service.autofill.FillEventHistory.Event#TYPE_DATASET_AUTHENTICATION_SELECTED}
      * event.
      *
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
index efd001f..ace3d6f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
@@ -140,6 +140,7 @@
             final Intent intent = new Intent(this, WelcomeActivity.class);
             final String message = getWelcomeMessage(username);
             intent.putExtra(WelcomeActivity.EXTRA_MESSAGE, message);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             setLoginMessage(message);
             startActivity(intent);
             finish();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
index 0dbfc73..e8f7fb9 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -519,10 +519,6 @@
 
     @Test
     public void testDatasetPickerPosition() throws Exception {
-        // TODO(b/75281985): currently disabled because the screenshot contains elements external to
-        // the activity that can change (for example, clock), which causes flakiness to the test.
-        final boolean compareBitmaps = false;
-
         final boolean pickerAndViewBoundsMatches = !isAutofillWindowFullScreen(mContext);
 
         // Set service.
@@ -544,7 +540,7 @@
         sReplier.getNextFillRequest();
         callback.assertUiShownEvent(username);
         final Rect usernamePickerBoundaries1 = mUiBot.assertDatasets("DUDE").getVisibleBounds();
-        final Bitmap usernameScreenshot1 = compareBitmaps ? mUiBot.takeScreenshot() : null;
+        final Bitmap usernameScreenshot1 = mUiBot.takeScreenshot(mActivity);
         Log.v(TAG,
                 "Username1 at " + usernameBoundaries1 + "; picker at " + usernamePickerBoundaries1);
         // TODO(b/37566627): assertions below might be too aggressive - use range instead?
@@ -558,7 +554,7 @@
         callback.assertUiHiddenEvent(username);
         callback.assertUiShownEvent(password);
         final Rect passwordPickerBoundaries1 = mUiBot.assertDatasets("SWEET").getVisibleBounds();
-        final Bitmap passwordScreenshot1 = compareBitmaps ? mUiBot.takeScreenshot() : null;
+        final Bitmap passwordScreenshot1 = mUiBot.takeScreenshot(mActivity);
         Log.v(TAG,
                 "Password1 at " + passwordBoundaries1 + "; picker at " + passwordPickerBoundaries1);
         // TODO(b/37566627): assertions below might be too aggressive - use range instead?
@@ -572,7 +568,7 @@
         callback.assertUiHiddenEvent(password);
         callback.assertUiShownEvent(username);
         final Rect usernamePickerBoundaries2 = mUiBot.assertDatasets("DUDE").getVisibleBounds();
-        final Bitmap usernameScreenshot2 = compareBitmaps ? mUiBot.takeScreenshot() : null;
+        final Bitmap usernameScreenshot2 = mUiBot.takeScreenshot(mActivity);
         Log.v(TAG,
                 "Username2 at " + usernameBoundaries2 + "; picker at " + usernamePickerBoundaries2);
 
@@ -581,7 +577,7 @@
         callback.assertUiHiddenEvent(username);
         callback.assertUiShownEvent(password);
         final Rect passwordPickerBoundaries2 = mUiBot.assertDatasets("SWEET").getVisibleBounds();
-        final Bitmap passwordScreenshot2 = compareBitmaps ? mUiBot.takeScreenshot() : null;
+        final Bitmap passwordScreenshot2 = mUiBot.takeScreenshot(mActivity);
         Log.v(TAG,
                 "Password2 at " + passwordBoundaries2 + "; picker at " + passwordPickerBoundaries2);
 
@@ -589,15 +585,12 @@
         // ... for username
         assertThat(usernameBoundaries2).isEqualTo(usernameBoundaries1);
         assertThat(usernamePickerBoundaries2).isEqualTo(usernamePickerBoundaries1);
-        if (compareBitmaps) {
-            Helper.assertBitmapsAreSame("username", usernameScreenshot1, usernameScreenshot2);
-        }
+        Helper.assertBitmapsAreSame("username", usernameScreenshot1, usernameScreenshot2);
+
         // ... for password
         assertThat(passwordBoundaries2).isEqualTo(passwordBoundaries1);
         assertThat(passwordPickerBoundaries2).isEqualTo(passwordPickerBoundaries1);
-        if (compareBitmaps) {
-            Helper.assertBitmapsAreSame("password", passwordScreenshot1, passwordScreenshot2);
-        }
+        Helper.assertBitmapsAreSame("password", passwordScreenshot1, passwordScreenshot2);
 
         // Final sanity check
         callback.assertNumberUnhandledEvents(0);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginWithStringsActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginWithStringsActivityTest.java
index e612161..d702052 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginWithStringsActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginWithStringsActivityTest.java
@@ -20,8 +20,9 @@
 import static android.autofillservice.cts.Helper.ID_PASSWORD_LABEL;
 import static android.autofillservice.cts.Helper.ID_USERNAME;
 import static android.autofillservice.cts.Helper.ID_USERNAME_LABEL;
+import static android.autofillservice.cts.Helper.assertHintFromResources;
 import static android.autofillservice.cts.Helper.assertTextAndValue;
-import static android.autofillservice.cts.Helper.assertTextFromResouces;
+import static android.autofillservice.cts.Helper.assertTextFromResources;
 import static android.autofillservice.cts.Helper.assertTextIsSanitized;
 import static android.autofillservice.cts.Helper.findNodeByResourceId;
 import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilConnected;
@@ -92,11 +93,17 @@
         assertTextIsSanitized(fillRequest.structure, ID_PASSWORD);
 
         // Make sure labels were not sanitized
-        assertTextFromResouces(fillRequest.structure, ID_USERNAME_LABEL, "Username", false,
+        assertTextFromResources(fillRequest.structure, ID_USERNAME_LABEL, "Username", false,
                 "username_string");
-        assertTextFromResouces(fillRequest.structure, ID_PASSWORD_LABEL, "Password", false,
+        assertTextFromResources(fillRequest.structure, ID_PASSWORD_LABEL, "Password", false,
                 "password_string");
 
+        // Check text hints
+        assertHintFromResources(fillRequest.structure, ID_USERNAME, "Hint for username",
+                "username_hint");
+        assertHintFromResources(fillRequest.structure, ID_PASSWORD, "Hint for password",
+                "password_hint");
+
         // Auto-fill it.
         mUiBot.selectDataset("The Dude");
 
@@ -130,11 +137,17 @@
         assertTextAndValue(password, "dude");
 
         // Make sure labels were not sanitized
-        assertTextFromResouces(saveRequest.structure, ID_USERNAME_LABEL, "Username", false,
+        assertTextFromResources(saveRequest.structure, ID_USERNAME_LABEL, "Username", false,
                 "username_string");
-        assertTextFromResouces(saveRequest.structure, ID_PASSWORD_LABEL, "Password", false,
+        assertTextFromResources(saveRequest.structure, ID_PASSWORD_LABEL, "Password", false,
                 "password_string");
 
+        // Check text hints
+        assertHintFromResources(fillRequest.structure, ID_USERNAME, "Hint for username",
+                "username_hint");
+        assertHintFromResources(fillRequest.structure, ID_PASSWORD, "Hint for password",
+                "password_hint");
+
         // Make sure extras were passed back on onSave()
         assertThat(saveRequest.data).isNotNull();
         final String extraValue = saveRequest.data.getString("numbers");
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivityTest.java
index 681fd1a..e7f26e2 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivityTest.java
@@ -141,12 +141,12 @@
 
         // Make LoginActivity to regain window focus and fill ui is expected to show
         tapViewAndExpectWindowEvent(loginActivity.getUsername());
-        mUiBot.assertDatasets("The Dude");
+        mUiBot.assertNoDatasetsEver();
         assertThat(emptyActivity.hasWindowFocus()).isFalse();
 
         // Tap on EmptyActivity and fill ui is gone.
         tapViewAndExpectWindowEvent(emptyActivity.getEmptyView());
-        mUiBot.assertNoDatasets();
+        mUiBot.assertNoDatasetsEver();
         assertThat(emptyActivity.hasWindowFocus()).isTrue();
         // LoginActivity username field is still focused but window has no focus
         assertThat(loginActivity.getUsername().hasFocus()).isTrue();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java
index fd392d5..06191a5 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java
@@ -20,7 +20,7 @@
 import static android.autofillservice.cts.Helper.ID_USERNAME;
 import static android.autofillservice.cts.Helper.ID_USERNAME_LABEL;
 import static android.autofillservice.cts.Helper.assertTextAndValue;
-import static android.autofillservice.cts.Helper.assertTextFromResouces;
+import static android.autofillservice.cts.Helper.assertTextFromResources;
 import static android.autofillservice.cts.Helper.assertTextIsSanitized;
 import static android.autofillservice.cts.Helper.assertTextOnly;
 import static android.autofillservice.cts.Helper.findNodeByResourceId;
@@ -75,7 +75,7 @@
         assertTextIsSanitized(fillRequest.structure, ID_USERNAME_LABEL);
 
         // ...password label should be ok because it was set from other resource id
-        assertTextFromResouces(fillRequest.structure, ID_PASSWORD_LABEL, "DA PASSWORD", false,
+        assertTextFromResources(fillRequest.structure, ID_PASSWORD_LABEL, "DA PASSWORD", false,
                 "new_password_label");
 
         // ...username and password should be ok because they were set in the SML
@@ -94,7 +94,7 @@
 
         // Assert sanitization on save: everything should be available!
         assertTextOnly(findNodeByResourceId(saveRequest.structure, ID_USERNAME_LABEL), "DA USER");
-        assertTextFromResouces(saveRequest.structure, ID_PASSWORD_LABEL, "DA PASSWORD", false,
+        assertTextFromResources(saveRequest.structure, ID_PASSWORD_LABEL, "DA PASSWORD", false,
                 "new_password_label");
         assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_USERNAME), "malkovich");
         assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_PASSWORD), "malkovich");
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index ff884ce..3c24967 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -36,11 +36,13 @@
 
 import static org.junit.Assume.assumeTrue;
 
+import android.app.Activity;
 import android.app.Instrumentation;
 import android.app.UiAutomation;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.Rect;
 import android.os.SystemClock;
 import android.service.autofill.SaveInfo;
 import android.support.test.uiautomator.By;
@@ -51,6 +53,8 @@
 import android.support.test.uiautomator.Until;
 import android.text.Html;
 import android.util.Log;
+import android.view.View;
+import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityWindowInfo;
 
@@ -333,7 +337,8 @@
     }
 
     /**
-     * Selects a dataset that should be visible in the floating UI.
+     * Selects a dataset that should be visible in the floating UI and does not need to wait for
+     * application become idle.
      */
     public void selectDataset(String name) throws Exception {
         final UiObject2 picker = findDatasetPicker(UI_DATASET_PICKER_TIMEOUT);
@@ -341,6 +346,16 @@
     }
 
     /**
+     * Selects a dataset that should be visible in the floating UI and waits for application become
+     * idle if needed.
+     */
+    public void selectDatasetSync(String name) throws Exception {
+        final UiObject2 picker = findDatasetPicker(UI_DATASET_PICKER_TIMEOUT);
+        selectDataset(picker, name);
+        mDevice.waitForIdle();
+    }
+
+    /**
      * Selects a dataset that should be visible in the floating UI.
      */
     public void selectDataset(UiObject2 picker, String name) {
@@ -817,7 +832,12 @@
     private String getString(String id) {
         final Resources resources = mContext.getResources();
         final int stringId = resources.getIdentifier(id, "string", "android");
-        return resources.getString(stringId);
+        try {
+            return resources.getString(stringId);
+        } catch (Resources.NotFoundException e) {
+            throw new IllegalStateException("no internal string for '" + id + "' / res=" + stringId
+                    + ": ", e);
+        }
     }
 
     /**
@@ -826,7 +846,12 @@
     private String getString(String id, Object... formatArgs) {
         final Resources resources = mContext.getResources();
         final int stringId = resources.getIdentifier(id, "string", "android");
-        return resources.getString(stringId, formatArgs);
+        try {
+            return resources.getString(stringId, formatArgs);
+        } catch (Resources.NotFoundException e) {
+            throw new IllegalStateException("no internal string for '" + id + "' / res=" + stringId
+                    + ": ", e);
+        }
     }
 
     /**
@@ -1014,15 +1039,49 @@
         }
     }
 
+    private Rect cropScreenshotWithoutScreenDecoration(Activity activity) {
+        final WindowInsets[] inset = new WindowInsets[1];
+        final View[] rootView = new View[1];
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            rootView[0] = activity.getWindow().getDecorView();
+            inset[0] = rootView[0].getRootWindowInsets();
+        });
+        final int navBarHeight = inset[0].getStableInsetBottom();
+        final int statusBarHeight = inset[0].getStableInsetTop();
+
+        return new Rect(0, statusBarHeight, rootView[0].getWidth(),
+                rootView[0].getHeight() - navBarHeight - statusBarHeight);
+    }
+
     // TODO(b/74358143): ideally we should take a screenshot limited by the boundaries of the
     // activity window, so external elements (such as the clock) are filtered out and don't cause
     // test flakiness when the contents are compared.
     public Bitmap takeScreenshot() {
+        return takeScreenshotWithRect(null);
+    }
+
+    public Bitmap takeScreenshot(@NonNull Activity activity) {
+        // crop the screenshot without screen decoration to prevent test flakiness.
+        final Rect rect = cropScreenshotWithoutScreenDecoration(activity);
+        return takeScreenshotWithRect(rect);
+    }
+
+    private Bitmap takeScreenshotWithRect(@Nullable Rect r) {
         final long before = SystemClock.elapsedRealtime();
         final Bitmap bitmap = mAutoman.takeScreenshot();
         final long delta = SystemClock.elapsedRealtime() - before;
         Log.v(TAG, "Screenshot taken in " + delta + "ms");
-        return bitmap;
+        if (r == null) {
+            return bitmap;
+        }
+        try {
+            return Bitmap.createBitmap(bitmap, r.left, r.top, r.right, r.bottom);
+        } finally {
+            if (bitmap != null) {
+                bitmap.recycle();
+            }
+        }
     }
 
     /**
diff --git a/tests/backup/OWNERS b/tests/backup/OWNERS
index 3637e32..c28c4d8 100644
--- a/tests/backup/OWNERS
+++ b/tests/backup/OWNERS
@@ -1,3 +1,4 @@
+# Bug component: 41666
 # Use this reviewer by default.
 br-framework-team+reviews@google.com
 
diff --git a/tests/backup/TEST_MAPPING b/tests/backup/TEST_MAPPING
new file mode 100644
index 0000000..4e5beb0
--- /dev/null
+++ b/tests/backup/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsBackupTestCases"
+    }
+  ]
+}
diff --git a/tests/backup/app/src/android/backup/app/FullBackupBackupAgent.java b/tests/backup/app/src/android/backup/app/FullBackupBackupAgent.java
index 8535344..0f4a123 100644
--- a/tests/backup/app/src/android/backup/app/FullBackupBackupAgent.java
+++ b/tests/backup/app/src/android/backup/app/FullBackupBackupAgent.java
@@ -53,23 +53,25 @@
     @Override
     public void onRestoreFile(ParcelFileDescriptor data, long size,
             File destination, int type, long mode, long mtime) throws IOException {
-        Log.d(MainActivity.TAG, "onRestoreFile " + destination);
         super.onRestoreFile(data, size, destination, type, mode, mtime);
+        Log.d(MainActivity.TAG, "onRestoreFile " + destination);
     }
 
     @Override
     public void onFullBackup(FullBackupDataOutput data) throws IOException {
-        Log.d(MainActivity.TAG, "Full backup requested, quota is " + data.getQuota());
         super.onFullBackup(data);
+        Log.d(MainActivity.TAG, "Full backup requested, quota is " + data.getQuota());
     }
 
     @Override
     public void onQuotaExceeded(long backupDataBytes, long quotaBytes) {
+        super.onQuotaExceeded(backupDataBytes, quotaBytes);
         Log.d(MainActivity.TAG, "Quota exceeded!");
     }
 
     @Override
     public void onRestoreFinished() {
+        super.onRestoreFinished();
         Log.d(MainActivity.TAG, "onRestoreFinished");
     }
 
diff --git a/tests/backup/app/src/android/backup/app/KeyValueBackupAgent.java b/tests/backup/app/src/android/backup/app/KeyValueBackupAgent.java
index 155d8f9..c73731e 100644
--- a/tests/backup/app/src/android/backup/app/KeyValueBackupAgent.java
+++ b/tests/backup/app/src/android/backup/app/KeyValueBackupAgent.java
@@ -77,11 +77,13 @@
 
     @Override
     public void onQuotaExceeded(long backupDataBytes, long quotaBytes) {
+        super.onQuotaExceeded(backupDataBytes, quotaBytes);
         Log.d(MainActivity.TAG, "Quota exceeded!");
     }
 
     @Override
     public void onRestoreFinished() {
+        super.onRestoreFinished();
         Log.d(MainActivity.TAG, "onRestoreFinished");
     }
 
diff --git a/tests/camera/libctscamera2jni/native-camera-jni.cpp b/tests/camera/libctscamera2jni/native-camera-jni.cpp
index b9fc256..5b9f74c 100644
--- a/tests/camera/libctscamera2jni/native-camera-jni.cpp
+++ b/tests/camera/libctscamera2jni/native-camera-jni.cpp
@@ -2721,10 +2721,12 @@
             goto cleanup;
         }
 
+        StaticInfo staticInfo(chars);
         ACameraMetadata_const_entry sessionParamKeys{};
         ret = ACameraMetadata_getConstEntry(chars, ACAMERA_REQUEST_AVAILABLE_SESSION_KEYS,
                 &sessionParamKeys);
-        if ((ret != ACAMERA_OK) || (sessionParamKeys.count == 0)) {
+        if ((ret != ACAMERA_OK) || (sessionParamKeys.count == 0) ||
+                !staticInfo.isColorOutputSupported()) {
             ACameraMetadata_free(chars);
             chars = nullptr;
             continue;
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
index a64cd50..23f406f 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
@@ -2698,4 +2698,166 @@
             }
         }
     }
+
+    /**
+     * Verify audio restrictions are set properly for single CameraDevice usage
+     */
+    public void testAudioRestrictionSingleDevice() throws Exception {
+        int[] testModes = {
+            CameraDevice.AUDIO_RESTRICTION_VIBRATION_SOUND,
+            CameraDevice.AUDIO_RESTRICTION_NONE,
+            CameraDevice.AUDIO_RESTRICTION_VIBRATION,
+        };
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                openDevice(mCameraIds[i], mCameraMockListener);
+                waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
+
+                for (int mode : testModes) {
+                    int retMode = mCamera.setCameraAudioRestriction(mode);
+                    assertTrue("Audio restriction mode mismatch: input: " + mode +
+                            ", output:" + retMode, mode == retMode);
+                }
+
+                try {
+                    // Test invalid mode
+                    mCamera.setCameraAudioRestriction(42);
+                    fail("Should get IllegalArgumentException for invalid mode");
+                } catch (IllegalArgumentException e) {
+                    // expected
+                }
+            }
+            finally {
+                closeDevice(mCameraIds[i], mCameraMockListener);
+            }
+        }
+    }
+
+    private void testTwoCameraDevicesAudioRestriction(String id0, String id1) throws Exception {
+        BlockingStateCallback cam0Cb = new BlockingStateCallback();
+        BlockingStateCallback cam1Cb = new BlockingStateCallback();
+        CameraDevice cam0 = null;
+        CameraDevice cam1 = null;
+        try {
+            cam0 = CameraTestUtils.openCamera(mCameraManager, id0, cam0Cb, mHandler);
+            cam0Cb.waitForState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
+
+            int mode0 = CameraDevice.AUDIO_RESTRICTION_VIBRATION_SOUND;
+            int retMode = cam0.setCameraAudioRestriction(mode0);
+            assertTrue("Audio restriction mode mismatch: input: " + mode0 + ", output:" + retMode,
+                    retMode == mode0);
+
+            cam1 = CameraTestUtils.openCamera(mCameraManager, id1, cam1Cb, mHandler);
+            cam1Cb.waitForState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
+
+            // See if cam0 is evicted.
+            boolean cam0Evicted = true;
+            try {
+                final int cameraEvictedTimeoutMs = 1000;
+                cam0Cb.waitForState(STATE_DISCONNECTED, cameraEvictedTimeoutMs);
+            } catch (TimeoutRuntimeException e) {
+                // camera 0 is not evicted
+                cam0Evicted = false;
+            }
+
+            if (cam0Evicted) {
+                Log.i(TAG, "Camera " + id0 + " is evicted. Testing camera " + id1 + " alone.");
+                // cam0 is evicted
+                try {
+                    cam0.setCameraAudioRestriction(mode0);
+                    fail("Should get CameraAccessException for disconnected camera.");
+                } catch (CameraAccessException e) {
+                    // expected
+                }
+                // Test the behavior for single remaining client
+                int mode1 = CameraDevice.AUDIO_RESTRICTION_VIBRATION;
+                retMode = cam1.setCameraAudioRestriction(mode1);
+                assertTrue("Audio restriction mode mismatch: input: " + mode1 +
+                        ", output:" + retMode, retMode == mode1);
+                return;
+            }
+
+            // The output mode should be union of all CameraDevices
+            int mode1 = CameraDevice.AUDIO_RESTRICTION_VIBRATION;
+            int expectMode = mode0 | mode1;
+            retMode = cam1.setCameraAudioRestriction(mode1);
+            assertTrue("Audio restriction mode mismatch: expect: " + expectMode +
+                    ", output:" + retMode, retMode == expectMode);
+
+            // test turning off mute settings also
+            mode0 = CameraDevice.AUDIO_RESTRICTION_NONE;
+            expectMode = mode0 | mode1;
+            retMode = cam0.setCameraAudioRestriction(mode0);
+            assertTrue("Audio restriction mode mismatch: expect: " + expectMode +
+                    ", output:" + retMode, retMode == expectMode);
+
+            // mode should be NONE when both device set to NONE
+            mode1 = CameraDevice.AUDIO_RESTRICTION_NONE;
+            expectMode = mode0 | mode1;
+            retMode = cam1.setCameraAudioRestriction(mode1);
+            assertTrue("Audio restriction mode mismatch: expect: " + expectMode +
+                    ", output:" + retMode, retMode == expectMode);
+
+            // test removal of VIBRATE won't affect existing VIBRATE_SOUND state
+            mode0 = CameraDevice.AUDIO_RESTRICTION_VIBRATION_SOUND;
+            expectMode = mode0 | mode1;
+            retMode = cam0.setCameraAudioRestriction(mode0);
+            assertTrue("Audio restriction mode mismatch: expect: " + expectMode +
+                    ", output:" + retMode, retMode == expectMode);
+
+            mode1 = CameraDevice.AUDIO_RESTRICTION_VIBRATION;
+            expectMode = mode0 | mode1;
+            retMode = cam1.setCameraAudioRestriction(mode1);
+            assertTrue("Audio restriction mode mismatch: expect: " + expectMode +
+                    ", output:" + retMode, retMode == expectMode);
+
+            mode1 = CameraDevice.AUDIO_RESTRICTION_NONE;
+            expectMode = mode0 | mode1;
+            retMode = cam1.setCameraAudioRestriction(mode1);
+            assertTrue("Audio restriction mode mismatch: expect: " + expectMode +
+                    ", output:" + retMode, retMode == expectMode);
+
+            // Now test CameraDevice.close will remove setting and exception is thrown for closed
+            // camera.
+            cam0.close();
+            cam0Cb.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
+            try {
+                cam0.setCameraAudioRestriction(mode0);
+                fail("Should get IllegalStateException for closed camera.");
+            } catch (IllegalStateException e) {
+                // expected;
+            }
+
+            cam0 = null;
+            cam0Cb = null;
+            expectMode = mode1;
+            retMode = cam1.setCameraAudioRestriction(mode1);
+            assertTrue("Audio restriction mode mismatch: expect: " + expectMode +
+                    ", output:" + retMode, retMode == expectMode);
+        } finally {
+            if (cam0 != null) {
+                cam0.close();
+                cam0Cb.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
+                cam0Cb = null;
+            }
+            if (cam1 != null) {
+                cam1.close();
+                cam1Cb.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
+                cam1Cb = null;
+            }
+        }
+    }
+
+    public void testAudioRestrictionMultipleDevices() throws Exception {
+        if (mCameraIds.length < 2) {
+            Log.i(TAG, "device doesn't have multiple cameras, skipping");
+            return;
+        }
+
+        for (int i = 0; i < mCameraIds.length; i++) {
+            for (int j = i+1; j < mCameraIds.length; j++) {
+                testTwoCameraDevicesAudioRestriction(mCameraIds[i], mCameraIds[j]);
+            }
+        }
+    }
 }
diff --git a/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java b/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
index ab65015..6f6b13c 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
@@ -21,6 +21,7 @@
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureFailure;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.params.BlackLevelPattern;
@@ -401,16 +402,22 @@
         }
 
         TotalCaptureResult result = null;
+        // List of (frameNumber, physical camera Id) pairs
+        ArrayList<Pair<Long, String>> droppedPhysicalResults = new ArrayList<>();
         for (int i = 0; i < numFramesVerified; i++) {
             result = captureListener.getTotalCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+
             Map<String, CaptureResult> physicalCaptureResults = result.getPhysicalCameraResults();
-            errorCollector.expectEquals("Number of physical result metadata doesn't match " +
-                    physicalCaptureResults.size() + " vs " + requestedPhysicalIds.size(),
-                    physicalCaptureResults.size(), requestedPhysicalIds.size());
+            ArrayList<String> droppedIds = new ArrayList<String>(requestedPhysicalIds);
+            droppedIds.removeAll(physicalCaptureResults.keySet());
+            for (String droppedId : droppedIds) {
+                droppedPhysicalResults.add(
+                        new Pair<Long, String>(result.getFrameNumber(), droppedId));
+            }
 
             validateOneCaptureResult(errorCollector, staticInfo, waiverKeys, allKeys,
                     requestBuilder, result, null/*cameraId*/, i);
-            for (String physicalId : requestedPhysicalIds) {
+            for (String physicalId : physicalCaptureResults.keySet()) {
                 StaticMetadata physicalStaticInfo = allStaticInfo.get(physicalId);
                 validateOneCaptureResult(errorCollector, physicalStaticInfo,
                         physicalWaiverKeys.get(physicalId),
@@ -418,6 +425,23 @@
                         physicalId, i);
             }
         }
+
+        // Verify that all dropped physical camera results are notified via capture failure.
+        while (captureListener.hasMoreFailures()) {
+            ArrayList<CaptureFailure> failures =
+                    captureListener.getCaptureFailures(/*maxNumFailures*/ 1);
+            for (CaptureFailure failure : failures) {
+                String failedPhysicalId = failure.getPhysicalCameraId();
+                Long failedFrameNumber = failure.getFrameNumber();
+                if (failedPhysicalId != null) {
+                    droppedPhysicalResults.removeIf(
+                            n -> n.equals(
+                            new Pair<Long, String>(failedFrameNumber, failedPhysicalId)));
+                }
+            }
+        }
+        errorCollector.expectTrue("Not all dropped results for physical cameras are notified",
+                droppedPhysicalResults.isEmpty());
     }
 
     private static void validateOneCaptureResult(CameraErrorCollector errorCollector,
diff --git a/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java b/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java
index 38f8816..59e89a2 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java
@@ -19,6 +19,7 @@
 import static android.hardware.camera2.cts.CameraTestUtils.*;
 
 import android.graphics.ImageFormat;
+import android.graphics.SurfaceTexture;
 import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
 import android.hardware.camera2.CameraDevice;
@@ -170,6 +171,23 @@
         }
     }
 
+    public void testWriterFormatOverride() throws Exception {
+        int[] TEXTURE_TEST_FORMATS = {ImageFormat.YV12, ImageFormat.YUV_420_888};
+        SurfaceTexture texture = new SurfaceTexture(/*random int*/1);
+        texture.setDefaultBufferSize(640, 480);
+        Surface surface = new Surface(texture);
+
+        for (int format : TEXTURE_TEST_FORMATS) {
+            // Override default buffer format of Surface texture to test format
+            ImageWriter writer = ImageWriter.newInstance(surface, MAX_NUM_IMAGES, format);
+            Image image = writer.dequeueInputImage();
+            Log.i(TAG, "testing format " + format + ", got input image format " +
+                    image.getFormat());
+            assertTrue(image.getFormat() == format);
+            writer.close();
+        }
+    }
+
     private void readerWriterFormatTestByCamera(int format, boolean altFactoryMethod)
             throws Exception {
         List<Size> sizes = getSortedSizesForFormat(mCamera.getId(), mCameraManager, format, null);
diff --git a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
index 37dfbdd..7a2e339 100644
--- a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
@@ -1109,8 +1109,12 @@
             Map<String, CaptureResult> physicalResultsDual =
                     totalCaptureResultDual.getPhysicalCameraResults();
             for (String physicalId : physicalCameraIds) {
-                 physicalTimestamps[index][i] = physicalResultsDual.get(physicalId).get(
-                         CaptureResult.SENSOR_TIMESTAMP);
+                 if (physicalResultsDual.containsKey(physicalId)) {
+                     physicalTimestamps[index][i] = physicalResultsDual.get(physicalId).get(
+                             CaptureResult.SENSOR_TIMESTAMP);
+                 } else {
+                     physicalTimestamps[index][i] = -1;
+                 }
                  index++;
             }
         }
@@ -1129,15 +1133,19 @@
         }
         for (int i = 0; i < physicalCameraIds.size(); i++) {
             for (int j = 0 ; j < NUM_FRAMES_CHECKED-1; j++) {
-                assertTrue("Physical camera timestamp must monolithically increase",
-                        physicalTimestamps[i][j] < physicalTimestamps[i][j+1]);
-                if (j > 0) {
+                if (physicalTimestamps[i][j] != -1 && physicalTimestamps[i][j+1] != -1) {
+                    assertTrue("Physical camera timestamp must monolithically increase",
+                            physicalTimestamps[i][j] < physicalTimestamps[i][j+1]);
+                }
+                if (j > 0 && physicalTimestamps[i][j] != -1) {
                     assertTrue("Physical camera's timestamp N must be greater than logical " +
                             "camera's timestamp N-1",
                             physicalTimestamps[i][j] > logicalTimestamps[j-1]);
                 }
-                assertTrue("Physical camera's timestamp N must be less than logical camera's " +
-                        "timestamp N+1", physicalTimestamps[i][j] > logicalTimestamps[j+1]);
+                if (physicalTimestamps[i][j] != -1) {
+                    assertTrue("Physical camera's timestamp N must be less than logical camera's " +
+                            "timestamp N+1", physicalTimestamps[i][j] > logicalTimestamps[j+1]);
+                }
             }
         }
         double logicalAvgDurationMs2 = (logicalTimestamps2[NUM_FRAMES_CHECKED-1] -
diff --git a/tests/contentcaptureservice/OWNERS b/tests/contentcaptureservice/OWNERS
index 4df1ffa..595c93d 100644
--- a/tests/contentcaptureservice/OWNERS
+++ b/tests/contentcaptureservice/OWNERS
@@ -1,2 +1,4 @@
 # Bug component: 544200
-felipeal@google.com
\ No newline at end of file
+felipeal@google.com
+adamhe@google.com
+svetoslavganov@google.com
\ No newline at end of file
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Assertions.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Assertions.java
index 0f7e298..866b947 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Assertions.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Assertions.java
@@ -311,12 +311,17 @@
             @NonNull List<ContentCaptureEvent> events, int minimumSize,
             @NonNull AutofillId... expectedIds) {
         final int actualSize = events.size();
+        final int disappearedEventIndex;
         if (actualSize == minimumSize) {
             // Activity stopped before TYPE_VIEW_DISAPPEARED were sent.
             return false;
+        } else if (actualSize == minimumSize + 1) {
+            // Activity did not receive TYPE_VIEW_TREE_APPEARING and TYPE_VIEW_TREE_APPEARED.
+            disappearedEventIndex = minimumSize;
+        } else {
+            disappearedEventIndex = minimumSize + 1;
         }
-        assertThat(events).hasSize(minimumSize + 1);
-        final ContentCaptureEvent batchDisappearEvent = events.get(minimumSize);
+        final ContentCaptureEvent batchDisappearEvent = events.get(disappearedEventIndex);
 
         if (expectedIds.length == 1) {
             assertWithMessage("Should have just one deleted id on %s", batchDisappearEvent)
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/unit/ViewNodeTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/unit/ViewNodeTest.java
index 5ece59f..67d6670 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/unit/ViewNodeTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/unit/ViewNodeTest.java
@@ -143,6 +143,9 @@
 
         assertThrows(NullPointerException.class, () -> structure.setTextIdEntry(null));
         assertThat(node.getTextIdEntry()).isNull();
+
+        assertThrows(NullPointerException.class, () -> structure.setHintIdEntry(null));
+        assertThat(node.getHintIdEntry()).isNull();
     }
 
     @Test
@@ -363,6 +366,7 @@
         structure.setText("IGNORE ME!");
         structure.setText("Now we're talking!", 4, 8);
         structure.setHint("Soylent Green is SPOILER ALERT");
+        structure.setHintIdEntry("HINT ID ENTRY");
         structure.setTextStyle(15.0f, 16, 23, 42);
         structure.setTextLines(new int[] {4,  8, 15} , new int[] {16, 23, 42});
         return structure;
@@ -377,6 +381,7 @@
         assertThat(node.getTextSelectionStart()).isEqualTo(4);
         assertThat(node.getTextSelectionEnd()).isEqualTo(8);
         assertThat(node.getHint()).isEqualTo("Soylent Green is SPOILER ALERT");
+        assertThat(node.getHintIdEntry()).isEqualTo("HINT ID ENTRY");
         assertThat(node.getTextSize()).isWithin(1.0e-10f).of(15.0f);
         assertThat(node.getTextColor()).isEqualTo(16);
         assertThat(node.getTextBackgroundColor()).isEqualTo(23);
diff --git a/tests/core/runner/Android.bp b/tests/core/runner/Android.bp
deleted file mode 100644
index de3d724..0000000
--- a/tests/core/runner/Android.bp
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT 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 core runner.
-//==========================================================
-
-// Build library
-java_library {
-    name: "cts-core-test-runner",
-
-    srcs: ["src/**/*.java"],
-    static_libs: [
-        "compatibility-device-util",
-        "android-support-test",
-        "vogarexpect",
-        "testng",
-    ],
-
-    libs: ["android.test.runner.stubs"],
-    sdk_version: "test_current",
-
-}
-
-//==========================================================
-// Build the run listener
-//==========================================================
-
-// Build library
-java_library {
-    name: "cts-test-runner",
-
-    srcs: ["src/com/android/cts/runner/**/*.java"],
-    static_libs: ["android-support-test"],
-    sdk_version: "current",
-
-}
diff --git a/tests/core/runner/AndroidManifest.xml b/tests/core/runner/AndroidManifest.xml
deleted file mode 100644
index 001e6f2..0000000
--- a/tests/core/runner/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.core.tests.runner">
-    <uses-permission android:name="android.permission.INTERNET" />
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
-                     android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
-
-</manifest>
diff --git a/tests/core/runner/src/com/android/cts/core/runner/ExpectationBasedFilter.java b/tests/core/runner/src/com/android/cts/core/runner/ExpectationBasedFilter.java
deleted file mode 100644
index 4419b4b..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/ExpectationBasedFilter.java
+++ /dev/null
@@ -1,145 +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.core.runner;
-
-import android.os.Bundle;
-import android.util.Log;
-import com.google.common.base.Splitter;
-import java.io.IOException;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-import javax.annotation.Nullable;
-import org.junit.runner.Description;
-import org.junit.runner.manipulation.Filter;
-import org.junit.runners.ParentRunner;
-import org.junit.runners.Suite;
-import vogar.expect.Expectation;
-import vogar.expect.ExpectationStore;
-import vogar.expect.ModeId;
-import vogar.expect.Result;
-
-/**
- * Filter out tests/classes that are not requested or which are expected to fail.
- *
- * <p>This filter has to handle both a hierarchy of {@code Description descriptions} that looks
- * something like this:
- * <pre>
- * Suite
- *     Suite
- *         Suite
- *             ParentRunner
- *                 Test
- *                 ...
- *             ...
- *         ParentRunner
- *             Test
- *             ...
- *         ...
- *     Suite
- *         ParentRunner
- *             Test
- *             ...
- *         ...
- *     ...
- * </pre>
- *
- * <p>It cannot filter out the non-leaf nodes in the hierarchy, i.e. {@link Suite} and
- * {@link ParentRunner}, as that would prevent it from traversing the hierarchy and finding
- * the leaf nodes.
- */
-class ExpectationBasedFilter extends Filter {
-
-    static final String TAG = "ExpectationBasedFilter";
-
-    private static final String ARGUMENT_EXPECTATIONS = "core-expectations";
-
-    private static final Splitter CLASS_LIST_SPLITTER = Splitter.on(',').trimResults();
-
-    private final ExpectationStore expectationStore;
-
-    private static List<String> getExpectationResourcePaths(Bundle args) {
-        return CLASS_LIST_SPLITTER.splitToList(args.getString(ARGUMENT_EXPECTATIONS));
-    }
-
-    public ExpectationBasedFilter(Bundle args) {
-        ExpectationStore expectationStore = null;
-        try {
-            // Get the set of resource names containing the expectations.
-            Set<String> expectationResources = new LinkedHashSet<>(
-                getExpectationResourcePaths(args));
-            Log.i(TAG, "Loading expectations from: " + expectationResources);
-            expectationStore = ExpectationStore.parseResources(
-                getClass(), expectationResources, ModeId.DEVICE);
-        } catch (IOException e) {
-            Log.e(TAG, "Could not initialize ExpectationStore: ", e);
-        }
-
-        this.expectationStore = expectationStore;
-    }
-
-    @Override
-    public boolean shouldRun(Description description) {
-        // Only filter leaf nodes. The description is for a test if and only if it is a leaf node.
-        // Non-leaf nodes must not be filtered out as that would prevent leaf nodes from being
-        // visited in the case when we are traversing the hierarchy of classes.
-        Description testDescription = getTestDescription(description);
-        if (testDescription != null) {
-            String className = testDescription.getClassName();
-            String methodName = testDescription.getMethodName();
-            String testName = className + "#" + methodName;
-
-            if (expectationStore != null) {
-                Expectation expectation = expectationStore.get(testName);
-                if (expectation.getResult() != Result.SUCCESS) {
-                    Log.d(TAG, "Excluding test " + testDescription
-                            + " as it matches expectation: " + expectation);
-                    return false;
-                }
-            }
-        }
-
-        return true;
-    }
-
-    private Description getTestDescription(Description description) {
-        List<Description> children = description.getChildren();
-        // An empty description is by definition a test.
-        if (children.isEmpty()) {
-            return description;
-        }
-
-        // Handle initialization errors that were wrapped in an ErrorReportingRunner as a special
-        // case. This is needed because ErrorReportingRunner is treated as a suite of Throwables,
-        // (where each Throwable corresponds to a test called initializationError) and so its
-        // description contains children, one for each Throwable, and so is not treated as a test
-        // to filter. Unfortunately, it does not support Filterable so this filter is never applied
-        // to its children.
-        // See https://github.com/junit-team/junit/issues/1253
-        Description child = children.get(0);
-        String methodName = child.getMethodName();
-        if ("initializationError".equals(methodName)) {
-            return child;
-        }
-
-        return null;
-    }
-
-    @Override
-    public String describe() {
-        return "TestFilter";
-    }
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/support/SingleTestNGTestRunListener.java b/tests/core/runner/src/com/android/cts/core/runner/support/SingleTestNGTestRunListener.java
deleted file mode 100644
index 7a68a8b..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/support/SingleTestNGTestRunListener.java
+++ /dev/null
@@ -1,124 +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.core.runner.support;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-/**
- * Listener for TestNG runs that provides gtest-like console output.
- *
- * Prints a message like [RUN], [OK], [ERROR], [SKIP] to stdout
- * as tests are being executed with their status.
- *
- * This output is also saved as the device logs (logcat) when the test is run through
- * cts-tradefed.
- */
-public class SingleTestNGTestRunListener implements org.testng.ITestListener {
-    private int mTestStarted = 0;
-
-    private Map<String, Throwable> failures = new LinkedHashMap<>();
-
-    private static class Prefixes {
-        @SuppressWarnings("unused")
-        private static final String INFORMATIONAL_MARKER =  "[----------]";
-        private static final String START_TEST_MARKER =     "[ RUN      ]";
-        private static final String OK_TEST_MARKER =        "[       OK ]";
-        private static final String ERROR_TEST_RUN_MARKER = "[    ERROR ]";
-        private static final String SKIPPED_TEST_MARKER =   "[     SKIP ]";
-        private static final String TEST_RUN_MARKER =       "[==========]";
-    }
-
-    // How many tests did TestNG *actually* try to run?
-    public int getNumTestStarted() {
-      return mTestStarted;
-    }
-
-    public Map<String, Throwable> getFailures() {
-        return Collections.unmodifiableMap(failures);
-    }
-
-    @Override
-    public void onFinish(org.testng.ITestContext context) {
-        System.out.println(String.format("%s", Prefixes.TEST_RUN_MARKER));
-    }
-
-    @Override
-    public void onStart(org.testng.ITestContext context) {
-        System.out.println(String.format("%s", Prefixes.INFORMATIONAL_MARKER));
-    }
-
-    @Override
-    public void onTestFailedButWithinSuccessPercentage(org.testng.ITestResult result) {
-        onTestFailure(result);
-    }
-
-    @Override
-    public void onTestFailure(org.testng.ITestResult result) {
-        // All failures are coalesced into one '[ FAILED ]' message at the end
-        // This is because a single test method can run multiple times with different parameters.
-        // Since we only test a single method, it's safe to combine all failures into one
-        // failure at the end.
-        //
-        // The big pass/fail is printed from SingleTestNGTestRunner, not from the listener.
-        String id = getId(result);
-        Throwable throwable = result.getThrowable();
-        System.out.println(String.format("%s %s ::: %s", Prefixes.ERROR_TEST_RUN_MARKER,
-                id, stringify(throwable)));
-        failures.put(id, throwable);
-    }
-
-    @Override
-    public void onTestSkipped(org.testng.ITestResult result) {
-        System.out.println(String.format("%s %s", Prefixes.SKIPPED_TEST_MARKER,
-              getId(result)));
-    }
-
-    @Override
-    public void onTestStart(org.testng.ITestResult result) {
-        mTestStarted++;
-        System.out.println(String.format("%s %s", Prefixes.START_TEST_MARKER,
-              getId(result)));
-    }
-
-    @Override
-    public void onTestSuccess(org.testng.ITestResult result) {
-        System.out.println(String.format("%s", Prefixes.OK_TEST_MARKER));
-    }
-
-    private String getId(org.testng.ITestResult test) {
-        // TestNG is quite complicated since tests can have arbitrary parameters.
-        // Use its code to stringify a result name instead of doing it ourselves.
-
-        org.testng.remote.strprotocol.TestResultMessage msg =
-                new org.testng.remote.strprotocol.TestResultMessage(
-                    null, /*suite name*/
-                    null, /*test name -- display the test method name instead */
-                    test);
-
-        String className = test.getTestClass().getName();
-        //String name = test.getMethod().getMethodName();
-        return String.format("%s#%s", className, msg.toDisplayString());
-
-    }
-
-    private String stringify(Throwable error) {
-        return Arrays.toString(error.getStackTrace()).replaceAll("\n", " ");
-    }
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/support/SingleTestNgTestExecutor.java b/tests/core/runner/src/com/android/cts/core/runner/support/SingleTestNgTestExecutor.java
deleted file mode 100644
index deb18df..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/support/SingleTestNgTestExecutor.java
+++ /dev/null
@@ -1,132 +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.core.runner.support;
-
-import android.util.Log;
-
-import org.testng.TestNG;
-import org.testng.xml.XmlClass;
-import org.testng.xml.XmlInclude;
-import org.testng.xml.XmlSuite;
-import org.testng.xml.XmlTest;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Test executor to run a single TestNG test method.
- */
-public class SingleTestNgTestExecutor {
-    // Execute any method which is in the class klass.
-    // The klass is passed in separately to handle inherited methods only.
-    // Returns true if all tests pass, false otherwise.
-    public static Result execute(Class<?> klass, String methodName) {
-        if (klass == null) {
-          throw new NullPointerException("klass must not be null");
-        }
-
-        if (methodName == null) {
-          throw new NullPointerException("methodName must not be null");
-        }
-
-        //if (!method.getDeclaringClass().isAssignableFrom(klass)) {
-        //  throw new IllegalArgumentException("klass must match method's declaring class");
-        //}
-
-        SingleTestNGTestRunListener listener = new SingleTestNGTestRunListener();
-
-        // Although creating a new testng "core" every time might seem heavyweight, in practice
-        // it seems to take a mere few milliseconds at most.
-        // Since we're running all the parameteric combinations of a test,
-        // this ends up being neglible relative to that.
-        TestNG testng = createTestNG(klass.getName(), methodName, listener);
-        testng.run();
-
-        if (listener.getNumTestStarted() <= 0) {
-          // It's possible to be invoked here with an arbitrary method name
-          // so print out a warning incase TestNG actually had a no-op.
-          Log.w("TestNgExec", "execute class " + klass.getName() + ", method " + methodName +
-              " had 0 tests executed. Not a test method?");
-        }
-
-        return new Result(testng.hasFailure(), listener.getFailures());
-    }
-
-    private static org.testng.TestNG createTestNG(String klass, String method,
-            SingleTestNGTestRunListener listener) {
-        org.testng.TestNG testng = new org.testng.TestNG();
-        testng.setUseDefaultListeners(false);  // Don't create the testng-specific HTML/XML reports.
-        // It still prints the X/Y tests succeeded/failed summary to stdout.
-
-        // We don't strictly need this listener for CTS, but having it print SUCCESS/FAIL
-        // makes it easier to diagnose which particular combination of a test method had failed
-        // from looking at device logcat.
-        testng.addListener(listener);
-
-        /* Construct the following equivalent XML configuration:
-         *
-         * <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
-         * <suite>
-         *   <test>
-         *     <classes>
-         *       <class name="$klass">
-         *         <include name="$method" />
-         *       </class>
-         *     </classes>
-         *   </test>
-         * </suite>
-         *
-         * This will ensure that only a single klass/method is being run by testng.
-         * (It can still be run multiple times due to @DataProvider, with different parameters
-         * each time)
-         */
-        List<XmlSuite> suites = new ArrayList<>();
-        XmlSuite the_suite = new XmlSuite();
-        XmlTest the_test = new XmlTest(the_suite);
-        XmlClass the_class = new XmlClass(klass);
-        XmlInclude the_include = new XmlInclude(method);
-
-        the_class.getIncludedMethods().add(the_include);
-        the_test.getXmlClasses().add(the_class);
-        suites.add(the_suite);
-        testng.setXmlSuites(suites);
-
-        return testng;
-    }
-
-    public static class Result {
-        private final boolean hasFailure;
-        private final Map<String,Throwable> failures;
-
-
-        Result(boolean hasFailure, Map<String, Throwable> failures) {
-            this.hasFailure = hasFailure;
-            this.failures = Collections.unmodifiableMap(new LinkedHashMap<>(failures));
-        }
-
-        public boolean hasFailure() {
-            return hasFailure;
-        }
-
-        public Map<String, Throwable> getFailures() {
-            return failures;
-        }
-    }
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/support/TestNgRunner.java b/tests/core/runner/src/com/android/cts/core/runner/support/TestNgRunner.java
deleted file mode 100644
index d9bf037..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/support/TestNgRunner.java
+++ /dev/null
@@ -1,235 +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.core.runner.support;
-
-import android.util.Log;
-
-import org.junit.runner.Description;
-import org.junit.runner.Runner;
-import org.junit.runner.manipulation.Filter;
-import org.junit.runner.manipulation.Filterable;
-import org.junit.runner.manipulation.NoTestsRemainException;
-import org.junit.runner.notification.Failure;
-import org.junit.runner.notification.RunNotifier;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.HashSet;
-import java.util.Map;
-
-/**
- * A {@link Runner} that can TestNG tests.
- *
- * <p>Implementation note: Avoid extending ParentRunner since that also has
- * logic to handle BeforeClass/AfterClass and other junit-specific functionality
- * that would be invalid for TestNG.</p>
- */
-class TestNgRunner extends Runner implements Filterable {
-
-  private static final boolean DEBUG = false;
-
-  private Description mDescription;
-  /** Class name for debugging. */
-  private String mClassName;
-  /** Don't include the same method names twice. */
-  private HashSet<String> mMethodSet = new HashSet<>();
-
-  /**
-   * @param testClass the test class to run
-   */
-  TestNgRunner(Class<?> testClass) {
-    mDescription = generateTestNgDescription(testClass);
-    mClassName = testClass.getName();
-  }
-
-  // Runner implementation
-  @Override
-  public Description getDescription() {
-    return mDescription;
-  }
-
-  // Runner implementation
-  @Override
-  public int testCount() {
-    if (!descriptionHasChildren(getDescription())) {  // Avoid NPE when description is null.
-      return 0;
-    }
-
-    // We always follow a flat Parent->Leaf hierarchy, so no recursion necessary.
-    return getDescription().testCount();
-  }
-
-  // Filterable implementation
-  @Override
-  public void filter(Filter filter) throws NoTestsRemainException {
-    mDescription = filterDescription(mDescription, filter);
-
-    if (!descriptionHasChildren(getDescription())) {  // Avoid NPE when description is null.
-      if (DEBUG) {
-        Log.d("TestNgRunner",
-            "Filtering has removed all tests :( for class " + mClassName);
-      }
-      throw new NoTestsRemainException();
-    }
-
-    if (DEBUG) {
-      Log.d("TestNgRunner",
-          "Filtering has retained " + testCount() + " tests for class " + mClassName);
-    }
-  }
-
-  // Filterable implementation
-  @Override
-  public void run(RunNotifier notifier) {
-    if (!descriptionHasChildren(getDescription())) {  // Avoid NPE when description is null.
-      // Nothing to do.
-      return;
-    }
-
-    for (Description child : getDescription().getChildren()) {
-      String className = child.getClassName();
-      String methodName = child.getMethodName();
-
-      Class<?> klass;
-      try {
-        klass = Class.forName(className, false, Thread.currentThread().getContextClassLoader());
-      } catch (ClassNotFoundException e) {
-        throw new AssertionError(e);
-      }
-
-      notifier.fireTestStarted(child);
-
-      // Avoid looking at all the methods by just using the string method name.
-      SingleTestNgTestExecutor.Result result = SingleTestNgTestExecutor.execute(klass, methodName);
-      if (result.hasFailure()) {
-        // TODO: get the error messages from testng somehow.
-        notifier.fireTestFailure(new Failure(child, extractException(result.getFailures())));
-      }
-
-      notifier.fireTestFinished(child);
-      // TODO: Check @Test(enabled=false) and invoke #fireTestIgnored instead.
-    }
-  }
-
-  private Throwable extractException(Map<String, Throwable> failures) {
-    if (failures.isEmpty()) {
-      return new AssertionError();
-    }
-    if (failures.size() == 1) {
-      return failures.values().iterator().next();
-    }
-
-    StringBuilder errorMessage = new StringBuilder("========== Multiple Failures ==========");
-    for (Map.Entry<String, Throwable> failureEntry : failures.entrySet()) {
-      errorMessage.append("\n\n=== "). append(failureEntry.getKey()).append(" ===\n");
-      Throwable throwable = failureEntry.getValue();
-      errorMessage
-              .append(throwable.getClass()).append(": ")
-              .append(throwable.getMessage());
-      for (StackTraceElement e : throwable.getStackTrace()) {
-        if (e.getClassName().equals(getClass().getName())) {
-          break;
-        }
-        errorMessage.append("\n  at ").append(e);
-      }
-    }
-    errorMessage.append("\n=======================================\n\n");
-    return new AssertionError(errorMessage.toString());
-  }
-
-
-  /**
-   * Recursively (preorder traversal) apply the filter to all the descriptions.
-   *
-   * @return null if the filter rejects the whole tree.
-   */
-  private static Description filterDescription(Description desc, Filter filter) {
-    if (!filter.shouldRun(desc)) {  // XX: Does the filter itself do the recursion?
-      return null;
-    }
-
-    Description newDesc = desc.childlessCopy();
-
-    // Return leafs.
-    if (!descriptionHasChildren(desc)) {
-      return newDesc;
-    }
-
-    // Filter all subtrees, only copying them if the filter accepts them.
-    for (Description child : desc.getChildren()) {
-      Description filteredChild = filterDescription(child, filter);
-
-      if (filteredChild != null) {
-        newDesc.addChild(filteredChild);
-      }
-    }
-
-    return newDesc;
-  }
-
-  private Description generateTestNgDescription(Class<?> cls) {
-    // Add the overall class description as the parent.
-    Description parent = Description.createSuiteDescription(cls);
-
-    if (DEBUG) {
-      Log.d("TestNgRunner", "Generating TestNg Description for class " + cls.getName());
-    }
-
-    // Add each test method as a child.
-    for (Method m : cls.getDeclaredMethods()) {
-
-      // Filter to only 'public void' signatures.
-      if ((m.getModifiers() & Modifier.PUBLIC) == 0) {
-        continue;
-      }
-
-      if (!m.getReturnType().equals(Void.TYPE)) {
-        continue;
-      }
-
-      // Note that TestNG methods may actually have parameters
-      // (e.g. with @DataProvider) which TestNG will populate itself.
-
-      // Add [Class, MethodName] as a Description leaf node.
-      String name = m.getName();
-
-      if (!mMethodSet.add(name)) {
-        // Overloaded methods have the same name, don't add them twice.
-        if (DEBUG) {
-          Log.d("TestNgRunner", "Already added child " + cls.getName() + "#" + name);
-        }
-        continue;
-      }
-
-      Description child = Description.createTestDescription(cls, name);
-
-      parent.addChild(child);
-
-      if (DEBUG) {
-        Log.d("TestNgRunner", "Add child " + cls.getName() + "#" + name);
-      }
-    }
-
-    return parent;
-  }
-
-  private static boolean descriptionHasChildren(Description desc) {
-    // Note: Although "desc.isTest()" is equivalent to "!desc.getChildren().isEmpty()"
-    // we add the pre-requisite 2 extra null checks to avoid throwing NPEs.
-    return desc != null && desc.getChildren() != null && !desc.getChildren().isEmpty();
-  }
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/support/TestNgRunnerBuilder.java b/tests/core/runner/src/com/android/cts/core/runner/support/TestNgRunnerBuilder.java
deleted file mode 100644
index 2f084b3..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/support/TestNgRunnerBuilder.java
+++ /dev/null
@@ -1,56 +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.core.runner.support;
-
-import java.lang.reflect.Method;
-
-import org.junit.runner.Runner;
-import org.junit.runners.model.RunnerBuilder;
-
-import org.testng.annotations.Test;
-
-/**
- * A {@link RunnerBuilder} that can handle TestNG tests.
- */
-public class TestNgRunnerBuilder extends RunnerBuilder {
-  // Returns a TestNG runner for this class, only if it is a class
-  // annotated with testng's @Test or has any methods with @Test in it.
-  @Override
-  public Runner runnerForClass(Class<?> testClass) {
-    if (isTestNgTestClass(testClass)) {
-      return new TestNgRunner(testClass);
-    }
-
-    return null;
-  }
-
-  private static boolean isTestNgTestClass(Class<?> cls) {
-    // TestNG test is either marked @Test at the class
-    if (cls.getAnnotation(Test.class) != null) {
-      return true;
-    }
-
-    // Or It's marked @Test at the method level
-    for (Method m : cls.getDeclaredMethods()) {
-      if (m.getAnnotation(Test.class) != null) {
-        return true;
-      }
-    }
-
-    return false;
-  }
-}
diff --git a/tests/core/runner/src/com/android/cts/runner/CrashParserRunListener.java b/tests/core/runner/src/com/android/cts/runner/CrashParserRunListener.java
deleted file mode 100644
index fbbb684..0000000
--- a/tests/core/runner/src/com/android/cts/runner/CrashParserRunListener.java
+++ /dev/null
@@ -1,39 +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.
- */
-
-package com.android.cts.runner;
-
-import android.support.test.internal.runner.listener.InstrumentationRunListener;
-import android.util.Log;
-import org.junit.runner.Description;
-
-/**
- * A {@link RunListener} for CrashParser. Dumps the test name to logs when
- * tests start.
- */
-public class CrashParserRunListener extends InstrumentationRunListener {
-
-    private static final String TAG = "CrashParserRunListener";
-
-    // Constant must be kept in sync with CrashUtils.java
-    public static final String NEW_TEST_ALERT = "New test starting with name: ";
-
-    @Override
-    public void testStarted(Description description) throws Exception {
-        Log.i(TAG, NEW_TEST_ALERT + description.toString());
-    }
-
-}
diff --git a/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java b/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java
deleted file mode 100644
index 1f9a939..0000000
--- a/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.runner;
-
-import android.app.ActivityManager;
-import android.app.Instrumentation;
-import android.app.KeyguardManager;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.support.test.internal.runner.listener.InstrumentationRunListener;
-import android.text.TextUtils;
-import android.util.Log;
-
-import junit.framework.TestCase;
-
-import org.junit.runner.Description;
-import org.junit.runner.notification.RunListener;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.lang.Class;
-import java.lang.ReflectiveOperationException;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.net.Authenticator;
-import java.net.CookieHandler;
-import java.net.ResponseCache;
-import java.text.DateFormat;
-import java.util.Locale;
-import java.util.Properties;
-import java.util.TimeZone;
-
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLSocketFactory;
-
-/**
- * A {@link RunListener} for CTS. Sets the system properties necessary for many
- * core tests to run. This is needed because there are some core tests that need
- * writing access to the file system.
- * Finally, we add a means to free memory allocated by a TestCase after its
- * execution.
- */
-public class CtsTestRunListener extends InstrumentationRunListener {
-
-    private static final String TAG = "CtsTestRunListener";
-
-    private TestEnvironment mEnvironment;
-    private Class<?> lastClass;
-
-    @Override
-    public void testRunStarted(Description description) throws Exception {
-        mEnvironment = new TestEnvironment(getInstrumentation().getTargetContext());
-
-        // We might want to move this to /sdcard, if is is mounted/writable.
-        File cacheDir = getInstrumentation().getTargetContext().getCacheDir();
-        System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
-
-        // attempt to disable keyguard, if current test has permission to do so
-        // TODO: move this to a better place, such as InstrumentationTestRunner
-        // ?
-        if (getInstrumentation().getContext().checkCallingOrSelfPermission(
-                android.Manifest.permission.DISABLE_KEYGUARD)
-                == PackageManager.PERMISSION_GRANTED) {
-            Log.i(TAG, "Disabling keyguard");
-            KeyguardManager keyguardManager =
-                    (KeyguardManager) getInstrumentation().getContext().getSystemService(
-                            Context.KEYGUARD_SERVICE);
-            keyguardManager.newKeyguardLock("cts").disableKeyguard();
-        } else {
-            Log.i(TAG, "Test lacks permission to disable keyguard. " +
-                    "UI based tests may fail if keyguard is up");
-        }
-    }
-
-    @Override
-    public void testStarted(Description description) throws Exception {
-        if (description.getTestClass() != lastClass) {
-            lastClass = description.getTestClass();
-            printMemory(description.getTestClass());
-        }
-
-        mEnvironment.reset();
-    }
-
-    @Override
-    public void testFinished(Description description) {
-        // no way to implement this in JUnit4...
-        // offending test cases that need this logic should probably be cleaned
-        // up individually
-        // if (test instanceof TestCase) {
-        // cleanup((TestCase) test);
-        // }
-    }
-
-    /**
-     * Dumps some memory info.
-     */
-    private void printMemory(Class<?> testClass) {
-        Runtime runtime = Runtime.getRuntime();
-
-        long total = runtime.totalMemory();
-        long free = runtime.freeMemory();
-        long used = total - free;
-
-        Log.d(TAG, "Total memory  : " + total);
-        Log.d(TAG, "Used memory   : " + used);
-        Log.d(TAG, "Free memory   : " + free);
-
-        String tempdir = System.getProperty("java.io.tmpdir", "");
-        // TODO: Remove these extra Logs added to debug a specific timeout problem.
-        Log.d(TAG, "java.io.tmpdir is:" + tempdir);
-
-        if (!TextUtils.isEmpty(tempdir)) {
-            String[] commands = {"df", tempdir};
-            BufferedReader in = null;
-            try {
-                Log.d(TAG, "About to .exec df");
-                Process proc = runtime.exec(commands);
-                Log.d(TAG, ".exec returned");
-                in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
-                Log.d(TAG, "Stream reader created");
-                String line;
-                while ((line = in.readLine()) != null) {
-                    Log.d(TAG, line);
-                }
-            } catch (IOException e) {
-                Log.d(TAG, "Exception: " + e.toString());
-                // Well, we tried
-            } finally {
-                Log.d(TAG, "In finally");
-                if (in != null) {
-                    try {
-                        in.close();
-                    } catch (IOException e) {
-                        // Meh
-                    }
-                }
-            }
-        }
-
-        Log.d(TAG, "Now executing : " + testClass.getName());
-    }
-
-    /**
-     * Nulls all non-static reference fields in the given test class. This
-     * method helps us with those test classes that don't have an explicit
-     * tearDown() method. Normally the garbage collector should take care of
-     * everything, but since JUnit keeps references to all test cases, a little
-     * help might be a good idea.
-     */
-    private void cleanup(TestCase test) {
-        Class<?> clazz = test.getClass();
-
-        while (clazz != TestCase.class) {
-            Field[] fields = clazz.getDeclaredFields();
-            for (int i = 0; i < fields.length; i++) {
-                Field f = fields[i];
-                if (!f.getType().isPrimitive() &&
-                        !Modifier.isStatic(f.getModifiers())) {
-                    try {
-                        f.setAccessible(true);
-                        f.set(test, null);
-                    } catch (Exception ignored) {
-                        // Nothing we can do about it.
-                    }
-                }
-            }
-
-            clazz = clazz.getSuperclass();
-        }
-    }
-
-    // http://code.google.com/p/vogar/source/browse/trunk/src/vogar/target/TestEnvironment.java
-    static class TestEnvironment {
-        private static final Field sDateFormatIs24HourField;
-        static {
-            try {
-                Class<?> dateFormatClass = Class.forName("java.text.DateFormat");
-                sDateFormatIs24HourField = dateFormatClass.getDeclaredField("is24Hour");
-            } catch (ReflectiveOperationException e) {
-                throw new AssertionError("Missing DateFormat.is24Hour", e);
-            }
-        }
-
-        private final Locale mDefaultLocale;
-        private final TimeZone mDefaultTimeZone;
-        private final HostnameVerifier mHostnameVerifier;
-        private final SSLSocketFactory mSslSocketFactory;
-        private final Properties mProperties = new Properties();
-        private final Boolean mDefaultIs24Hour;
-
-        TestEnvironment(Context context) {
-            mDefaultLocale = Locale.getDefault();
-            mDefaultTimeZone = TimeZone.getDefault();
-            mHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
-            mSslSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
-
-            mProperties.setProperty("user.home", "");
-            mProperties.setProperty("java.io.tmpdir", context.getCacheDir().getAbsolutePath());
-            // The CDD mandates that devices that support WiFi are the only ones that will have
-            // multicast.
-            PackageManager pm = context.getPackageManager();
-            mProperties.setProperty("android.cts.device.multicast",
-                    Boolean.toString(pm.hasSystemFeature(PackageManager.FEATURE_WIFI)));
-            mDefaultIs24Hour = getDateFormatIs24Hour();
-
-            // There are tests in libcore that should be disabled for low ram devices. They can't
-            // access ActivityManager to call isLowRamDevice, but can read system properties.
-            ActivityManager activityManager =
-                    (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
-            mProperties.setProperty("android.cts.device.lowram",
-                    Boolean.toString(activityManager.isLowRamDevice()));
-        }
-
-        void reset() {
-            System.setProperties(null);
-            System.setProperties(mProperties);
-            Locale.setDefault(mDefaultLocale);
-            TimeZone.setDefault(mDefaultTimeZone);
-            Authenticator.setDefault(null);
-            CookieHandler.setDefault(null);
-            ResponseCache.setDefault(null);
-            HttpsURLConnection.setDefaultHostnameVerifier(mHostnameVerifier);
-            HttpsURLConnection.setDefaultSSLSocketFactory(mSslSocketFactory);
-            setDateFormatIs24Hour(mDefaultIs24Hour);
-        }
-
-        private static Boolean getDateFormatIs24Hour() {
-            try {
-                return (Boolean) sDateFormatIs24HourField.get(null);
-            } catch (ReflectiveOperationException e) {
-                throw new AssertionError("Unable to get java.text.DateFormat.is24Hour", e);
-            }
-        }
-
-        private static void setDateFormatIs24Hour(Boolean value) {
-            try {
-                sDateFormatIs24HourField.set(null, value);
-            } catch (ReflectiveOperationException e) {
-                throw new AssertionError("Unable to set java.text.DateFormat.is24Hour", e);
-            }
-        }
-    }
-
-}
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
index a674c86..0e1a498 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -150,6 +150,9 @@
 
         <activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$SlowActivity"/>
 
+        <activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$NoDisplayActivity"
+                  android:theme="@android:style/Theme.NoDisplay" />
+
         <activity android:name="android.server.wm.StartActivityTests$TestActivity2" />
 
         <activity android:name="android.server.wm.MultiDisplaySystemDecorationTests$ImeTestActivity" />
@@ -233,6 +236,10 @@
             android:launchMode="standard"
             android:taskAffinity=".t1"/>
         <activity
+            android:name="android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity"
+            android:relinquishTaskIdentity="true"
+            android:taskAffinity=".t1"/>
+        <activity
             android:name="android.server.wm.intent.Activities$TaskAffinity2Activity"
             android:allowTaskReparenting="true"
             android:launchMode="standard"
@@ -264,6 +271,9 @@
             android:name="android.server.wm.intent.Activities$LauncherActivity"
             android:documentLaunchMode="always"
             android:launchMode="singleInstance"/>
+        <activity
+            android:name="android.server.wm.intent.Activities$RelinquishTaskIdentityActivity"
+            android:relinquishTaskIdentity="true"/>
 
         <service
             android:name="android.server.wm.TestLogService"
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
index 94b397f..08c32d1 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
@@ -273,6 +273,12 @@
         public static final String EXTRA_FONT_ACTIVITY_DPI = "fontActivityDpi";
     }
 
+    /** Extra key constants for {@link android.server.wm.app.TurnScreenOnActivity}. */
+    public static class TurnScreenOnActivity {
+        // Turn on screen by window flags or APIs.
+        public static final String EXTRA_USE_WINDOW_FLAGS = "useWindowFlags";
+    }
+
     /**
      *  Logging constants for {@link android.server.wm.app.KeyguardDismissLoggerCallback}.
      *
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/TurnScreenOnActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/TurnScreenOnActivity.java
index af827cd..3c82695 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/TurnScreenOnActivity.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/TurnScreenOnActivity.java
@@ -16,17 +16,22 @@
 
 package android.server.wm.app;
 
-import android.app.Activity;
 import android.os.Bundle;
 import android.view.WindowManager;
 
-public class TurnScreenOnActivity extends Activity {
+public class TurnScreenOnActivity extends AbstractLifecycleLogActivity {
     @Override
     protected void onCreate(Bundle savedInstanceState) {
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
-                | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
-                | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
-                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
         super.onCreate(savedInstanceState);
+
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        if (getIntent().getBooleanExtra(Components.TurnScreenOnActivity.EXTRA_USE_WINDOW_FLAGS,
+                false /* defaultValue */)) {
+            getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+                  | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
+        } else {
+            setShowWhenLocked(true);
+            setTurnScreenOn(true);
+        }
     }
 }
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/VirtualDisplayActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/VirtualDisplayActivity.java
index 269a151..e8019aa 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/VirtualDisplayActivity.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/VirtualDisplayActivity.java
@@ -21,6 +21,7 @@
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
 import static android.server.wm.ActivityLauncher.KEY_DISPLAY_ID;
 import static android.server.wm.ActivityLauncher.KEY_LAUNCH_ACTIVITY;
+import static android.server.wm.ActivityLauncher.KEY_NEW_TASK;
 import static android.server.wm.ActivityLauncher.KEY_TARGET_COMPONENT;
 import static android.server.wm.ComponentNameUtils.getActivityName;
 import static android.server.wm.app.Components.VirtualDisplayActivity.COMMAND_CREATE_DISPLAY;
@@ -255,6 +256,7 @@
         extras.putBoolean(KEY_LAUNCH_ACTIVITY, true);
         extras.putString(KEY_TARGET_COMPONENT, getActivityName(activityName));
         extras.putInt(KEY_DISPLAY_ID, displayId);
+        extras.putBoolean(KEY_NEW_TASK, true);
         ActivityLauncher.launchActivityFromExtras(this, extras);
     }
 }
diff --git a/tests/framework/base/windowmanager/intent_tests/clearCases/clear-task_with_new-task.json b/tests/framework/base/windowmanager/intent_tests/clearCases/clear-task_with_new-task.json
new file mode 100644
index 0000000..09753b8
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/clearCases/clear-task_with_new-task.json
@@ -0,0 +1,54 @@
+{
+    "setup": {
+        "initialIntents": [
+            {
+                "flags": "FLAG_ACTIVITY_NEW_TASK",
+                "class": "android.server.wm.intent.Activities$RegularActivity",
+                "package": "android.server.wm.cts",
+                "startForResult": false
+            }
+        ],
+        "act": [
+            {
+                "flags": "FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK",
+                "class": "android.server.wm.intent.Activities$RegularActivity",
+                "package": "android.server.wm.cts",
+                "startForResult": false
+            }
+        ]
+    },
+    "initialState": {
+        "stacks": [
+            {
+                "tasks": [
+                    {
+                        "activities": [
+                            {
+                                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+                                "state": "RESUMED"
+                            }
+                        ]
+                    }
+                ],
+                "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+            }
+        ]
+    },
+    "endState": {
+        "stacks": [
+            {
+                "tasks": [
+                    {
+                        "activities": [
+                            {
+                                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+                                "state": "RESUMED"
+                            }
+                        ]
+                    }
+                ],
+                "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+            }
+        ]
+    }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/clearCases/clear-task_without_new-task.json b/tests/framework/base/windowmanager/intent_tests/clearCases/clear-task_without_new-task.json
new file mode 100644
index 0000000..da82d52
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/clearCases/clear-task_without_new-task.json
@@ -0,0 +1,58 @@
+{
+    "setup": {
+        "initialIntents": [
+            {
+                "flags": "FLAG_ACTIVITY_NEW_TASK",
+                "class": "android.server.wm.intent.Activities$RegularActivity",
+                "package": "android.server.wm.cts",
+                "startForResult": false
+            }
+        ],
+        "act": [
+            {
+                "flags": "FLAG_ACTIVITY_CLEAR_TASK",
+                "class": "android.server.wm.intent.Activities$RegularActivity",
+                "package": "android.server.wm.cts",
+                "startForResult": false
+            }
+        ]
+    },
+    "initialState": {
+        "stacks": [
+            {
+                "tasks": [
+                    {
+                        "activities": [
+                            {
+                                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+                                "state": "RESUMED"
+                            }
+                        ]
+                    }
+                ],
+                "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+            }
+        ]
+    },
+    "endState": {
+        "stacks": [
+            {
+                "tasks": [
+                    {
+                        "activities": [
+                            {
+                                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+                                "state": "RESUMED"
+                            },
+                            {
+                                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+                                "state": "STOPPED"
+                            }
+                        ]
+                    }
+                ],
+                "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+            }
+        ]
+    }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/clearCases/test-3.json b/tests/framework/base/windowmanager/intent_tests/clearCases/test-3.json
index 28e6a48..42f1ffa 100644
--- a/tests/framework/base/windowmanager/intent_tests/clearCases/test-3.json
+++ b/tests/framework/base/windowmanager/intent_tests/clearCases/test-3.json
@@ -53,6 +53,10 @@
                             {
                                 "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
                                 "state": "RESUMED"
+                            },
+                            {
+                                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+                                "state": "STOPPED"
                             }
                         ]
                     }
diff --git a/tests/framework/base/windowmanager/intent_tests/newTask/request_new_task_different_affinity-new_task.json b/tests/framework/base/windowmanager/intent_tests/newTask/request_new_task_different_affinity-new_task.json
new file mode 100644
index 0000000..9d878d8
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/newTask/request_new_task_different_affinity-new_task.json
@@ -0,0 +1,66 @@
+{
+  "setup": {
+    "initialIntents": [
+      {
+        "flags": "FLAG_ACTIVITY_NEW_TASK",
+        "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      }
+    ],
+    "act": [
+      {
+        "flags": "FLAG_ACTIVITY_NEW_TASK",
+        "class": "android.server.wm.intent.Activities$RegularActivity",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      }
+    ]
+  },
+  "initialState": {
+    "stacks": [
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+                "state": "RESUMED"
+              }
+            ]
+          }
+        ],
+        "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity"
+      }
+    ]
+  },
+  "endState": {
+    "stacks": [
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+                "state": "RESUMED"
+              }
+            ]
+          }
+        ],
+        "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+      },
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+                "state": "STOPPED"
+              }
+            ]
+          }
+        ]
+      }
+    ]
+  }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/newTask/request_new_task_same_affinity-same_task.json b/tests/framework/base/windowmanager/intent_tests/newTask/request_new_task_same_affinity-same_task.json
new file mode 100644
index 0000000..6944973
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/newTask/request_new_task_same_affinity-same_task.json
@@ -0,0 +1,58 @@
+{
+  "setup": {
+    "initialIntents": [
+      {
+        "flags": "FLAG_ACTIVITY_NEW_TASK",
+        "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      }
+    ],
+    "act": [
+      {
+        "flags": "FLAG_ACTIVITY_NEW_TASK",
+        "class": "android.server.wm.intent.Activities$TaskAffinity1Activity2",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      }
+    ]
+  },
+  "initialState": {
+    "stacks": [
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+                "state": "RESUMED"
+              }
+            ]
+          }
+        ],
+        "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity"
+      }
+    ]
+  },
+  "endState": {
+    "stacks": [
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity2",
+                "state": "RESUMED"
+              },
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+                "state": "STOPPED"
+              }
+            ]
+          }
+        ],
+        "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity2"
+      }
+    ]
+  }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/newTask/request_same_task_different_affinity-same_task.json b/tests/framework/base/windowmanager/intent_tests/newTask/request_same_task_different_affinity-same_task.json
new file mode 100644
index 0000000..a444299
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/newTask/request_same_task_different_affinity-same_task.json
@@ -0,0 +1,58 @@
+{
+  "setup": {
+    "initialIntents": [
+      {
+        "flags": "FLAG_ACTIVITY_NEW_TASK",
+        "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      }
+    ],
+    "act": [
+      {
+        "flags": "",
+        "class": "android.server.wm.intent.Activities$RegularActivity",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      }
+    ]
+  },
+  "initialState": {
+    "stacks": [
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+                "state": "RESUMED"
+              }
+            ]
+          }
+        ],
+        "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity"
+      }
+    ]
+  },
+  "endState": {
+    "stacks": [
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+                "state": "RESUMED"
+              },
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+                "state": "STOPPED"
+              }
+            ]
+          }
+        ],
+        "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+      }
+    ]
+  }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/keep_affinity-request_new_task_with_same_affinity-same_task.json b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/keep_affinity-request_new_task_with_same_affinity-same_task.json
new file mode 100644
index 0000000..3dd2c99
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/keep_affinity-request_new_task_with_same_affinity-same_task.json
@@ -0,0 +1,72 @@
+{
+  "setup": {
+    "initialIntents": [
+      {
+        "flags": "FLAG_ACTIVITY_NEW_TASK",
+        "class": "android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      },
+      {
+        "flags": "",
+        "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      }
+    ],
+    "act": [
+      {
+        "flags": "FLAG_ACTIVITY_NEW_TASK",
+        "class": "android.server.wm.intent.Activities$TaskAffinity1Activity2",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      }
+    ]
+  },
+  "initialState": {
+    "stacks": [
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+                "state": "RESUMED"
+              },
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+                "state": "STOPPED"
+              }
+            ]
+          }
+        ],
+        "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity"
+      }
+    ]
+  },
+  "endState": {
+    "stacks": [
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity2",
+                "state": "RESUMED"
+              },
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+                "state": "STOPPED"
+              },
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+                "state": "STOPPED"
+              }
+            ]
+          }
+        ],
+        "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity2"
+      }
+    ]
+  }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_new_task_with_changed_affinity-new_task.json b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_new_task_with_changed_affinity-new_task.json
new file mode 100644
index 0000000..53f01b5
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_new_task_with_changed_affinity-new_task.json
@@ -0,0 +1,81 @@
+{
+  "comment": "relinquishTaskIdentity does not allow changing the root affinity of the task - ag/548224",
+  "setup": {
+    "initialIntents": [
+      {
+        "flags": "FLAG_ACTIVITY_NEW_TASK",
+        "class": "android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      },
+      {
+        "flags": "",
+        "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      }
+    ],
+    "act": [
+      {
+        "flags": "FLAG_ACTIVITY_NEW_TASK",
+        "class": "android.server.wm.intent.Activities$TaskAffinity1Activity2",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      }
+    ]
+  },
+  "initialState": {
+    "stacks": [
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+                "state": "RESUMED"
+              },
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+                "state": "STOPPED"
+              }
+            ]
+          }
+        ],
+        "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity"
+      }
+    ]
+  },
+  "endState": {
+    "stacks": [
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity2",
+                "state": "RESUMED"
+              }
+            ]
+          }
+        ],
+        "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity2"
+      },
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+                "state": "STOPPED"
+              },
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+                "state": "STOPPED"
+              }
+            ]
+          }
+        ]
+      }
+    ]
+  }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_new_task_with_old_affinity-same_task.json b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_new_task_with_old_affinity-same_task.json
new file mode 100644
index 0000000..2ec6f78
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_new_task_with_old_affinity-same_task.json
@@ -0,0 +1,73 @@
+{
+  "comment": "relinquishTaskIdentity does not allow changing the root affinity of the task - ag/548224",
+  "setup": {
+    "initialIntents": [
+      {
+        "flags": "FLAG_ACTIVITY_NEW_TASK",
+        "class": "android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      },
+      {
+        "flags": "",
+        "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      }
+    ],
+    "act": [
+      {
+        "flags": "FLAG_ACTIVITY_NEW_TASK",
+        "class": "android.server.wm.intent.Activities$RegularActivity",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      }
+    ]
+  },
+  "initialState": {
+    "stacks": [
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+                "state": "RESUMED"
+              },
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+                "state": "STOPPED"
+              }
+            ]
+          }
+        ],
+        "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity"
+      }
+    ]
+  },
+  "endState": {
+    "stacks": [
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+                "state": "RESUMED"
+              },
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+                "state": "STOPPED"
+              },
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+                "state": "STOPPED"
+              }
+            ]
+          }
+        ],
+        "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+      }
+    ]
+  }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_same_task_with_changed_affinity-same_task.json b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_same_task_with_changed_affinity-same_task.json
new file mode 100644
index 0000000..7e31db4
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_same_task_with_changed_affinity-same_task.json
@@ -0,0 +1,73 @@
+{
+  "comment": "relinquishTaskIdentity does not allow changing the root affinity of the task - ag/548224",
+  "setup": {
+    "initialIntents": [
+      {
+        "flags": "FLAG_ACTIVITY_NEW_TASK",
+        "class": "android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      },
+      {
+        "flags": "",
+        "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      }
+    ],
+    "act": [
+      {
+        "flags": "",
+        "class": "android.server.wm.intent.Activities$TaskAffinity1Activity2",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      }
+    ]
+  },
+  "initialState": {
+    "stacks": [
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+                "state": "RESUMED"
+              },
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+                "state": "STOPPED"
+              }
+            ]
+          }
+        ],
+        "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity"
+      }
+    ]
+  },
+  "endState": {
+    "stacks": [
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity2",
+                "state": "RESUMED"
+              },
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+                "state": "STOPPED"
+              },
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+                "state": "STOPPED"
+              }
+            ]
+          }
+        ],
+        "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity2"
+      }
+    ]
+  }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_same_task_with_old_affinity-same_task.json b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_same_task_with_old_affinity-same_task.json
new file mode 100644
index 0000000..afd155a
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_same_task_with_old_affinity-same_task.json
@@ -0,0 +1,73 @@
+{
+  "comment": "relinquishTaskIdentity does not allow changing the root affinity of the task - ag/548224",
+  "setup": {
+    "initialIntents": [
+      {
+        "flags": "FLAG_ACTIVITY_NEW_TASK",
+        "class": "android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      },
+      {
+        "flags": "",
+        "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      }
+    ],
+    "act": [
+      {
+        "flags": "",
+        "class": "android.server.wm.intent.Activities$RegularActivity",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      }
+    ]
+  },
+  "initialState": {
+    "stacks": [
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+                "state": "RESUMED"
+              },
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+                "state": "STOPPED"
+              }
+            ]
+          }
+        ],
+        "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity"
+      }
+    ]
+  },
+  "endState": {
+    "stacks": [
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+                "state": "RESUMED"
+              },
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+                "state": "STOPPED"
+              },
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+                "state": "STOPPED"
+              }
+            ]
+          }
+        ],
+        "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+      }
+    ]
+  }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_new_task_with_different_affinity-new_task.json b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_new_task_with_different_affinity-new_task.json
new file mode 100644
index 0000000..cd6a3a4
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_new_task_with_different_affinity-new_task.json
@@ -0,0 +1,66 @@
+{
+  "setup": {
+    "initialIntents": [
+      {
+        "flags": "FLAG_ACTIVITY_NEW_TASK",
+        "class": "android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      }
+    ],
+    "act": [
+      {
+        "flags": "FLAG_ACTIVITY_NEW_TASK",
+        "class": "android.server.wm.intent.Activities$RegularActivity",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      }
+    ]
+  },
+  "initialState": {
+    "stacks": [
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+                "state": "RESUMED"
+              }
+            ]
+          }
+        ],
+        "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity"
+      }
+    ]
+  },
+  "endState": {
+    "stacks": [
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+                "state": "RESUMED"
+              }
+            ]
+          }
+        ],
+        "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+      },
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+                "state": "STOPPED"
+              }
+            ]
+          }
+        ]
+      }
+    ]
+  }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_new_task_with_same_affinity-same_task.json b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_new_task_with_same_affinity-same_task.json
new file mode 100644
index 0000000..999ee82
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_new_task_with_same_affinity-same_task.json
@@ -0,0 +1,58 @@
+{
+  "setup": {
+    "initialIntents": [
+      {
+        "flags": "FLAG_ACTIVITY_NEW_TASK",
+        "class": "android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      }
+    ],
+    "act": [
+      {
+        "flags": "FLAG_ACTIVITY_NEW_TASK",
+        "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      }
+    ]
+  },
+  "initialState": {
+    "stacks": [
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+                "state": "RESUMED"
+              }
+            ]
+          }
+        ],
+        "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity"
+      }
+    ]
+  },
+  "endState": {
+    "stacks": [
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+                "state": "RESUMED"
+              },
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+                "state": "STOPPED"
+              }
+            ]
+          }
+        ],
+        "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity"
+      }
+    ]
+  }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_same_task_with_different_affinity-same_task.json b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_same_task_with_different_affinity-same_task.json
new file mode 100644
index 0000000..d0ec60c
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_same_task_with_different_affinity-same_task.json
@@ -0,0 +1,58 @@
+{
+  "setup": {
+    "initialIntents": [
+      {
+        "flags": "FLAG_ACTIVITY_NEW_TASK",
+        "class": "android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      }
+    ],
+    "act": [
+      {
+        "flags": "",
+        "class": "android.server.wm.intent.Activities$RegularActivity",
+        "package": "android.server.wm.cts",
+        "startForResult": false
+      }
+    ]
+  },
+  "initialState": {
+    "stacks": [
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+                "state": "RESUMED"
+              }
+            ]
+          }
+        ],
+        "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity"
+      }
+    ]
+  },
+  "endState": {
+    "stacks": [
+      {
+        "tasks": [
+          {
+            "activities": [
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+                "state": "RESUMED"
+              },
+              {
+                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+                "state": "STOPPED"
+              }
+            ]
+          }
+        ],
+        "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+      }
+    ]
+  }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/reorderToFront/reorder-to-front.json b/tests/framework/base/windowmanager/intent_tests/reorderToFront/reorder-to-front.json
new file mode 100644
index 0000000..9daaa0f
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/reorderToFront/reorder-to-front.json
@@ -0,0 +1,96 @@
+{
+    "setup": {
+        "initialIntents": [
+            {
+                "flags": "FLAG_ACTIVITY_NEW_TASK",
+                "class": "android.server.wm.intent.Activities$RegularActivity",
+                "package": "android.server.wm.cts",
+                "startForResult": false
+            },
+            {
+                "flags": "",
+                "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+                "package": "android.server.wm.cts",
+                "startForResult": false
+            },
+            {
+                "flags": "",
+                "class": "android.server.wm.intent.Activities$TaskAffinity2Activity",
+                "package": "android.server.wm.cts",
+                "startForResult": false
+            },
+            {
+                "flags": "",
+                "class": "android.server.wm.intent.Activities$RegularActivity",
+                "package": "android.server.wm.cts",
+                "startForResult": false
+            }
+        ],
+        "act": [
+            {
+                "flags": "FLAG_ACTIVITY_REORDER_TO_FRONT",
+                "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+                "package": "android.server.wm.cts",
+                "startForResult": false
+            }
+        ]
+    },
+    "initialState": {
+        "stacks": [
+            {
+                "tasks": [
+                    {
+                        "activities": [
+                            {
+                                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+                                "state": "RESUMED"
+                            },
+                            {
+                                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity2Activity",
+                                "state": "STOPPED"
+                            },
+                            {
+                                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+                                "state": "STOPPED"
+                            },
+                            {
+                                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+                                "state": "STOPPED"
+                            }
+                        ]
+                    }
+                ],
+                "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+            }
+        ]
+    },
+    "endState": {
+        "stacks": [
+            {
+                "tasks": [
+                    {
+                        "activities": [
+                            {
+                                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+                                "state": "RESUMED"
+                            },
+                            {
+                                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+                                "state": "STOPPED"
+                            },
+                            {
+                                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity2Activity",
+                                "state": "STOPPED"
+                            },
+                            {
+                                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+                                "state": "STOPPED"
+                            }
+                        ]
+                    }
+                ],
+                "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity"
+            }
+        ]
+    }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/reorderToFront/reorder-to-front_with_new-task_on_different_affinity.json b/tests/framework/base/windowmanager/intent_tests/reorderToFront/reorder-to-front_with_new-task_on_different_affinity.json
new file mode 100644
index 0000000..ab9ae9b
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/reorderToFront/reorder-to-front_with_new-task_on_different_affinity.json
@@ -0,0 +1,95 @@
+{
+    "comment": "A new activity should be created on a new task, without interfering the activity order of caller's task.",
+    "setup": {
+        "initialIntents": [
+            {
+                "flags": "FLAG_ACTIVITY_NEW_TASK",
+                "class": "android.server.wm.intent.Activities$RegularActivity",
+                "package": "android.server.wm.cts",
+                "startForResult": false
+            },
+            {
+                "flags": "",
+                "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+                "package": "android.server.wm.cts",
+                "startForResult": false
+            },
+            {
+                "flags": "",
+                "class": "android.server.wm.intent.Activities$TaskAffinity2Activity",
+                "package": "android.server.wm.cts",
+                "startForResult": false
+            }
+        ],
+        "act": [
+            {
+                "flags": "FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_REORDER_TO_FRONT",
+                "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+                "package": "android.server.wm.cts",
+                "startForResult": false
+            }
+        ]
+    },
+    "initialState": {
+        "stacks": [
+            {
+                "tasks": [
+                    {
+                        "activities": [
+                            {
+                                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity2Activity",
+                                "state": "RESUMED"
+                            },
+                            {
+                                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+                                "state": "STOPPED"
+                            },
+                            {
+                                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+                                "state": "STOPPED"
+                            }
+                        ]
+                    }
+                ],
+                "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity2Activity"
+            }
+        ]
+    },
+    "endState": {
+        "stacks": [
+            {
+                "tasks": [
+                    {
+                        "activities": [
+                            {
+                                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+                                "state": "RESUMED"
+                            }
+                        ]
+                    }
+                ],
+                "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity"
+            },
+            {
+                "tasks": [
+                    {
+                        "activities": [
+                            {
+                                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity2Activity",
+                                "state": "STOPPED"
+                            },
+                            {
+                                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+                                "state": "STOPPED"
+                            },
+                            {
+                                "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+                                "state": "STOPPED"
+                            }
+                        ]
+                    }
+                ]
+            }
+        ]
+    }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-1.json b/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-1.json
index 601134c..2cf1652 100644
--- a/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-1.json
+++ b/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-1.json
@@ -55,7 +55,12 @@
                                 "state": "RESUMED"
                             }
                         ]
-                    },
+                    }
+                ],
+                "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+            },
+            {
+                "tasks": [
                     {
                         "activities": [
                             {
@@ -64,8 +69,7 @@
                             }
                         ]
                     }
-                ],
-                "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+                ]
             }
         ]
     }
diff --git a/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-3.json b/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-3.json
index 7364d63..c89cdd3 100644
--- a/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-3.json
+++ b/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-3.json
@@ -73,7 +73,12 @@
                                 "state": "RESUMED"
                             }
                         ]
-                    },
+                    }
+                ],
+                "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+            },
+            {
+                "tasks": [
                     {
                         "activities": [
                             {
@@ -82,8 +87,7 @@
                             }
                         ]
                     }
-                ],
-                "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+                ]
             },
             {
                 "tasks": [
diff --git a/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-4.json b/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-4.json
index 62c1441..d8f3f7f 100644
--- a/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-4.json
+++ b/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-4.json
@@ -65,7 +65,12 @@
                                 "state": "RESUMED"
                             }
                         ]
-                    },
+                    }
+                ],
+                "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+            },
+            {
+                "tasks": [
                     {
                         "activities": [
                             {
@@ -78,8 +83,7 @@
                             }
                         ]
                     }
-                ],
-                "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+                ]
             }
         ]
     }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ActivityMetricsLoggerTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ActivityMetricsLoggerTests.java
index b827353..13713f4 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ActivityMetricsLoggerTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ActivityMetricsLoggerTests.java
@@ -61,8 +61,6 @@
 import android.support.test.metricshelper.MetricsAsserts;
 import android.util.EventLog.Event;
 
-import androidx.test.filters.FlakyTest;
-
 import com.android.compatibility.common.util.SystemUtil;
 
 import org.hamcrest.collection.IsIn;
@@ -215,7 +213,6 @@
      * metrics logs. Verify we output the correct launch state.
      */
     @Test
-    @FlakyTest(bugId = 130764822)
     public void testAppWarmLaunchSetsWaitResultDelayData() {
         SystemUtil.runShellCommand("am start -S -W " + TEST_ACTIVITY.flattenToShortString());
 
@@ -252,7 +249,6 @@
      * metrics logs. Verify we output the correct launch state.
      */
     @Test
-    @FlakyTest(bugId = 130764822)
     public void testAppHotLaunchSetsWaitResultDelayData() {
         SystemUtil.runShellCommand("am start -S -W " + TEST_ACTIVITY.flattenToShortString());
 
@@ -289,7 +285,6 @@
      * metrics logs. Verify we output the correct launch state.
      */
     @Test
-    @FlakyTest(bugId = 130764822)
     public void testAppColdLaunchSetsWaitResultDelayData() {
         final String amStartOutput = SystemUtil.runShellCommand(
                 "am start -S -W " + TEST_ACTIVITY.flattenToShortString());
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ActivityViewTest.java b/tests/framework/base/windowmanager/src/android/server/wm/ActivityViewTest.java
index c2deb24..0664b52 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ActivityViewTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ActivityViewTest.java
@@ -128,18 +128,16 @@
                 boundsMatched);
     }
 
+    /** @return {@code true} if the display size for the activity matches the given size. */
     private boolean checkDisplaySize(ComponentName activity, int requestedWidth,
             int requestedHeight) {
-        final int maxTries = 5;
-        final int retryIntervalMs = 1000;
-
-        boolean boundsMatched = false;
-
         // Display size for the activity may not get updated right away. Retry in case.
-        for (int i = 0; i < maxTries; i++) {
-            mAmWmState.getWmState().computeState();
-            int displayId = mAmWmState.getAmState().getDisplayByActivity(activity);
-            WindowManagerState.Display display = mAmWmState.getWmState().getDisplay(displayId);
+        return Condition.waitFor("display size=" + requestedWidth + "x" + requestedHeight, () -> {
+            final WindowManagerState wmState = mAmWmState.getWmState();
+            wmState.computeState();
+
+            final int displayId = mAmWmState.getAmState().getDisplayByActivity(activity);
+            final WindowManagerState.Display display = wmState.getDisplay(displayId);
             int avDisplayWidth = 0;
             int avDisplayHeight = 0;
             if (display != null) {
@@ -149,16 +147,8 @@
                     avDisplayHeight = bounds.height();
                 }
             }
-            boundsMatched = avDisplayWidth == requestedWidth && avDisplayHeight == requestedHeight;
-            if (boundsMatched) {
-                return true;
-            }
-
-            // wait and try again
-            SystemClock.sleep(retryIntervalMs);
-        }
-
-        return boundsMatched;
+            return avDisplayWidth == requestedWidth && avDisplayHeight == requestedHeight;
+        });
     }
 
     @Test
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java
index 523e660..1eddce9 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java
@@ -59,8 +59,9 @@
 import static org.junit.Assume.assumeTrue;
 
 import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.FlakyTest;
+import android.server.wm.CommandSession.ActivitySession;
+import android.server.wm.CommandSession.ActivitySessionClient;
+import android.server.wm.app.Components;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -76,7 +77,6 @@
     public final DisableScreenDozeRule mDisableScreenDozeRule = new DisableScreenDozeRule();
 
     @Test
-    @FlakyTest(bugId = 110276714)
     public void testTranslucentActivityOnTopOfPinnedStack() throws Exception {
         if (!supportsPip()) {
             return;
@@ -177,19 +177,34 @@
     }
 
     @Test
-    @FlakyTest(bugId = 110276714)
-    public void testTurnScreenOnActivity() throws Exception {
-        try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
-            lockScreenSession.sleepDevice();
-            launchActivity(TURN_SCREEN_ON_ACTIVITY);
-
-            mAmWmState.assertVisibility(TURN_SCREEN_ON_ACTIVITY, true);
-            assertTrue("Display turns on", isDisplayOn(DEFAULT_DISPLAY));
+    public void testTurnScreenOnActivity() {
+        try (final LockScreenSession lockScreenSession = new LockScreenSession();
+             final ActivitySessionClient activityClient = new ActivitySessionClient(mContext)) {
+            testTurnScreenOnActivity(lockScreenSession, activityClient, true /* useWindowFlags */);
+            testTurnScreenOnActivity(lockScreenSession, activityClient, false /* useWindowFlags */);
         }
     }
 
+    private void testTurnScreenOnActivity(LockScreenSession lockScreenSession,
+            ActivitySessionClient activitySessionClient, boolean useWindowFlags) {
+        lockScreenSession.sleepDevice();
+
+        final ActivitySession activity = activitySessionClient.startActivity(
+                getLaunchActivityBuilder()
+                        .setUseInstrumentation()
+                        .setIntentExtra(extra -> extra.putBoolean(
+                                Components.TurnScreenOnActivity.EXTRA_USE_WINDOW_FLAGS,
+                                useWindowFlags))
+                        .setTargetActivity(TURN_SCREEN_ON_ACTIVITY));
+
+        mAmWmState.assertVisibility(TURN_SCREEN_ON_ACTIVITY, true);
+        assertTrue("Display turns on by " + (useWindowFlags ? "flags" : "APIs"),
+                isDisplayOn(DEFAULT_DISPLAY));
+
+        activity.finish();
+    }
+
     @Test
-    @FlakyTest(bugId = 110276714)
     public void testFinishActivityInNonFocusedStack() throws Exception {
         if (!supportsSplitScreenMultiWindow()) {
             // Skipping test: no multi-window support
@@ -235,13 +250,11 @@
     }
 
     @Test
-    @FlakyTest
     public void testFinishActivityWithMoveTaskToBackAfterPause() throws Exception {
         performFinishActivityWithMoveTaskToBack(FINISH_POINT_ON_PAUSE);
     }
 
     @Test
-    @FlakyTest
     public void testFinishActivityWithMoveTaskToBackAfterStop() throws Exception {
         performFinishActivityWithMoveTaskToBack(FINISH_POINT_ON_STOP);
     }
@@ -428,7 +441,6 @@
     }
 
     @Test
-    @FlakyTest
     public void testTurnScreenOnAttrRemove() throws Exception {
         try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
             lockScreenSession.sleepDevice();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsAppOpsTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsAppOpsTests.java
index 10c6fc8..324398b 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsAppOpsTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsAppOpsTests.java
@@ -91,10 +91,11 @@
         final AlertWindowsAppOpsTestsActivity activity = mActivityRule.getActivity();
 
         // Start watching for app op
-        appOpsManager.startWatchingActive(new int[] {OP_SYSTEM_ALERT_WINDOW}, listener);
+        appOpsManager.startWatchingActive(new String[] { OPSTR_SYSTEM_ALERT_WINDOW },
+                getInstrumentation().getContext().getMainExecutor(), listener);
 
         // Assert the app op is not started
-        assertFalse(appOpsManager.isOperationActive(OP_SYSTEM_ALERT_WINDOW, uid, packageName));
+        assertFalse(appOpsManager.isOpActive(OPSTR_SYSTEM_ALERT_WINDOW, uid, packageName));
 
 
         // Show a system alert window.
@@ -102,11 +103,11 @@
 
         // The app op should start
         verify(listener, timeout(APP_OP_CHANGE_TIMEOUT_MILLIS)
-                .only()).onOpActiveChanged(eq(OP_SYSTEM_ALERT_WINDOW),
+                .only()).onOpActiveChanged(eq(OPSTR_SYSTEM_ALERT_WINDOW),
                 eq(uid), eq(packageName), eq(true));
 
         // The app op should be reported as started
-        assertTrue(appOpsManager.isOperationActive(OP_SYSTEM_ALERT_WINDOW,
+        assertTrue(appOpsManager.isOpActive(OPSTR_SYSTEM_ALERT_WINDOW,
                 uid, packageName));
 
 
@@ -118,11 +119,11 @@
 
         // The app op should finish
         verify(listener, timeout(APP_OP_CHANGE_TIMEOUT_MILLIS)
-                .only()).onOpActiveChanged(eq(OP_SYSTEM_ALERT_WINDOW),
+                .only()).onOpActiveChanged(eq(OPSTR_SYSTEM_ALERT_WINDOW),
                 eq(uid), eq(packageName), eq(false));
 
         // The app op should be reported as finished
-        assertFalse(appOpsManager.isOperationActive(OP_SYSTEM_ALERT_WINDOW, uid, packageName));
+        assertFalse(appOpsManager.isOpActive(OPSTR_SYSTEM_ALERT_WINDOW, uid, packageName));
 
 
         // Start with a clean slate
@@ -136,10 +137,10 @@
 
         // No other callbacks expected
         verify(listener, timeout(APP_OP_CHANGE_TIMEOUT_MILLIS).times(0))
-                .onOpActiveChanged(eq(OP_SYSTEM_ALERT_WINDOW),
+                .onOpActiveChanged(eq(OPSTR_SYSTEM_ALERT_WINDOW),
                         anyInt(), anyString(), anyBoolean());
 
         // The app op should be reported as started
-        assertTrue(appOpsManager.isOperationActive(OP_SYSTEM_ALERT_WINDOW, uid, packageName));
+        assertTrue(appOpsManager.isOpActive(OPSTR_SYSTEM_ALERT_WINDOW, uid, packageName));
     }
 }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AmStartOptionsTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AmStartOptionsTests.java
index 839371f..e8bd4a4 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AmStartOptionsTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AmStartOptionsTests.java
@@ -70,7 +70,6 @@
     }
 
     @Test
-    @FlakyTest
     public void testDashW_Indirect() throws Exception {
         testDashW(ENTRY_POINT_ALIAS_ACTIVITY, SINGLE_TASK_ACTIVITY);
     }
@@ -120,4 +119,4 @@
         waitAndAssertTopResumedActivity(actualActivity, DEFAULT_DISPLAY,
                 "Activity must be launched");
     }
-}
\ No newline at end of file
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AnimationBackgroundTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AnimationBackgroundTests.java
index 2126f83..b9de595 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AnimationBackgroundTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AnimationBackgroundTests.java
@@ -52,14 +52,11 @@
         assumeActivityNotInFreeformDisplay(ANIMATION_TEST_ACTIVITY);
 
         // Make sure we are in the middle of the animation.
-        mAmWmState.waitForWithWmState(state -> state
-                .getStandardStackByWindowingMode(WINDOWING_MODE_FULLSCREEN)
+        assertTrue("window animation background needs to be showing",
+                mAmWmState.waitForWithWmState(state -> state
+                        .getStandardStackByWindowingMode(WINDOWING_MODE_FULLSCREEN)
                         .isWindowAnimationBackgroundSurfaceShowing(),
-                "***Waiting for animation background showing");
-
-        assertTrue("window animation background needs to be showing", mAmWmState.getWmState()
-                .getStandardStackByWindowingMode(WINDOWING_MODE_FULLSCREEN)
-                .isWindowAnimationBackgroundSurfaceShowing());
+                        "animation background showing"));
     }
 
     @Test
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
index d6489a8..2056380 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
@@ -58,8 +58,6 @@
 import android.platform.test.annotations.Presubmit;
 import android.server.wm.CommandSession.SizeInfo;
 
-import androidx.test.filters.FlakyTest;
-
 import org.junit.Ignore;
 import org.junit.Test;
 
@@ -104,7 +102,6 @@
      * from docked state to fullscreen (reverse).
      */
     @Test
-    @FlakyTest(bugId = 71792393)
     public void testConfigurationUpdatesWhenResizedFromDockedStack() {
         assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
 
@@ -276,7 +273,6 @@
      * relaunched twice and it should have same config as initial one.
      */
     @Test
-    @FlakyTest
     public void testSameConfigurationSplitFullSplitRelaunch() {
         moveActivitySplitFullSplit(TEST_ACTIVITY);
     }
@@ -285,7 +281,6 @@
      * Same as {@link #testSameConfigurationSplitFullSplitRelaunch} but without relaunch.
      */
     @Test
-    @FlakyTest
     public void testSameConfigurationSplitFullSplitNoRelaunch() {
         moveActivitySplitFullSplit(RESIZEABLE_ACTIVITY);
     }
@@ -295,7 +290,6 @@
      * screen.
      */
     @Test
-    @FlakyTest(bugId = 110276714)
     public void testDialogWhenLargeSplitSmall() {
         assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
 
@@ -320,7 +314,6 @@
      * Test that device handles consequent requested orientations and displays the activities.
      */
     @Test
-    @FlakyTest(bugId = 71875755)
     public void testFullscreenAppOrientationRequests() {
         assumeTrue("Skipping test: no rotation support", supportsRotation());
 
@@ -374,7 +367,6 @@
      * change to an invisible activity.
      */
     @Test
-    @FlakyTest
     public void testAppOrientationRequestConfigChanges() {
         assumeTrue("Skipping test: no rotation support", supportsRotation());
 
@@ -571,7 +563,6 @@
      * Also verify that occluded activity will not get config changes.
      */
     @Test
-    @FlakyTest
     public void testAppOrientationWhenRotating() throws Exception {
         assumeTrue("Skipping test: no rotation support", supportsRotation());
 
@@ -660,7 +651,6 @@
      * Test that device handles moving between two tasks with different orientations.
      */
     @Test
-    @FlakyTest(bugId = 71792393)
     public void testTaskMoveToBackOrientation() {
         assumeTrue("Skipping test: no rotation support", supportsRotation());
 
@@ -690,7 +680,6 @@
     /**
      * Test that device doesn't change device orientation by app request while in multi-window.
      */
-    @FlakyTest(bugId = 71918731)
     @Test
     public void testSplitscreenPortraitAppOrientationRequests() throws Exception {
         assumeTrue("Skipping test: no rotation support", supportsRotation());
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java
index 9662980..d850ed7 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java
@@ -98,7 +98,6 @@
         }
     }
 
-    @FlakyTest(bugId = 69573940)
     @Test
     public void testAssistantStackZOrder() throws Exception {
         assumeTrue(assistantRunsOnPrimaryDisplay());
@@ -189,7 +188,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 71875631)
     public void testAssistantStackFinishToPreviousApp() throws Exception {
         // Launch an assistant activity on top of an existing fullscreen activity, and ensure that
         // the fullscreen activity is still visible and on top after the assistant activity finishes
@@ -200,7 +198,7 @@
             launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
                     EXTRA_ASSISTANT_FINISH_SELF, "true");
             mAmWmState.waitFor((amState, wmState) -> !amState.containsActivity(ASSISTANT_ACTIVITY),
-                    "Waiting for " + getActivityName(ASSISTANT_ACTIVITY) + " finished");
+                    getActivityName(ASSISTANT_ACTIVITY) + " finished");
         }
         waitForValidStateWithActivityTypeAndWindowingMode(
                 TEST_ACTIVITY, ACTIVITY_TYPE_STANDARD, WINDOWING_MODE_FULLSCREEN);
@@ -214,7 +212,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 71875631)
     public void testDisallowEnterPiPFromAssistantStack() throws Exception {
         try (final AssistantSession assistantSession = new AssistantSession()) {
             assistantSession.setVoiceInteractionService(ASSISTANT_VOICE_INTERACTION_SERVICE);
@@ -300,7 +297,6 @@
         }
     }
 
-    @FlakyTest(bugId = 69229402)
     @Test
     public void testLaunchIntoSameTask() throws Exception {
         try (final AssistantSession assistantSession = new AssistantSession()) {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java
index bc84c83..8fc36e8 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java
@@ -46,7 +46,6 @@
 import android.server.wm.settings.SettingsSession;
 
 import androidx.annotation.IntDef;
-import androidx.test.filters.FlakyTest;
 
 import com.android.compatibility.common.util.SystemUtil;
 
@@ -119,7 +118,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 110533226, detail = "Promote to presubmit once confirm it's not flaky")
     public void testRotation180RelaunchWithCutout() throws Exception {
         assumeTrue("Skipping test: no rotation support", supportsRotation());
         assumeTrue("Skipping test: no display cutout", hasDisplayCutout());
@@ -128,7 +126,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 110533226, detail = "Promote to presubmit once confirm it's not flaky")
     public void testRotation180NoRelaunchWithCutout() throws Exception {
         assumeTrue("Skipping test: no rotation support", supportsRotation());
         assumeTrue("Skipping test: no display cutout", hasDisplayCutout());
@@ -141,7 +138,6 @@
      * reverse-landscape rotations should result in same screen space available for apps.
      */
     @Test
-    @FlakyTest(bugId = 110533226, detail = "Promote to presubmit once confirm it's not flaky")
     public void testConfigChangeWhenRotatingWithCutout() throws Exception {
         assumeTrue("Skipping test: no rotation support", supportsRotation());
         assumeTrue("Skipping test: no display cutout", hasDisplayCutout());
@@ -320,7 +316,7 @@
                 logE("Error waiting for valid state: " + e.getMessage());
                 return false;
             }
-        }, "Waiting asset sequence number to be updated and for activity to be resumed.");
+        }, "asset sequence number to be updated and for activity to be resumed.");
 
         // Check if activity is relaunched and asset seq is updated.
         assertRelaunchOrConfigChanged(TEST_ACTIVITY, 1 /* numRelaunch */,
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTestBase.java
index d5f7709..5ad4c01 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTestBase.java
@@ -16,16 +16,12 @@
 
 package android.server.wm;
 
-import static android.server.wm.StateLogger.logAlways;
 import static android.server.wm.app.Components.KeyguardDismissLoggerCallback.ENTRY_ON_DISMISS_CANCELLED;
 import static android.server.wm.app.Components.KeyguardDismissLoggerCallback.ENTRY_ON_DISMISS_ERROR;
 import static android.server.wm.app.Components.KeyguardDismissLoggerCallback.ENTRY_ON_DISMISS_SUCCEEDED;
 
-import static org.junit.Assert.fail;
-
 import android.app.KeyguardManager;
 import android.content.ComponentName;
-import android.os.SystemClock;
 import android.server.wm.TestJournalProvider.TestJournalContainer;
 
 class KeyguardTestBase extends ActivityManagerTestBase {
@@ -51,14 +47,7 @@
     }
 
     private static void assertDismissCallback(ComponentName testingComponentName, String entry) {
-        for (int retry = 1; retry <= 5; retry++) {
-            if (TestJournalContainer.get(testingComponentName).extras
-                    .getBoolean(entry)) {
-                return;
-            }
-            logAlways("Waiting for " + entry + "... retry=" + retry);
-            SystemClock.sleep(500);
-        }
-        fail("Waiting for " + entry + " failed");
+        waitForOrFail(entry + " of " + testingComponentName,
+                () -> TestJournalContainer.get(testingComponentName).extras.getBoolean(entry));
     }
 }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java
index c608991..1193a73 100755
--- a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java
@@ -553,7 +553,7 @@
             // The activity may not be destroyed immediately.
             mAmWmState.waitForWithWmState(
                     wmState -> !wmState.containsWindow(getWindowName(SHOW_WHEN_LOCKED_ACTIVITY)),
-                    "Waiting for " + getActivityName(SHOW_WHEN_LOCKED_ACTIVITY) + " to be removed");
+                    getActivityName(SHOW_WHEN_LOCKED_ACTIVITY) + " to be removed");
             // The {@link SHOW_WHEN_LOCKED_ACTIVITY} has gone because of {@link pressBackButton()}.
             mAmWmState.assertNotExist(SHOW_WHEN_LOCKED_ACTIVITY);
         }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/LayoutTests.java b/tests/framework/base/windowmanager/src/android/server/wm/LayoutTests.java
index 5513de7..b9d84d7 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/LayoutTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/LayoutTests.java
@@ -54,7 +54,6 @@
  * Build/Install/Run:
  *     atest CtsWindowManagerDeviceTestCases:LayoutTests
  */
-@FlakyTest(detail = "Can be promoted to pre-submit once confirmed stable.")
 @AppModeFull(reason = "Cannot write global settings as an instant app.")
 @Presubmit
 public class LayoutTests extends WindowManagerTestBase {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/LocationOnScreenTests.java b/tests/framework/base/windowmanager/src/android/server/wm/LocationOnScreenTests.java
index ad3995c..9c9491c 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/LocationOnScreenTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/LocationOnScreenTests.java
@@ -64,7 +64,6 @@
 
 import java.util.function.Supplier;
 
-@FlakyTest(detail = "until proven non-flaky")
 @SmallTest
 @Presubmit
 public class LocationOnScreenTests {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
index 67f1db8..9d6f2d4 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
@@ -19,8 +19,11 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
 import static android.server.wm.ActivityLauncher.KEY_LAUNCH_ACTIVITY;
 import static android.server.wm.ActivityLauncher.KEY_NEW_TASK;
 import static android.server.wm.ActivityManagerState.STATE_RESUMED;
@@ -35,6 +38,7 @@
 import static android.server.wm.app.Components.SINGLE_TASK_INSTANCE_DISPLAY_ACTIVITY2;
 import static android.server.wm.app.Components.SINGLE_TASK_INSTANCE_DISPLAY_ACTIVITY3;
 import static android.server.wm.app.Components.TEST_ACTIVITY;
+import static android.server.wm.app.Components.TOP_ACTIVITY;
 import static android.server.wm.app.Components.VIRTUAL_DISPLAY_ACTIVITY;
 import static android.server.wm.second.Components.SECOND_ACTIVITY;
 import static android.server.wm.second.Components.SECOND_LAUNCH_BROADCAST_ACTION;
@@ -42,6 +46,8 @@
 import static android.server.wm.third.Components.THIRD_ACTIVITY;
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -49,8 +55,11 @@
 import static org.junit.Assume.assumeTrue;
 
 import android.app.ActivityOptions;
+import android.app.PendingIntent;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
+import android.hardware.display.DisplayManager;
 import android.os.Bundle;
 import android.platform.test.annotations.Presubmit;
 import android.server.wm.ActivityManagerState.ActivityDisplay;
@@ -257,12 +266,11 @@
             // Try to move the non-resizeable activity to the top of stack on secondary display.
             moveActivityToStack(NON_RESIZEABLE_ACTIVITY, externalFrontStackId);
             // Wait for a while to check that it will move.
-            mAmWmState.waitForWithAmState(state ->
-                    newDisplay.mId == state.getDisplayByActivity(NON_RESIZEABLE_ACTIVITY),
-                    "Waiting to see if activity is moved");
-            assertEquals("Non-resizeable activity should be moved",
-                    newDisplay.mId,
-                    mAmWmState.getAmState().getDisplayByActivity(NON_RESIZEABLE_ACTIVITY));
+            assertTrue("Non-resizeable activity should be moved",
+                    mAmWmState.waitForWithAmState(
+                            state -> newDisplay.mId == state
+                                    .getDisplayByActivity(NON_RESIZEABLE_ACTIVITY),
+                            "seeing if activity won't be moved"));
 
             waitAndAssertTopResumedActivity(NON_RESIZEABLE_ACTIVITY, newDisplay.mId,
                     "The moved non-resizeable activity must be focused");
@@ -848,4 +856,90 @@
                             SINGLE_TASK_INSTANCE_DISPLAY_ACTIVITY3));
         }
     }
+
+    @Test
+    public void testLaunchPendingIntentActivity() throws Exception {
+        // Prevent the launch of pure PendingIntent from being delayed after home key is pressed.
+        resumeAppSwitches();
+        final DisplayManager displayManager =
+                getInstrumentation().getContext().getSystemService(DisplayManager.class);
+
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            final ActivityDisplay activityDisplay =
+                    virtualDisplaySession.setSimulateDisplay(true).createDisplay();
+
+            // Activity should be launched on primary display by default.
+            getPendingIntentActivity(TEST_ACTIVITY).send();
+            waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
+                    "Activity launched on primary display and on top");
+
+            // Activity should be launched on target display according to the caller context.
+            final Context displayContext =
+                    mContext.createDisplayContext(displayManager.getDisplay(activityDisplay.mId));
+            getPendingIntentActivity(TOP_ACTIVITY).send(displayContext, 1 /* code */,
+                    null /* intent */);
+            waitAndAssertTopResumedActivity(TOP_ACTIVITY, activityDisplay.mId,
+                    "Activity launched on secondary display and on top");
+
+            // Activity should be brought to front on the same display if it already existed.
+            getPendingIntentActivity(TEST_ACTIVITY).send(displayContext, 1 /* code */,
+                    null /* intent */);
+            waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
+                    "Activity launched on primary display and on top");
+
+            // Activity should be moved to target display.
+            ActivityOptions options = ActivityOptions.makeBasic();
+            options.setLaunchDisplayId(activityDisplay.mId);
+            getPendingIntentActivity(TEST_ACTIVITY).send(getInstrumentation().getContext(),
+                    1 /* code */, null /* intent */, null /* onFinished */, null /* handler */,
+                    null /* requiredPermission */, options.toBundle());
+            waitAndAssertTopResumedActivity(TEST_ACTIVITY, activityDisplay.mId,
+                    "Activity launched on secondary display and on top");
+        }
+    }
+
+    @Test
+    public void testLaunchActivityClearTask() throws Exception {
+        assertBroughtExistingTaskToAnotherDisplay(FLAG_ACTIVITY_CLEAR_TASK, LAUNCHING_ACTIVITY);
+    }
+
+    @Test
+    public void testLaunchActivityClearTop() throws Exception {
+        assertBroughtExistingTaskToAnotherDisplay(FLAG_ACTIVITY_CLEAR_TOP, LAUNCHING_ACTIVITY);
+    }
+
+    @Test
+    public void testLaunchActivitySingleTop() throws Exception {
+        assertBroughtExistingTaskToAnotherDisplay(FLAG_ACTIVITY_SINGLE_TOP, TEST_ACTIVITY);
+    }
+
+    private void assertBroughtExistingTaskToAnotherDisplay(int flags, ComponentName topActivity)
+            throws Exception {
+        // Start TEST_ACTIVITY on top of LAUNCHING_ACTIVITY within the same task
+        getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY).execute();
+
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            final ActivityDisplay newDisplay =
+                    virtualDisplaySession.setSimulateDisplay(true).createDisplay();
+
+            // Start LAUNCHING_ACTIVITY on secondary display with target flags, verify the task
+            // be reparented to secondary display
+            getLaunchActivityBuilder()
+                    .setUseInstrumentation()
+                    .setTargetActivity(LAUNCHING_ACTIVITY)
+                    .setIntentFlags(flags)
+                    .allowMultipleInstances(false)
+                    .setDisplayId(newDisplay.mId).execute();
+            waitAndAssertTopResumedActivity(topActivity, newDisplay.mId,
+                    "Activity launched on secondary display and on top");
+        }
+    }
+
+    private PendingIntent getPendingIntentActivity(ComponentName activity) {
+        final Intent intent = new Intent();
+        intent.setClassName(activity.getPackageName(), activity.getClassName());
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        return PendingIntent.getActivity(mContext, 1 /* requestCode */, intent,
+                PendingIntent.FLAG_CANCEL_CURRENT);
+    }
 }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayClientTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayClientTests.java
index df087db..9d2921c 100755
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayClientTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayClientTests.java
@@ -28,6 +28,7 @@
 import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
 import android.app.Activity;
@@ -36,8 +37,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.hardware.display.DisplayManager;
-import android.platform.test.annotations.Presubmit;
 import android.os.Bundle;
+import android.platform.test.annotations.Presubmit;
 import android.view.Display;
 import android.view.WindowManager;
 import android.view.inputmethod.InputMethodManager;
@@ -73,13 +74,11 @@
     }
 
     @Test
-    @FlakyTest(bugId = 130260102, detail = "Promote to presubmit once proved stable")
     public void testDisplayIdUpdateOnMove_RelaunchActivity() throws Exception {
         testDisplayIdUpdateOnMove(ClientTestActivity.class, false /* handlesConfigChange */);
     }
 
     @Test
-    @FlakyTest(bugId = 130260102, detail = "Promote to presubmit once proved stable")
     public void testDisplayIdUpdateOnMove_NoRelaunchActivity() throws Exception {
         testDisplayIdUpdateOnMove(NoRelaunchActivity.class, true /* handlesConfigChange */);
     }
@@ -128,17 +127,17 @@
     }
 
     private void waitAndAssertConfigurationChange(ComponentName activityName) {
-        mAmWmState.waitForWithAmState((state) ->
-                        getCallbackCount(activityName, ON_CONFIGURATION_CHANGED) == 1,
-                "waitForConfigurationChange");
-        assertEquals("Must receive a single configuration change", 1,
-                getCallbackCount(activityName, ON_CONFIGURATION_CHANGED));
+        assertTrue("Must receive a single configuration change",
+                mAmWmState.waitForWithAmState(
+                        state -> getCallbackCount(activityName, ON_CONFIGURATION_CHANGED) == 1,
+                        activityName + " receives configuration change"));
     }
 
     private void waitAndAssertResume(ComponentName activityName) {
-        mAmWmState.waitForWithAmState((state) ->
-                getCallbackCount(activityName, ON_RESUME) == 1, "waitForResume");
-        assertEquals("Must be resumed once", 1, getCallbackCount(activityName, ON_RESUME));
+        assertTrue("Must be resumed once",
+                mAmWmState.waitForWithAmState(
+                        state -> getCallbackCount(activityName, ON_RESUME) == 1,
+                        activityName + " performs resume"));
     }
 
     private int getCallbackCount(ComponentName activityName,
@@ -148,7 +147,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 130379901, detail = "Promote to presubmit once proved stable")
     public void testDisplayIdUpdateWhenImeMove_RelaunchActivity() throws Exception {
         try (final TestActivitySession<ClientTestActivity> session = new TestActivitySession<>()) {
             testDisplayIdUpdateWhenImeMove(ClientTestActivity.class);
@@ -156,7 +154,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 130379901, detail = "Promote to presubmit once proved stable")
     public void testDisplayIdUpdateWhenImeMove_NoRelaunchActivity() throws Exception {
         try (final TestActivitySession<NoRelaunchActivity> session = new TestActivitySession<>()) {
             testDisplayIdUpdateWhenImeMove(NoRelaunchActivity.class);
@@ -202,7 +199,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 130379901, detail = "Promote to presubmit once proved stable")
     public void testInputMethodManagerDisplayId() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             // Create a simulated display.
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayKeyguardTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayKeyguardTests.java
index 9a51039..fb77815 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayKeyguardTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayKeyguardTests.java
@@ -80,11 +80,10 @@
             final ActivityDisplay publicDisplay = virtualDisplaySession.setPublicDisplay(true)
                     .createDisplay();
             lockScreenSession.gotoKeyguard();
-            mAmWmState.waitForWithWmState((state) -> isKeyguardOnDisplay(state, publicDisplay.mId),
-                    "Waiting for keyguard window to show");
-
             assertTrue("KeyguardDialog must show on external public display",
-                    isKeyguardOnDisplay(mAmWmState.getWmState(), publicDisplay.mId));
+                    mAmWmState.waitForWithWmState(
+                            state -> isKeyguardOnDisplay(state, publicDisplay.mId),
+                            "keyguard window to show"));
 
             // Keyguard dialog mustn't be removed when press back key
             pressBackButton();
@@ -108,11 +107,11 @@
                     .createDisplay();
 
             lockScreenSession.gotoKeyguard();
-            mAmWmState.waitForWithWmState((state) -> isKeyguardOnDisplay(state, publicDisplay.mId),
-                    "Waiting for keyguard window to show");
-
             assertTrue("KeyguardDialog must show on external public display",
-                    isKeyguardOnDisplay(mAmWmState.getWmState(), publicDisplay.mId));
+                    mAmWmState.waitForWithWmState(
+                            state -> isKeyguardOnDisplay(state, publicDisplay.mId),
+                            "keyguard window to show"));
+
             assertFalse("KeyguardDialog must not show on external private display",
                     isKeyguardOnDisplay(mAmWmState.getWmState(), privateDisplay.mId));
         }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayLockedKeyguardTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayLockedKeyguardTests.java
index 642a3f0..b508760 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayLockedKeyguardTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayLockedKeyguardTests.java
@@ -29,8 +29,6 @@
 import android.platform.test.annotations.Presubmit;
 import android.server.wm.ActivityManagerState.ActivityDisplay;
 
-import androidx.test.filters.FlakyTest;
-
 import org.junit.Before;
 import org.junit.Test;
 
@@ -56,7 +54,6 @@
      * Test that virtual display content is hidden when device is locked.
      */
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testVirtualDisplayHidesContentWhenLocked() throws Exception {
         try (final LockScreenSession lockScreenSession = new LockScreenSession();
              final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
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 d3aabb7..dae2607 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
@@ -21,7 +21,6 @@
 import static android.server.wm.ActivityManagerState.STATE_RESUMED;
 import static android.server.wm.ActivityManagerState.STATE_STOPPED;
 import static android.server.wm.ComponentNameUtils.getWindowName;
-import static android.server.wm.StateLogger.logAlways;
 import static android.server.wm.StateLogger.logE;
 import static android.server.wm.WindowManagerState.TRANSIT_TASK_CLOSE;
 import static android.server.wm.WindowManagerState.TRANSIT_TASK_OPEN;
@@ -49,7 +48,6 @@
 import static org.junit.Assume.assumeTrue;
 
 import android.content.ComponentName;
-import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 import android.server.wm.ActivityManagerState.ActivityDisplay;
 import android.server.wm.ActivityManagerState.ActivityStack;
@@ -58,13 +56,9 @@
 import android.server.wm.CommandSession.SizeInfo;
 import android.util.SparseArray;
 
-import androidx.test.filters.FlakyTest;
-
 import org.junit.Before;
 import org.junit.Test;
 
-import java.util.concurrent.TimeUnit;
-
 /**
  * Build/Install/Run:
  *     atest CtsWindowManagerDeviceTestCases:MultiDisplayPolicyTests
@@ -175,7 +169,7 @@
                     logE("Error waiting for valid state: " + e.getMessage());
                     return false;
                 }
-            }, "Wait for the configuration change to happen and for activity to be resumed.");
+            }, "the configuration change to happen and activity to be resumed");
 
             mAmWmState.computeState(false /* compareTaskAndStackBounds */,
                     new WaitForValidActivityState(RESIZEABLE_ACTIVITY),
@@ -282,20 +276,11 @@
 
             // Wait for the fullscreen stack to start sleeping, and then make sure the
             // test activity is still resumed.
-            int retry = 0;
-            int stopCount = 0;
-            do {
-                stopCount = (new ActivityLifecycleCounts(RESIZEABLE_ACTIVITY))
-                        .getCount(ActivityCallback.ON_STOP);
-                if (stopCount == 1) {
-                    break;
-                }
-                logAlways("***testExternalDisplayActivityTurnPrimaryOff... retry=" + retry);
-                SystemClock.sleep(TimeUnit.SECONDS.toMillis(1));
-            } while (retry++ < 5);
-
-            if (stopCount != 1) {
-                fail(RESIZEABLE_ACTIVITY + " has received " + stopCount
+            final ActivityLifecycleCounts counts = new ActivityLifecycleCounts(RESIZEABLE_ACTIVITY);
+            if (!Condition.waitFor(counts.countWithRetry(RESIZEABLE_ACTIVITY + " to be stopped",
+                    countSpec(ActivityCallback.ON_STOP, CountSpec.EQUALS, 1)))) {
+                fail(RESIZEABLE_ACTIVITY + " has received "
+                        + counts.getCount(ActivityCallback.ON_STOP)
                         + " onStop() calls, expecting 1");
             }
             // For this test we create this virtual display with flag showContentWhenLocked, so it
@@ -359,7 +344,7 @@
                     VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId);
         }
         mAmWmState.waitFor((amState, wmState) -> amState.getDisplayCount() == displayCount,
-                "Waiting for external displays to be removed");
+                "external displays to be removed");
         assertEquals(displayCount, mAmWmState.getAmState().getDisplayCount());
         assertEquals(displayCount, mAmWmState.getAmState().getKeyguardControllerState().
                 mKeyguardOccludedStates.size());
@@ -755,7 +740,6 @@
      * Tests that toast works on a secondary display.
      */
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testSecondaryDisplayShowToast() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             final ActivityDisplay newDisplay =
@@ -764,11 +748,9 @@
             launchActivityOnDisplay(TOAST_ACTIVITY, newDisplay.mId);
             waitAndAssertTopResumedActivity(TOAST_ACTIVITY, newDisplay.mId,
                     "Activity launched on external display must be resumed");
-            mAmWmState.waitForWithWmState((state) -> state.containsWindow(TOAST_NAME),
-                    "Waiting for toast window to show");
 
-            assertTrue("Toast window must be shown",
-                    mAmWmState.getWmState().containsWindow(TOAST_NAME));
+            assertTrue("Toast window must be shown", mAmWmState.waitForWithWmState(
+                    state -> state.containsWindow(TOAST_NAME), "toast window to show"));
             assertTrue("Toast window must be visible",
                     mAmWmState.getWmState().isWindowVisible(TOAST_NAME));
         }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySecurityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySecurityTests.java
index b2654ee..46101f5 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySecurityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySecurityTests.java
@@ -17,7 +17,6 @@
 package android.server.wm;
 
 import static android.server.wm.ComponentNameUtils.getActivityName;
-import static android.server.wm.StateLogger.logAlways;
 import static android.server.wm.app.Components.DISPLAY_ACCESS_CHECK_EMBEDDING_ACTIVITY;
 import static android.server.wm.app.Components.LAUNCHING_ACTIVITY;
 import static android.server.wm.app.Components.LAUNCH_BROADCAST_RECEIVER;
@@ -40,7 +39,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -55,26 +54,23 @@
 import android.content.Intent;
 import android.hardware.display.DisplayManager;
 import android.os.Bundle;
-import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 import android.server.wm.ActivityManagerState.ActivityDisplay;
 import android.server.wm.ActivityManagerState.ActivityStack;
 import android.server.wm.CommandSession.ActivitySession;
+import android.server.wm.TestJournalProvider.TestJournalContainer;
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
 
-import androidx.test.filters.FlakyTest;
-
 import com.android.compatibility.common.util.SystemUtil;
 import com.android.compatibility.common.util.TestUtils;
 
 import org.junit.Before;
 import org.junit.Test;
 
-
 /**
  * Build/Install/Run:
  *     atest CtsWindowManagerDeviceTestCases:MultiDisplaySecurityTests
@@ -405,17 +401,16 @@
 
     private void assertActivityStartCheckResult(boolean expected) {
         final String component = ActivityLauncher.TAG;
-        for (int retry = 1; retry <= 5; retry++) {
-            final Bundle extras = TestJournalProvider.TestJournalContainer.get(component).extras;
-            if (extras.containsKey(ActivityLauncher.KEY_IS_ACTIVITY_START_ALLOWED_ON_DISPLAY)) {
-                assertEquals("Activity start check must match", expected, extras
-                        .getBoolean(ActivityLauncher.KEY_IS_ACTIVITY_START_ALLOWED_ON_DISPLAY));
-                return;
-            }
-
-            logAlways("***Waiting for activity start check for " + component
-                    + " ... retry=" + retry);
-            SystemClock.sleep(500);
+        final Bundle resultExtras = Condition.waitForResult(
+                new Condition<Bundle>("activity start check for " + component)
+                        .setRetryIntervalMs(500)
+                        .setResultSupplier(() -> TestJournalContainer.get(component).extras)
+                        .setResultValidator(extras -> extras.containsKey(
+                                ActivityLauncher.KEY_IS_ACTIVITY_START_ALLOWED_ON_DISPLAY)));
+        if (resultExtras != null) {
+            assertEquals("Activity start check must match", expected, resultExtras
+                    .getBoolean(ActivityLauncher.KEY_IS_ACTIVITY_START_ALLOWED_ON_DISPLAY));
+            return;
         }
         fail("Expected activity start check from " + component + " not found");
     }
@@ -692,17 +687,9 @@
         }
     }
 
-    private void assertSecurityExceptionFromActivityLauncher() {
-        final String component = ActivityLauncher.TAG;
-        for (int retry = 1; retry <= 5; retry++) {
-            if (ActivityLauncher.hasCaughtSecurityException()) {
-                return;
-            }
-
-            logAlways("***Waiting for SecurityException from " + component + " ... retry=" + retry);
-            SystemClock.sleep(500);
-        }
-        fail("Expected exception from " + component + " not found");
+    private static void assertSecurityExceptionFromActivityLauncher() {
+        waitForOrFail("SecurityException from " + ActivityLauncher.TAG,
+                ActivityLauncher::hasCaughtSecurityException);
     }
 
     /**
@@ -727,7 +714,6 @@
      * Test setting system decoration flag and show IME flag without sufficient permissions.
      */
     @Test
-    @FlakyTest(bugId = 130284250)
     public void testSettingFlagWithoutInternalSystemPermission() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             // The reason to use a trusted display is that we can guarantee the security exception
@@ -771,7 +757,6 @@
      * Test getting system decoration flag and show IME flag without sufficient permissions.
      */
     @Test
-    @FlakyTest(bugId = 130284250)
     public void testGettingFlagWithoutInternalSystemPermission() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             // The reason to use a trusted display is that we can guarantee the security exception
@@ -802,7 +787,6 @@
      * Test setting system decoration flag and show IME flag to the untrusted display.
      */
     @Test
-    @FlakyTest(bugId = 130284250)
     public void testSettingFlagToUntrustedDisplay() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             final ActivityDisplay untrustedDisplay = virtualDisplaySession.createDisplay();
@@ -849,7 +833,6 @@
      * Test getting system decoration flag and show IME flag from the untrusted display.
      */
     @Test
-    @FlakyTest(bugId = 130284250)
     public void testGettingFlagFromUntrustedDisplay() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             final ActivityDisplay untrustedDisplay = virtualDisplaySession.createDisplay();
@@ -871,7 +854,6 @@
      * Test setting system decoration flag and show IME flag to the trusted display.
      */
     @Test
-    @FlakyTest(bugId = 130284250)
     public void testSettingFlagToTrustedDisplay() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             final ActivityDisplay trustedDisplay = virtualDisplaySession
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 07a8dba..db52031 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
@@ -64,8 +64,6 @@
 import android.widget.EditText;
 import android.widget.LinearLayout;
 
-import androidx.test.filters.FlakyTest;
-
 import com.android.compatibility.common.util.ImeAwareEditText;
 import com.android.compatibility.common.util.SystemUtil;
 import com.android.compatibility.common.util.TestUtils;
@@ -84,7 +82,7 @@
 
 /**
  * Build/Install/Run:
- *     atest CtsWindowManagerDeviceTestCases:SystemDecorationMultiDisplayTests
+ *     atest CtsWindowManagerDeviceTestCases:MultiDisplaySystemDecorationTests
  *
  * This tests that verify the following should not be run for OEM device verification:
  * Wallpaper added if display supports system decorations (and not added otherwise)
@@ -130,7 +128,6 @@
      * Tests that wallpaper shows on secondary displays.
      */
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testWallpaperShowOnSecondaryDisplays() throws Exception {
         try (final ChangeWallpaperSession wallpaperSession = new ChangeWallpaperSession();
              final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession();
@@ -145,12 +142,10 @@
             final Bitmap tmpWallpaper = wallpaperSession.getTestBitmap();
             wallpaperSession.setImageWallpaper(tmpWallpaper);
 
-            mAmWmState.waitForWithWmState(
-                    (state) -> isWallpaperOnDisplay(state, decoredSystemDisplay.mId),
-                    "Waiting for wallpaper window to show");
-
             assertTrue("Wallpaper must be displayed on system owned display with system decor flag",
-                    isWallpaperOnDisplay(mAmWmState.getWmState(), decoredSystemDisplay.mId));
+                    mAmWmState.waitForWithWmState(
+                            state -> isWallpaperOnDisplay(state, decoredSystemDisplay.mId),
+                            "wallpaper window to show"));
 
             assertFalse("Wallpaper must not be displayed on the untrusted display",
                     isWallpaperOnDisplay(mAmWmState.getWmState(), untrustedDisplay.mId));
@@ -217,7 +212,6 @@
      * Test that navigation bar should not show on display without system decoration.
      */
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testNavBarNotShowingOnDisplayWithoutDecor() throws Exception {
         try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
             // Wait navigation bar show on default display and record the states.
@@ -236,7 +230,6 @@
      * supports system decoration.
      */
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testNavBarNotShowingOnPrivateDisplay() throws Exception {
         try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
             // Wait navigation bar show on default display and record the states.
@@ -385,7 +378,6 @@
 
     // IME related tests
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testImeWindowCanSwitchToDifferentDisplays() throws Exception {
         try (final TestActivitySession<ImeTestActivity> imeTestActivitySession = new
                 TestActivitySession<>();
@@ -436,7 +428,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testImeApiForBug118341760() throws Exception {
         final long TIMEOUT_START_INPUT = TimeUnit.SECONDS.toMillis(5);
 
@@ -476,7 +467,6 @@
     }
 
     @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.
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
index ef78bc7..afd6ab9 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
@@ -17,10 +17,8 @@
 package android.server.wm;
 
 import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
-import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
 import static android.server.wm.ComponentNameUtils.getActivityName;
 import static android.server.wm.StateLogger.log;
-import static android.server.wm.StateLogger.logAlways;
 import static android.server.wm.UiDeviceUtils.pressSleepButton;
 import static android.server.wm.UiDeviceUtils.pressWakeupButton;
 import static android.server.wm.app.Components.VIRTUAL_DISPLAY_ACTIVITY;
@@ -40,34 +38,31 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.hasSize;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
-import android.os.SystemClock;
 import android.provider.Settings;
 import android.server.wm.ActivityManagerState.ActivityDisplay;
 import android.server.wm.CommandSession.ActivitySession;
 import android.server.wm.CommandSession.ActivitySessionClient;
 import android.server.wm.settings.SettingsSession;
 import android.util.Size;
-import android.util.SparseBooleanArray;
 import android.view.WindowManager;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.compatibility.common.util.SystemUtil;
-import com.android.compatibility.common.util.TestUtils;
+
 import org.junit.Before;
 
 import java.util.ArrayList;
@@ -245,16 +240,11 @@
         tapOnDisplay(bounds.centerX(), bounds.centerY(), displayId);
     }
 
-    private void waitForDisplayGone(Predicate<WindowManagerState.Display> displayPredicate) {
-        for (int retry = 1; retry <= 5; retry++) {
+    void waitForDisplayGone(Predicate<WindowManagerState.Display> displayPredicate) {
+        waitForOrFail("displays to be removed", () -> {
             mAmWmState.computeState(true);
-            if (!mAmWmState.getWmState().getDisplays().stream().anyMatch(displayPredicate::test)) {
-                return;
-            }
-            logAlways("Waiting for hosted displays destruction... retry=" + retry);
-            SystemClock.sleep(500);
-        }
-        fail("Waiting for hosted displays destruction failed.");
+            return !mAmWmState.getWmState().getDisplays().stream().anyMatch(displayPredicate::test);
+        });
     }
 
     /**
@@ -510,6 +500,8 @@
             super(Settings.Global.getUriFor(Settings.Global.OVERLAY_DISPLAY_DEVICES),
                     Settings.Global::getString,
                     Settings.Global::putString);
+            // Remove existing overlay display to avoid display count problem.
+            removeExisting();
             mWm = context.getSystemService(WindowManager.class);
         }
 
@@ -534,15 +526,13 @@
                             display.mId, showSystemDecors, showIme));
                     if (requestShowSysDecors != showSystemDecors) {
                         mWm.setShouldShowSystemDecors(display.mId, requestShowSysDecors);
-                        TestUtils.waitUntil("Waiting for display show system decors",
-                                5 /* timeoutSecond */,
+                        waitForOrFail("display config show-system-decors to be set",
                                 () -> mWm.shouldShowSystemDecors(
                                         display.mId) == requestShowSysDecors);
                     }
                     if (requestShowIme != showIme) {
                         mWm.setShouldShowIme(display.mId, requestShowIme);
-                        TestUtils.waitUntil("Waiting for display show Ime",
-                                5 /* timeoutSecond */,
+                        waitForOrFail("display config show-IME to be set",
                                 () -> mWm.shouldShowIme(display.mId) == requestShowIme);
                     }
                 }
@@ -555,8 +545,7 @@
                 mWm.setShouldShowIme(state.mId, state.mShouldShowIme);
 
                 // Only need to wait the last flag to be set.
-                TestUtils.waitUntil("Waiting for the show IME flag to be set",
-                        5 /* timeoutSecond */,
+                waitForOrFail("display config show-IME to be restored",
                         () -> mWm.shouldShowIme(state.mId) == state.mShouldShowIme);
             }));
         }
@@ -570,6 +559,12 @@
                     .anyMatch(state -> state.mId == display.getDisplayId()));
         }
 
+        private void removeExisting() {
+            if (mHasInitialValue && mInitialValue != null) {
+                delete(mUri);
+            }
+        }
+
         private class OverlayDisplayState {
             int mId;
             boolean mShouldShowSystemDecors;
@@ -585,20 +580,12 @@
 
     /** Wait for provided number of displays and report their configurations. */
     List<ActivityDisplay> getDisplayStateAfterChange(int expectedDisplayCount) {
-        List<ActivityDisplay> ds = getDisplaysStates();
-
-        int retriesLeft = 5;
-        while (!areDisplaysValid(ds, expectedDisplayCount) && retriesLeft-- > 0) {
-            log("***Waiting for the correct number of displays...");
-            try {
-                Thread.sleep(1000);
-            } catch (InterruptedException e) {
-                log(e.toString());
-            }
-            ds = getDisplaysStates();
-        }
-
-        return ds;
+        return Condition.waitForResult("the correct number of displays=" + expectedDisplayCount,
+                condition -> condition
+                        .setReturnLastResult(true)
+                        .setResultSupplier(this::getDisplaysStates)
+                        .setResultValidator(
+                                displays -> areDisplaysValid(displays, expectedDisplayCount)));
     }
 
     private boolean areDisplaysValid(List<ActivityDisplay> displays, int expectedDisplayCount) {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
index 98669b7..d6e8b3f 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
@@ -64,7 +64,7 @@
 import static android.server.wm.app27.Components.SDK_27_PIP_ACTIVITY;
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static org.hamcrest.Matchers.lessThan;
 import static org.junit.Assert.assertEquals;
@@ -107,13 +107,13 @@
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
 
 /**
  * Build/Install/Run:
  * atest CtsWindowManagerDeviceTestCases:PinnedStackTests
  */
 @Presubmit
-@FlakyTest(bugId = 71792368)
 public class PinnedStackTests extends ActivityManagerTestBase {
     private static final String TAG = PinnedStackTests.class.getSimpleName();
 
@@ -215,12 +215,7 @@
 
         try (final RotationSession rotationSession = new RotationSession()) {
             rotationSession.set(ROTATION_0);
-            mAmWmState.waitForWithWmState((wmState1) -> {
-                Rect db = wmState1.getDefaultPinnedStackBounds();
-                Rect sb = wmState1.getStableBounds();
-                return (db.width() > 0 && db.height() > 0) &&
-                        (sb.contains(db));
-            }, "Waiting for valid bounds..");
+            waitForValidPinnedStackBounds(WindowManagerState::getDefaultPinnedStackBounds);
             WindowManagerState wmState = mAmWmState.getWmState();
             wmState.computeState();
             Rect defaultPipBounds = wmState.getDefaultPinnedStackBounds();
@@ -229,12 +224,7 @@
             assertTrue(stableBounds.contains(defaultPipBounds));
 
             rotationSession.set(ROTATION_90);
-            mAmWmState.waitForWithWmState((wmState1) -> {
-                Rect db = wmState1.getDefaultPinnedStackBounds();
-                Rect sb = wmState1.getStableBounds();
-                return (db.width() > 0 && db.height() > 0) &&
-                        (sb.contains(db));
-            }, "Waiting for valid bounds...");
+            waitForValidPinnedStackBounds(WindowManagerState::getDefaultPinnedStackBounds);
             wmState = mAmWmState.getWmState();
             wmState.computeState();
             defaultPipBounds = wmState.getDefaultPinnedStackBounds();
@@ -253,12 +243,7 @@
 
         try (final RotationSession rotationSession = new RotationSession()) {
             rotationSession.set(ROTATION_0);
-            mAmWmState.waitForWithWmState((wmState1) -> {
-                Rect db = wmState1.getPinnedStackMovementBounds();
-                Rect sb = wmState1.getStableBounds();
-                return (db.width() > 0 && db.height() > 0) &&
-                        (sb.contains(db));
-            }, "Waiting for valid bounds...");
+            waitForValidPinnedStackBounds(WindowManagerState::getPinnedStackMovementBounds);
             WindowManagerState wmState = mAmWmState.getWmState();
             wmState.computeState();
             Rect pipMovementBounds = wmState.getPinnedStackMovementBounds();
@@ -267,12 +252,7 @@
             assertTrue(stableBounds.contains(pipMovementBounds));
 
             rotationSession.set(ROTATION_90);
-            mAmWmState.waitForWithWmState((wmState1) -> {
-                Rect db = wmState1.getPinnedStackMovementBounds();
-                Rect sb = wmState1.getStableBounds();
-                return (db.width() > 0 && db.height() > 0) &&
-                        (sb.contains(db));
-            }, "Waiting for valid bounds...");
+            waitForValidPinnedStackBounds(WindowManagerState::getPinnedStackMovementBounds);
             wmState = mAmWmState.getWmState();
             wmState.computeState();
             pipMovementBounds = wmState.getPinnedStackMovementBounds();
@@ -282,8 +262,16 @@
         }
     }
 
+    private void waitForValidPinnedStackBounds(Function<WindowManagerState, Rect> boundsFunc) {
+        mAmWmState.waitForWithWmState(wmState -> {
+            final Rect bounds = boundsFunc.apply(wmState);
+            final Rect displayStableBounds = wmState.getStableBounds();
+            return bounds.width() > 0 && bounds.height() > 0
+                    && displayStableBounds.contains(bounds);
+        }, "valid pinned stack bounds");
+    }
+
     @Test
-    @FlakyTest // TODO: Reintroduce to presubmit once b/71508234 is resolved.
     public void testPinnedStackOutOfBoundsInsetsNonNegative() throws Exception {
         final WindowManagerState wmState = mAmWmState.getWmState();
 
@@ -640,7 +628,6 @@
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
     }
 
-    @FlakyTest(bugId = 70746098)
     @Test
     public void testRemovePipWithHiddenFullscreenStack() throws Exception {
         // Launch a fullscreen activity, return home and while the fullscreen stack is hidden,
@@ -675,7 +662,6 @@
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
     }
 
-    @FlakyTest(bugId = 70906499)
     @Test
     public void testMovePipToBackWithVisibleFullscreenStack() throws Exception {
         // Launch a fullscreen activity, and a pip activity over that
@@ -691,7 +677,6 @@
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
     }
 
-    @FlakyTest(bugId = 70906499)
     @Test
     public void testMovePipToBackWithHiddenFullscreenStack() throws Exception {
         // Launch a fullscreen activity, return home and while the fullscreen stack is hidden,
@@ -805,6 +790,7 @@
         assertPinnedStackExists();
     }
 
+    @FlakyTest(bugId = 139111392)
     @Test
     public void testDisallowEnterPipActivityLocked() throws Exception {
         launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP_ON_PAUSE, "true");
@@ -827,7 +813,7 @@
         });
     }
 
-    @FlakyTest(bugId = 70328524)
+    @FlakyTest(bugId = 139111392)
     @Test
     public void testConfigurationChangeOrderDuringTransition() throws Exception {
         // Launch a PiP activity and ensure configuration change only happened once, and that the
@@ -1123,6 +1109,7 @@
                 TEST_ACTIVITY).mTaskId);
     }
 
+    @FlakyTest(bugId = 139111392)
     @Test
     public void testLaunchTaskByAffinityMatchSingleTask() throws Exception {
         // Launch an activity into the pinned stack with a fixed affinity
@@ -1145,7 +1132,6 @@
     }
 
     /** Test that reported display size corresponds to fullscreen after exiting PiP. */
-    @FlakyTest
     @Test
     public void testDisplayMetricsPinUnpin() throws Exception {
         separateTestJournal();
@@ -1180,6 +1166,7 @@
                 finalAppSize);
     }
 
+    @FlakyTest(bugId = 139111392)
     @Test
     public void testEnterPictureInPictureSavePosition() throws Exception {
         // Ensure we have static shelf offset by running this test over a non-home activity
@@ -1228,7 +1215,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 71792368)
     public void testEnterPictureInPictureDiscardSavedPositionOnFinish() throws Exception {
         // Ensure we have static shelf offset by running this test over a non-home activity
         launchActivity(NO_RELAUNCH_ACTIVITY);
@@ -1413,7 +1399,7 @@
         mAmWmState.waitFor((amState, wmState) -> {
                 WindowStack stack = wmState.getStandardStackByWindowingMode(WINDOWING_MODE_PINNED);
                 return stack != null && !stack.mAnimatingBounds;
-            }, "Waiting for pinned stack bounds animation to finish");
+            }, "pinned stack bounds animation to finish");
     }
 
     /**
@@ -1423,7 +1409,7 @@
         mAmWmState.waitFor((amState, wmState) -> {
             return !amState.containsStack(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD)
                     && !wmState.containsStack(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
-        }, "Waiting for pinned stack to be removed...");
+        }, "pinned stack to be removed");
     }
 
     /**
@@ -1445,7 +1431,7 @@
             return lifecycles.getCount(ActivityCallback.ON_CONFIGURATION_CHANGED) == 1
                     && lifecycles.getCount(ActivityCallback.ON_PICTURE_IN_PICTURE_MODE_CHANGED) == 1
                     && lifecycles.getCount(ActivityCallback.ON_MULTI_WINDOW_MODE_CHANGED) == 1;
-        }, "Waiting for picture-in-picture activity callbacks...");
+        }, "picture-in-picture activity callbacks...");
     }
 
     private void waitForValidAspectRatio(int num, int denom) {
@@ -1454,7 +1440,7 @@
         mAmWmState.waitForWithAmState((state) -> {
             Rect bounds = state.getStandardStackByWindowingMode(WINDOWING_MODE_PINNED).getBounds();
             return floatEquals((float) bounds.width() / bounds.height(), (float) num / denom);
-        }, "waitForValidAspectRatio");
+        }, "valid aspect ratio");
     }
 
     /**
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/SplitScreenTests.java b/tests/framework/base/windowmanager/src/android/server/wm/SplitScreenTests.java
index 2dfd254..04e1fb6 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/SplitScreenTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/SplitScreenTests.java
@@ -96,7 +96,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 71792393)
     public void testStackList() throws Exception {
         launchActivity(TEST_ACTIVITY);
         mAmWmState.computeState(TEST_ACTIVITY);
@@ -118,7 +117,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testNonResizeableNotDocked() throws Exception {
         launchActivityInSplitScreenWithRecents(NON_RESIZEABLE_ACTIVITY);
 
@@ -161,7 +159,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 72956284)
     public void testNoUserLeaveHintOnMultiWindowModeChanged() throws Exception {
         launchActivity(TEST_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
 
@@ -223,7 +220,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 71792393)
     public void testLaunchToSideMultiple() throws Exception {
         launchActivitiesInSplitScreen(
                 getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
@@ -249,7 +245,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 73808815)
     public void testLaunchToSideSingleInstance() throws Exception {
         launchTargetToSide(SINGLE_INSTANCE_ACTIVITY, false);
     }
@@ -259,7 +254,6 @@
         launchTargetToSide(SINGLE_TASK_ACTIVITY, false);
     }
 
-    @FlakyTest(bugId = 71792393)
     @Test
     public void testLaunchToSideMultipleWithDifferentIntent() throws Exception {
         launchTargetToSide(TEST_ACTIVITY, true);
@@ -414,7 +408,6 @@
     }
 
     @Test
-    @FlakyTest
     public void testMinimizedFromEachDockedSide() throws Exception {
         try (final RotationSession rotationSession = new RotationSession()) {
             for (int i = 0; i < 2; i++) {
@@ -432,7 +425,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testRotationWhileDockMinimized() throws Exception {
         launchActivityInDockStackAndMinimize(TEST_ACTIVITY);
 
@@ -461,7 +453,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testMinimizeAndUnminimizeThenGoingHome() throws Exception {
         // Rotate the screen to check that minimize, unminimize, dismiss the docked stack and then
         // going home has the correct app transition
@@ -498,7 +489,6 @@
         }
     }
 
-    @FlakyTest(bugId = 73813034)
     @Test
     public void testFinishDockActivityWhileMinimized() throws Exception {
         launchActivityInDockStackAndMinimize(TEST_ACTIVITY);
@@ -552,7 +542,6 @@
      * Verify split screen mode visibility after stack resize occurs.
      */
     @Test
-    @FlakyTest(bugId = 110276714)
     public void testResizeDockedStack() throws Exception {
         launchActivitiesInSplitScreen(
                 getLaunchActivityBuilder().setTargetActivity(DOCKED_ACTIVITY),
@@ -581,7 +570,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testDifferentProcessActivityResumedPreQ() {
         launchActivitiesInSplitScreen(
                 getLaunchActivityBuilder().setTargetActivity(SDK_27_TEST_ACTIVITY),
@@ -655,7 +643,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testStackListOrderOnSplitScreenDismissed() throws Exception {
         launchActivitiesInSplitScreen(
                 getLaunchActivityBuilder().setTargetActivity(DOCKED_ACTIVITY),
@@ -710,11 +697,11 @@
 
     private void waitForDockMinimized() throws Exception {
         mAmWmState.waitForWithWmState(state -> state.isDockedStackMinimized(),
-                "***Waiting for Dock stack to be minimized");
+                "Dock stack to be minimized");
     }
 
     private void waitForDockNotMinimized() throws Exception {
         mAmWmState.waitForWithWmState(state -> !state.isDockedStackMinimized(),
-                "***Waiting for Dock stack to not be minimized");
+                "Dock stack to not be minimized");
     }
 }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java
index 6923afb..bef21f8 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java
@@ -16,7 +16,10 @@
 
 package android.server.wm;
 
+import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+import static android.server.wm.ActivityManagerState.STATE_STOPPED;
 import static android.server.wm.app.Components.TEST_ACTIVITY;
+import static android.server.wm.app.Components.TRANSLUCENT_ACTIVITY;
 import static android.server.wm.app27.Components.SDK_27_LAUNCHING_ACTIVITY;
 
 import static org.junit.Assert.assertFalse;
@@ -25,7 +28,6 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.filters.FlakyTest;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -46,7 +48,7 @@
      * {@link android.content.Context}.
      */
     @Test
-    public void testStartActivityContexts() throws Exception {
+    public void testStartActivityContexts() {
         // Launch Activity from application context.
         getLaunchActivityBuilder()
                 .setTargetActivity(TEST_ACTIVITY)
@@ -85,14 +87,37 @@
                 TEST_ACTIVITY);
     }
 
+    @Test
+    public void testStartActivityTaskLaunchBehind() {
+        // launch an activity
+        getLaunchActivityBuilder()
+                .setTargetActivity(TEST_ACTIVITY)
+                .setUseInstrumentation()
+                .setNewTask(true)
+                .execute();
+
+        // launch an activity behind
+        getLaunchActivityBuilder()
+                .setTargetActivity(TRANSLUCENT_ACTIVITY)
+                .setUseInstrumentation()
+                .setIntentFlags(FLAG_ACTIVITY_NEW_DOCUMENT)
+                .setNewTask(true)
+                .setLaunchTaskBehind(true)
+                .execute();
+
+        waitAndAssertActivityState(TRANSLUCENT_ACTIVITY, STATE_STOPPED,
+                "Activity should be stopped");
+        mAmWmState.assertResumedActivity("Test Activity should be remained on top and resumed",
+                TEST_ACTIVITY);
+    }
+
     /**
      * Ensures you can start an {@link Activity} from a non {@link Activity}
      * {@link android.content.Context} when the target sdk is between N and O Mr1.
      * @throws Exception
      */
     @Test
-    @FlakyTest(bugId = 131005232)
-    public void testLegacyStartActivityFromNonActivityContext() throws Exception {
+    public void testLegacyStartActivityFromNonActivityContext() {
         getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)
                 .setLaunchingActivity(SDK_27_LAUNCHING_ACTIVITY)
                 .setUseApplicationContext(true)
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java b/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java
index ec666e0..2a04d2e 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java
@@ -58,7 +58,6 @@
  *     atest CtsWindowManagerDeviceTestCases:TransitionSelectionTests
  */
 @Presubmit
-@FlakyTest(bugId = 71792333)
 public class TransitionSelectionTests extends ActivityManagerTestBase {
 
     @Override
@@ -118,7 +117,6 @@
                 false /*slowStop*/, TRANSIT_TASK_OPEN);
     }
 
-    @FlakyTest(bugId = 71792333)
     @Test
     public void testCloseTask_NeitherWallpaper() {
         testCloseTask(false /*bottomWallpaper*/, false /*topWallpaper*/,
@@ -177,7 +175,6 @@
     // Test task close -- bottom task top activity slow in stopping
     // These simulate the case where the bottom activity is resumed
     // before AM receives its activitiyStopped
-    @FlakyTest(bugId = 71792333)
     @Test
     public void testCloseTask_NeitherWallpaper_SlowStop() {
         testCloseTask(false /*bottomWallpaper*/, false /*topWallpaper*/,
@@ -190,6 +187,7 @@
                 true /*slowStop*/, TRANSIT_WALLPAPER_OPEN);
     }
 
+    @FlakyTest(bugId = 139111132)
     @Test
     public void testCloseTask_BothWallpaper_SlowStop() {
         testCloseTask(true /*bottomWallpaper*/, true /*topWallpaper*/,
@@ -223,7 +221,6 @@
                 TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE);
     }
 
-    @FlakyTest(bugId = 71792333)
     @Test
     public void testCloseTask_BottomWallpaper_Translucent() {
         testCloseTaskTranslucent(true /*bottomWallpaper*/, false /*topWallpaper*/,
@@ -306,13 +303,15 @@
         }
         executeShellCommand(topStartCmd);
 
-        SystemClock.sleep(5000);
-        if (testOpen) {
-            mAmWmState.computeState(topActivity);
-        } else {
-            mAmWmState.computeState(BOTTOM_ACTIVITY);
-        }
-
+        Condition.waitFor("Retrieving correct transition", () -> {
+            if (testOpen) {
+                mAmWmState.computeState(topActivity);
+            } else {
+                mAmWmState.computeState(BOTTOM_ACTIVITY);
+            }
+            return expectedTransit.equals(
+                    mAmWmState.getWmState().getDefaultDisplayLastTransition());
+        });
         assertEquals("Picked wrong transition", expectedTransit,
                 mAmWmState.getWmState().getDefaultDisplayLastTransition());
     }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/VirtualDisplayHelper.java b/tests/framework/base/windowmanager/src/android/server/wm/VirtualDisplayHelper.java
index cbd6816..8faff8d 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/VirtualDisplayHelper.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/VirtualDisplayHelper.java
@@ -20,23 +20,19 @@
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
 import static android.server.wm.ActivityManagerTestBase.isDisplayOn;
+import static android.server.wm.ActivityManagerTestBase.waitForOrFail;
 import static android.server.wm.StateLogger.logAlways;
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
-
-import static org.junit.Assert.fail;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import android.graphics.PixelFormat;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.VirtualDisplay;
 import android.media.ImageReader;
-import android.os.SystemClock;
 
 import com.android.compatibility.common.util.SystemUtil;
 
-import java.util.function.Predicate;
-
 /**
  * Helper class to create virtual display.
  */
@@ -78,40 +74,29 @@
     int createAndWaitForDisplay() {
         SystemUtil.runWithShellPermissionIdentity(() -> {
             createVirtualDisplay();
-            waitForDisplayState(mVirtualDisplay.getDisplay().getDisplayId() /* default */,
-                    true /* on */);
+            waitForDisplayState(mVirtualDisplay.getDisplay().getDisplayId(), true /* wantOn */);
             mCreated = true;
         });
         return mVirtualDisplay.getDisplay().getDisplayId();
     }
 
     void turnDisplayOff() {
-        SystemUtil.runWithShellPermissionIdentity(() -> {
-            mVirtualDisplay.setSurface(null);
-            waitForDisplayState(mVirtualDisplay.getDisplay().getDisplayId() /* displayId */,
-                    false /* on */);
-        });
+        mVirtualDisplay.setSurface(null);
+        waitForDisplayState(mVirtualDisplay.getDisplay().getDisplayId(), false /* wantOn */);
     }
 
     void turnDisplayOn() {
-        SystemUtil.runWithShellPermissionIdentity(() -> {
-            mVirtualDisplay.setSurface(mReader.getSurface());
-            waitForDisplayState(mVirtualDisplay.getDisplay().getDisplayId() /* displayId */,
-                    true /* on */);
-        });
+        mVirtualDisplay.setSurface(mReader.getSurface());
+        waitForDisplayState(mVirtualDisplay.getDisplay().getDisplayId(), true /* wantOn */);
     }
 
     void releaseDisplay() {
-        SystemUtil.runWithShellPermissionIdentity(() -> {
-            if (mCreated) {
-                mVirtualDisplay.release();
-                mReader.close();
-                waitForDisplayCondition(mVirtualDisplay.getDisplay().getDisplayId() /* displayId */,
-                        onState -> onState != null && onState == false,
-                        "Waiting for virtual display destroy");
-            }
-            mCreated = false;
-        });
+        if (mCreated) {
+            mVirtualDisplay.release();
+            mReader.close();
+            waitForDisplayState(mVirtualDisplay.getDisplay().getDisplayId(), false /* wantOn */);
+        }
+        mCreated = false;
     }
 
     private void createVirtualDisplay() {
@@ -142,25 +127,12 @@
     }
 
     static void waitForDefaultDisplayState(boolean wantOn) {
-        waitForDisplayState(DEFAULT_DISPLAY /* default */, wantOn);
+        waitForDisplayState(DEFAULT_DISPLAY, wantOn);
     }
 
     private static void waitForDisplayState(int displayId, boolean wantOn) {
-        waitForDisplayCondition(displayId, state -> state != null && state == wantOn,
-                "Waiting for " + ((displayId == DEFAULT_DISPLAY) ? "default" : "virtual")
-                        + " display "
-                        + (wantOn ? "on" : "off"));
-    }
-
-    private static void waitForDisplayCondition(int displayId,
-            Predicate<Boolean> condition, String message) {
-        for (int retry = 1; retry <= 10; retry++) {
-            if (condition.test(isDisplayOn(displayId))) {
-                return;
-            }
-            logAlways(message + "... retry=" + retry);
-            SystemClock.sleep(500);
-        }
-        fail(message + " failed");
+        final String message = (displayId == DEFAULT_DISPLAY ? "default" : "virtual")
+                + " display " + (wantOn ? "on" : "off");
+        waitForOrFail(message, () -> isDisplayOn(displayId) == wantOn);
     }
 }
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 a361011..4886925 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java
@@ -39,8 +39,8 @@
 import static org.junit.Assert.assertEquals;
 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 static org.junit.Assume.assumeTrue;
 
 import android.content.Context;
 import android.content.res.Configuration;
@@ -140,7 +140,6 @@
      * - The window which lost top-focus can receive display-unspecified cancel events.
      */
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testKeyReceiving() throws InterruptedException {
         final PrimaryActivity primaryActivity = startActivity(PrimaryActivity.class,
                 DEFAULT_DISPLAY);
@@ -148,8 +147,6 @@
         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();
@@ -158,7 +155,13 @@
             sendAndAssertTargetConsumedKey(secondaryActivity, KEYCODE_2, INVALID_DISPLAY);
             sendAndAssertTargetConsumedKey(secondaryActivity, KEYCODE_3, secondaryDisplayId);
 
-            primaryActivity.waitAndAssertWindowFocusState(false /* hasFocus */);
+            final boolean perDisplayFocusEnabled = perDisplayFocusEnabled();
+            if (perDisplayFocusEnabled) {
+                primaryActivity.assertWindowFocusState(true /* hasFocus */);
+                sendAndAssertTargetConsumedKey(primaryActivity, KEYCODE_4, DEFAULT_DISPLAY);
+            } else {
+                primaryActivity.waitAndAssertWindowFocusState(false /* hasFocus */);
+            }
 
             // Press display-unspecified keys and a display-specified key but not release them.
             sendKey(ACTION_DOWN, KEYCODE_5, INVALID_DISPLAY);
@@ -175,7 +178,9 @@
             // 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);
-            secondaryActivity.waitAssertAndConsumeKeyEvent(ACTION_UP, KEYCODE_6, FLAG_CANCELED);
+            if (!perDisplayFocusEnabled) {
+                secondaryActivity.waitAssertAndConsumeKeyEvent(ACTION_UP, KEYCODE_6, FLAG_CANCELED);
+            }
             assertEquals(secondaryActivity.getLogTag() + " must only receive expected events.",
                     0 /* expected event count */, secondaryActivity.getKeyEventCount());
 
@@ -188,7 +193,6 @@
      * Test if a display targeted by a key event can be moved to top in a single-focus system.
      */
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testMovingDisplayToTopByKeyEvent() throws InterruptedException {
         assumeTrue(supportsMultiDisplay());
         assumeFalse(perDisplayFocusEnabled());
@@ -241,7 +245,6 @@
         primaryActivity.waitAndAssertPointerCaptureState(true /* hasCapture */);
 
         assumeTrue(supportsMultiDisplay());
-        assumeFalse(perDisplayFocusEnabled());
         try (VirtualDisplaySession displaySession = new VirtualDisplaySession()) {
             final int secondaryDisplayId = displaySession.createDisplay(
                     getInstrumentation().getTargetContext()).getDisplayId();
@@ -316,7 +319,6 @@
      * window on that display.
      */
     @Test
-    @FlakyTest(bugId = 130467737)
     public void testTapNonFocusableWindow() throws InterruptedException {
         assumeTrue(supportsMultiDisplay());
         assumeFalse(perDisplayFocusEnabled());
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java
index 55932cc..62bbee3 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java
@@ -32,7 +32,6 @@
 import android.view.WindowManager;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
 import androidx.test.rule.ActivityTestRule;
 
 import com.android.compatibility.common.util.CtsTouchUtils;
@@ -48,7 +47,6 @@
  * Build/Install/Run:
  *     atest CtsWindowManagerDeviceTestCases:WindowInputTests
  */
-@FlakyTest
 public class WindowInputTests {
     private final int TOTAL_NUMBER_OF_CLICKS = 100;
     private final ActivityTestRule<Activity> mActivityRule = new ActivityTestRule<>(Activity.class);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/intent/Activities.java b/tests/framework/base/windowmanager/src/android/server/wm/intent/Activities.java
index 6b5600c..9ba003c 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/intent/Activities.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/intent/Activities.java
@@ -77,4 +77,10 @@
 
     public static class LauncherActivity extends Activity {
     }
+
+    public static class RelinquishTaskIdentityActivity extends Activity {
+    }
+
+    public static class TaskAffinity1RelinquishTaskIdentityActivity extends Activity {
+    }
 }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/intent/IntentGenerationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/intent/IntentGenerationTests.java
index fcbd865..396ead6 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/intent/IntentGenerationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/intent/IntentGenerationTests.java
@@ -63,6 +63,17 @@
  *
  * Build/Install/Run:
  * atest CtsWindowManagerDeviceTestCases:IntentGenerationTests
+ *
+ *
+ * <p>NOTE: It is also possible to use this to manually verify a single test (useful for local
+ * debugging). To do that:
+ * 1. Put the correct test name in {@link #verifySingle()} and remove the @Ignore annotation
+ * 2. Push the file under test to device, e.g.
+ *    adb shell mkdir /storage/emulated/0/Documents/relinquishTaskIdentity/
+ *    adb push cts/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/test-0.json /storage/emulated/0/Documents/relinquishTaskIdentity/
+ * 3. Run the test
+ *    atest CtsWindowManagerDeviceTestCases:IntentGenerationTests#verifySingle
+ * </p>
  */
 public class IntentGenerationTests extends IntentTestBase {
     private static final Cases CASES = new Cases();
@@ -106,9 +117,11 @@
      * @throws JSONException if the file has invalid json in it.
      */
     @Test
+    // Comment the following line to test locally
     @Ignore
     public void verifySingle() throws IOException, JSONException {
-        String test = "forResult/test-1.json";
+        // Replace this line with the test you need
+        String test = "relinquishTaskIdentity/test-0.json";
         TestCase testCase = readFromStorage(test);
         mLaunchRunner.verify(mTargetContext, testCase);
     }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/intent/IntentTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/intent/IntentTestBase.java
index 887d599..08d04f4 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/intent/IntentTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/intent/IntentTestBase.java
@@ -42,7 +42,7 @@
 
         this.getAmWmState().waitForWithAmState(
                 state -> state.containsNoneOf(activitiesInUsedInTest),
-                "Waiting for activity to be removed");
+                "activity to be removed");
     }
 
     @After
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/intent/LaunchRunner.java b/tests/framework/base/windowmanager/src/android/server/wm/intent/LaunchRunner.java
index b7b34d8..9357e99 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/intent/LaunchRunner.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/intent/LaunchRunner.java
@@ -317,7 +317,7 @@
         SystemClock.sleep(BEFORE_DUMP_TIMEOUT);
         mTestBase.getAmWmState().waitForWithAmState(
                 am -> StateDump.fromStacks(am.getStacks(), mBaseStacks).equals(expected),
-                "Wait until the activity states match up with what we recorded");
+                "the activity states match up with what we recorded");
         mTestBase.getAmWmState().computeState(activity.getComponentName());
 
         List<ActivityManagerState.ActivityStack> endStateStacks =
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleClientTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleClientTestBase.java
index d2bb3ef..dd4f2c1 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleClientTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleClientTestBase.java
@@ -16,6 +16,7 @@
 
 package android.server.wm.lifecycle;
 
+import static android.app.Activity.RESULT_OK;
 import static android.server.wm.StateLogger.log;
 import static android.server.wm.app.Components.PipActivity.EXTRA_ENTER_PIP;
 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_ACTIVITY_RESULT;
@@ -58,8 +59,12 @@
 public class ActivityLifecycleClientTestBase extends MultiDisplayTestBase {
 
     static final String EXTRA_RECREATE = "recreate";
+    static final String EXTRA_FINISH_IN_ON_CREATE = "finish_in_on_create";
+    static final String EXTRA_FINISH_IN_ON_START = "finish_in_on_start";
     static final String EXTRA_FINISH_IN_ON_RESUME = "finish_in_on_resume";
     static final String EXTRA_FINISH_AFTER_RESUME = "finish_after_resume";
+    static final String EXTRA_FINISH_IN_ON_PAUSE = "finish_in_on_pause";
+    static final String EXTRA_FINISH_IN_ON_STOP = "finish_in_on_stop";
 
     static final ComponentName CALLBACK_TRACKING_ACTIVITY =
             getComponentName(CallbackTrackingActivity.class);
@@ -115,6 +120,12 @@
     final ActivityTestRule mSlowActivityTestRule = new ActivityTestRule<>(
             SlowActivity.class, true /* initialTouchMode */, false /* launchActivity */);
 
+    final ActivityTestRule mResultActivityTestRule = new ActivityTestRule(
+            ResultActivity.class, true /* initialTouchMode */, false /* launchActivity */);
+
+    final ActivityTestRule mNoDisplayActivityTestRule = new ActivityTestRule(
+            NoDisplayActivity.class, true /* initialTouchMode */, false /* launchActivity */);
+
     private static LifecycleLog mLifecycleLog;
 
     protected Context mTargetContext;
@@ -137,6 +148,7 @@
     /** Launch an activity given a class. */
     protected Activity launchActivity(Class<? extends Activity> activityClass) {
         final Intent intent = new Intent(mTargetContext, activityClass);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         return getInstrumentation().startActivitySync(intent);
     }
 
@@ -339,6 +351,10 @@
      * Test activity that launches {@link ResultActivity} for result.
      */
     public static class LaunchForResultActivity extends CallbackTrackingActivity {
+        public static String EXTRA_LAUNCH_ON_RESULT = "LAUNCH_ON_RESULT";
+        public static String EXTRA_LAUNCH_ON_RESUME_AFTER_RESULT = "LAUNCH_ON_RESUME_AFTER_RESULT";
+
+        boolean mReceivedResultOk;
 
         @Override
         protected void onCreate(Bundle savedInstanceState) {
@@ -346,6 +362,24 @@
             startForResult();
         }
 
+        @Override
+        protected void onResume() {
+            super.onResume();
+            if (mReceivedResultOk
+                    && getIntent().getBooleanExtra(EXTRA_LAUNCH_ON_RESUME_AFTER_RESULT, false)) {
+                startActivity(new Intent(this, CallbackTrackingActivity.class));
+            }
+        }
+
+        @Override
+        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+            super.onActivityResult(requestCode, resultCode, data);
+            mReceivedResultOk = resultCode == RESULT_OK;
+            if (mReceivedResultOk && getIntent().getBooleanExtra(EXTRA_LAUNCH_ON_RESULT, false)) {
+                startActivity(new Intent(this, CallbackTrackingActivity.class));
+            }
+        }
+
         private void startForResult() {
             final Intent intent = new Intent(this, ResultActivity.class);
             intent.putExtras(getIntent());
@@ -353,12 +387,28 @@
         }
     }
 
-    /** Test activity that is started for result and finishes itself in ON_RESUME. */
+    /** Test activity that is started for result and finishes itself. */
     public static class ResultActivity extends CallbackTrackingActivity {
         @Override
+        protected void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            setResult(RESULT_OK);
+            if (getIntent().getBooleanExtra(EXTRA_FINISH_IN_ON_CREATE, false)) {
+                finish();
+            }
+        }
+
+        @Override
+        protected void onStart() {
+            super.onStart();
+            if (getIntent().getBooleanExtra(EXTRA_FINISH_IN_ON_START, false)) {
+                finish();
+            }
+        }
+
+        @Override
         protected void onResume() {
             super.onResume();
-            setResult(RESULT_OK);
             final Intent intent = getIntent();
             if (intent.getBooleanExtra(EXTRA_FINISH_IN_ON_RESUME, false)) {
                 finish();
@@ -366,6 +416,26 @@
                 new Handler().postDelayed(() -> finish(), 2000);
             }
         }
+
+        @Override
+        protected void onPause() {
+            super.onPause();
+            if (getIntent().getBooleanExtra(EXTRA_FINISH_IN_ON_PAUSE, false)) {
+                finish();
+            }
+        }
+
+        @Override
+        protected void onStop() {
+            super.onStop();
+            if (getIntent().getBooleanExtra(EXTRA_FINISH_IN_ON_STOP, false)) {
+                finish();
+            }
+        }
+    }
+
+    /** Test activity with NoDisplay theme that can finish itself. */
+    public static class NoDisplayActivity extends ResultActivity {
     }
 
     /** Test activity that can call {@link Activity#recreate()} if requested in a new intent. */
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleFreeformTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleFreeformTests.java
index 767097b..b5cbccd 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleFreeformTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleFreeformTests.java
@@ -243,7 +243,7 @@
     public void testPreQTopProcessResumedActivityInFreeform() throws Exception {
         // Resume app switches, so the activities that we are going to launch won't be deferred
         // since Home activity was started in #setUp().
-        SystemUtil.runWithShellPermissionIdentity(ActivityManager::resumeAppSwitches);
+        resumeAppSwitches();
 
         final ActivityOptions launchOptions = ActivityOptions.makeBasic();
         launchOptions.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleKeyguardTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleKeyguardTests.java
index 5ea5526..bd17ce7 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleKeyguardTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleKeyguardTests.java
@@ -31,7 +31,6 @@
 import android.content.Intent;
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
 
 import org.junit.Test;
@@ -47,7 +46,6 @@
 public class ActivityLifecycleKeyguardTests extends ActivityLifecycleClientTestBase {
 
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testSingleLaunch() throws Exception {
         assumeTrue(supportsSecureLock());
         try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
@@ -61,7 +59,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testKeyguardShowHide() throws Exception {
         assumeTrue(supportsSecureLock());
 
@@ -84,7 +81,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testKeyguardShowHideOverSplitScreen() throws Exception {
         assumeTrue(supportsSecureLock());
         assumeTrue(supportsSplitScreenMultiWindow());
@@ -124,7 +120,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testKeyguardShowHideOverPip() throws Exception {
         if (!supportsPip()) {
             // Skipping test: no Picture-In-Picture support
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecyclePipTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecyclePipTests.java
index 9024c9a..2fcafd8 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecyclePipTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecyclePipTests.java
@@ -50,7 +50,6 @@
  * Build/Install/Run:
  *     atest CtsWindowManagerDeviceTestCases:ActivityLifecyclePipTests
  */
-@FlakyTest(bugId = 77652261)
 @MediumTest
 @Presubmit
 public class ActivityLifecyclePipTests extends ActivityLifecycleClientTestBase {
@@ -249,6 +248,7 @@
     }
 
     @Test
+    @FlakyTest(bugId = 127741025)
     public void testPipAboveSplitScreen() throws Exception {
         // Launch first activity
         final Activity firstActivity =
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java
index 64e34fb..8a4fbaf 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java
@@ -70,8 +70,8 @@
         assumeTrue(supportsSplitScreenMultiWindow());
     }
 
+    @FlakyTest(bugId = 137329632)
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testResumedWhenRecreatedFromInNonFocusedStack() throws Exception {
         // Launch first activity
         final Activity firstActivity =
@@ -93,20 +93,26 @@
         getLifecycleLog().clear();
 
         // Start an activity in separate task (will be placed in secondary stack)
-        getLaunchActivityBuilder().execute();
+        final Intent thirdIntent = new Intent();
+        thirdIntent.setFlags(FLAG_ACTIVITY_MULTIPLE_TASK | FLAG_ACTIVITY_NEW_TASK);
+        mThirdActivityTestRule.launchActivity(thirdIntent);
 
         // Finish top activity
         secondActivity.finish();
 
-        waitAndAssertActivityStates(state(secondActivity, ON_DESTROY));
-        waitAndAssertActivityStates(state(firstActivity, ON_RESUME));
+        waitAndAssertActivityStates(state(secondActivity, ON_DESTROY),
+                state(firstActivity, ON_RESUME));
 
         // Verify that the first activity was recreated to resume as it was created before
         // windowing mode was switched
         LifecycleVerifier.assertRecreateAndResumeSequence(FirstActivity.class, getLifecycleLog());
+
+        // Verify that the lifecycle state did not change for activity in non-focused stack
+        LifecycleVerifier.assertLaunchSequence(ThirdActivity.class, getLifecycleLog());
     }
 
     @Test
+    @FlakyTest(bugId = 127741025)
     public void testOccludingMovedBetweenStacks() throws Exception {
         // Launch first activity
         final Activity firstActivity =
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java
index 1df98db..de26026 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java
@@ -21,6 +21,8 @@
 import static android.server.wm.ActivityManagerState.STATE_PAUSED;
 import static android.server.wm.ActivityManagerState.STATE_STOPPED;
 import static android.server.wm.UiDeviceUtils.pressBackButton;
+import static android.server.wm.lifecycle.ActivityLifecycleClientTestBase.LaunchForResultActivity.EXTRA_LAUNCH_ON_RESULT;
+import static android.server.wm.lifecycle.ActivityLifecycleClientTestBase.LaunchForResultActivity.EXTRA_LAUNCH_ON_RESUME_AFTER_RESULT;
 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_ACTIVITY_RESULT;
 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_CREATE;
 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_DESTROY;
@@ -34,6 +36,7 @@
 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_TOP_POSITION_GAINED;
 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_TOP_POSITION_LOST;
 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.PRE_ON_CREATE;
+import static android.server.wm.lifecycle.LifecycleVerifier.transition;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_180;
 import static android.view.Surface.ROTATION_270;
@@ -51,6 +54,7 @@
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
 
 import com.android.compatibility.common.util.AmUtils;
 
@@ -63,7 +67,6 @@
  * Build/Install/Run:
  *     atest CtsWindowManagerDeviceTestCases:ActivityLifecycleTests
  */
-@FlakyTest(bugId = 77652261)
 @MediumTest
 @Presubmit
 public class ActivityLifecycleTests extends ActivityLifecycleClientTestBase {
@@ -273,6 +276,85 @@
                 Arrays.asList(ON_RESUME), "secondDestroy");
     }
 
+    @FlakyTest(bugId=137329632)
+    @Test
+    public void testFinishBottom() throws Exception {
+        final Activity bottomActivity = mFirstActivityTestRule.launchActivity(new Intent());
+        final Activity topActivity = mSecondActivityTestRule.launchActivity(new Intent());
+        waitAndAssertActivityStates(state(bottomActivity, ON_STOP),
+                state(topActivity, ON_RESUME));
+
+        // Finish the activity on the bottom
+        getLifecycleLog().clear();
+        bottomActivity.finish();
+
+        // Assert that activity on the bottom went directly to destroyed state, and activity on top
+        // did not get any lifecycle changes.
+        waitAndAssertActivityStates(state(bottomActivity, ON_DESTROY));
+        LifecycleVerifier.assertSequence(FirstActivity.class, getLifecycleLog(),
+                Arrays.asList(ON_DESTROY), "destroyOnBottom");
+        LifecycleVerifier.assertEmptySequence(SecondActivity.class, getLifecycleLog(),
+                "destroyOnBottom");
+    }
+
+    @FlakyTest(bugId=137329632)
+    @Test
+    public void testFinishAndLaunchOnResult() throws Exception {
+        testLaunchForResultAndLaunchAfterResultSequence(EXTRA_LAUNCH_ON_RESULT);
+    }
+
+    @FlakyTest(bugId=137329632)
+    @Test
+    public void testFinishAndLaunchAfterOnResultInOnResume() throws Exception {
+        testLaunchForResultAndLaunchAfterResultSequence(EXTRA_LAUNCH_ON_RESUME_AFTER_RESULT);
+    }
+
+    /**
+     * This triggers launch of an activity for result, which immediately finishes. After receiving
+     * result new activity launch is triggered automatically.
+     * @see android.server.wm.lifecycle.ActivityLifecycleClientTestBase.LaunchForResultActivity
+     */
+    private void testLaunchForResultAndLaunchAfterResultSequence(String flag) {
+        final Intent intent = new Intent();
+        intent.putExtra(flag, true);
+        intent.putExtra(EXTRA_FINISH_IN_ON_RESUME, true);
+        mLaunchForResultActivityTestRule.launchActivity(intent);
+
+        waitAndAssertActivityStates(state(CallbackTrackingActivity.class, ON_TOP_POSITION_GAINED),
+                state(ResultActivity.class, ON_DESTROY),
+                state(LaunchForResultActivity.class, ON_STOP));
+        LifecycleVerifier.assertOrder(getLifecycleLog(), Arrays.asList(
+                // Base launching activity starting.
+                transition(LaunchForResultActivity.class, PRE_ON_CREATE),
+                transition(LaunchForResultActivity.class, ON_CREATE),
+                transition(LaunchForResultActivity.class, ON_START),
+                transition(LaunchForResultActivity.class, ON_POST_CREATE),
+                transition(LaunchForResultActivity.class, ON_RESUME),
+                transition(LaunchForResultActivity.class, ON_TOP_POSITION_GAINED),
+                // An activity is automatically launched for result.
+                transition(LaunchForResultActivity.class, ON_TOP_POSITION_LOST),
+                transition(LaunchForResultActivity.class, ON_PAUSE),
+                transition(ResultActivity.class, PRE_ON_CREATE),
+                transition(ResultActivity.class, ON_CREATE),
+                transition(ResultActivity.class, ON_START),
+                transition(ResultActivity.class, ON_RESUME),
+                transition(ResultActivity.class, ON_TOP_POSITION_GAINED),
+                // Activity that was launched for result is finished automatically - the base
+                // launching activity is brought to front.
+                transition(LaunchForResultActivity.class, ON_ACTIVITY_RESULT),
+                transition(LaunchForResultActivity.class, ON_RESUME),
+                transition(LaunchForResultActivity.class, ON_TOP_POSITION_GAINED),
+                // New activity is launched after receiving result in base activity.
+                transition(LaunchForResultActivity.class, ON_TOP_POSITION_LOST),
+                transition(LaunchForResultActivity.class, ON_PAUSE),
+                transition(CallbackTrackingActivity.class, PRE_ON_CREATE),
+                transition(CallbackTrackingActivity.class, ON_CREATE),
+                transition(CallbackTrackingActivity.class, ON_START),
+                transition(CallbackTrackingActivity.class, ON_RESUME),
+                transition(CallbackTrackingActivity.class, ON_TOP_POSITION_GAINED)),
+                "launchForResultAndLaunchAfterOnResult");
+    }
+
     @Test
     public void testLaunchAndDestroy() throws Exception {
         final Activity activity = mFirstActivityTestRule.launchActivity(new Intent());
@@ -730,4 +812,91 @@
         LifecycleVerifier.assertSequence(SingleTopActivity.class, getLifecycleLog(),
                 expectedSequence, "newIntent");
     }
+
+    @Test
+    public void testFinishInOnCreate() throws Exception {
+        verifyFinishAtStage(mResultActivityTestRule, ResultActivity.class,
+                EXTRA_FINISH_IN_ON_CREATE, Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_DESTROY),
+                "onCreate");
+    }
+
+    @Test
+    public void testFinishInOnCreateNoDisplay() throws Exception {
+        verifyFinishAtStage(mNoDisplayActivityTestRule, NoDisplayActivity.class,
+                EXTRA_FINISH_IN_ON_CREATE, Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_DESTROY),
+                "onCreate");
+    }
+
+    @Test
+    public void testFinishInOnStart() throws Exception {
+        verifyFinishAtStage(mResultActivityTestRule, ResultActivity.class,
+                EXTRA_FINISH_IN_ON_START, Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START,
+                        ON_POST_CREATE, ON_STOP, ON_DESTROY), "onStart");
+    }
+
+    @Test
+    public void testFinishInOnStartNoDisplay() throws Exception {
+        verifyFinishAtStage(mNoDisplayActivityTestRule, NoDisplayActivity.class,
+                EXTRA_FINISH_IN_ON_START, Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START,
+                        ON_POST_CREATE, ON_STOP, ON_DESTROY), "onStart");
+    }
+
+    @Test
+    public void testFinishInOnResume() throws Exception {
+        verifyFinishAtStage(mResultActivityTestRule, ResultActivity.class,
+                EXTRA_FINISH_IN_ON_RESUME, Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START,
+                        ON_POST_CREATE, ON_RESUME, ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST,
+                        ON_PAUSE, ON_STOP, ON_DESTROY), "onResume");
+    }
+
+    @Test
+    public void testFinishInOnResumeNoDisplay() throws Exception {
+        verifyFinishAtStage(mNoDisplayActivityTestRule, NoDisplayActivity.class,
+                EXTRA_FINISH_IN_ON_RESUME, Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START,
+                        ON_POST_CREATE, ON_RESUME, ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST,
+                        ON_PAUSE, ON_STOP, ON_DESTROY), "onResume");
+    }
+
+    private void verifyFinishAtStage(ActivityTestRule rule, Class<? extends Activity> activityClass,
+            String finishStageExtra, List<LifecycleLog.ActivityCallback> expectedSequence,
+            String stageName) {
+        final Intent intent = new Intent();
+        intent.putExtra(finishStageExtra, true);
+        rule.launchActivity(intent);
+
+        waitAndAssertActivityTransitions(activityClass, expectedSequence, "finish in " + stageName);
+    }
+
+    @Test
+    public void testFinishInOnPause() throws Exception {
+        verifyFinishAtStage(mResultActivityTestRule, ResultActivity.class,
+                EXTRA_FINISH_IN_ON_PAUSE, "onPause", mTranslucentActivityTestRule);
+    }
+
+    @Test
+    public void testFinishInOnStop() throws Exception {
+        verifyFinishAtStage(mResultActivityTestRule, ResultActivity.class,
+                EXTRA_FINISH_IN_ON_STOP, "onStop", mFirstActivityTestRule);
+    }
+
+    private void verifyFinishAtStage(ActivityTestRule rule, Class<? extends Activity> activityClass,
+            String finishStageExtra, String stageName, ActivityTestRule launchOnTopRule) {
+        final Intent intent = new Intent();
+        intent.putExtra(finishStageExtra, true);
+
+        // Activity will finish itself after onResume, so need to launch an extra activity on
+        // top to get it there.
+        final Activity testActivity = rule.launchActivity(intent);
+
+        // Wait for the activity to resume and gain top position
+        waitAndAssertActivityStates(state(testActivity, ON_TOP_POSITION_GAINED));
+
+        // Launch an activity on top, which will make the first one paused or stopped.
+        launchOnTopRule.launchActivity(new Intent());
+
+        final List<LifecycleLog.ActivityCallback> expectedSequence =
+                LifecycleVerifier.getLaunchAndDestroySequence(activityClass);
+        waitAndAssertActivityTransitions(activityClass, expectedSequence, "finish in " + stageName);
+
+    }
 }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTopResumedStateTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTopResumedStateTests.java
index b047127..7d0b4b3 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTopResumedStateTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTopResumedStateTests.java
@@ -19,6 +19,7 @@
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT;
 import static android.server.wm.UiDeviceUtils.pressHomeButton;
 import static android.server.wm.app.Components.PipActivity.EXTRA_ENTER_PIP;
 import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_ACTIVITY_RESULT;
@@ -68,7 +69,6 @@
  * Build/Install/Run:
  *     atest CtsWindowManagerDeviceTestCases:ActivityLifecycleTopResumedStateTests
  */
-@FlakyTest(bugId = 117135575)
 @MediumTest
 @Presubmit
 public class ActivityLifecycleTopResumedStateTests extends ActivityLifecycleClientTestBase {
@@ -988,6 +988,93 @@
         }
     }
 
+    @FlakyTest(bugId=137329632)
+    @Test
+    public void testFinishOnDifferentDisplay_nonFocused() throws Exception {
+        assumeTrue(supportsMultiDisplay());
+
+        // Launch activity on some display.
+        final Activity callbackTrackingActivity =
+                mCallbackTrackingActivityTestRule.launchActivity(new Intent());
+
+        waitAndAssertTopResumedActivity(getComponentName(CallbackTrackingActivity.class),
+                DEFAULT_DISPLAY, "Activity launched on default display must be focused");
+
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new simulated display
+            final ActivityManagerState.ActivityDisplay newDisplay
+                    = virtualDisplaySession.setSimulateDisplay(true).createDisplay();
+
+            // Launch another activity on new secondary display.
+            getLifecycleLog().clear();
+            final ActivityOptions launchOptions = ActivityOptions.makeBasic();
+            launchOptions.setLaunchDisplayId(newDisplay.mId);
+            final Intent newDisplayIntent = new Intent(mContext, SingleTopActivity.class);
+            newDisplayIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
+            mTargetContext.startActivity(newDisplayIntent, launchOptions.toBundle());
+            waitAndAssertTopResumedActivity(getComponentName(SingleTopActivity.class),
+                    newDisplay.mId, "Activity launched on secondary display must be focused");
+
+            // Finish the activity on the default display
+            getLifecycleLog().clear();
+            callbackTrackingActivity.finish();
+
+            // Verify that activity was actually destroyed
+            waitAndAssertActivityStates(state(CallbackTrackingActivity.class, ON_DESTROY));
+            // Verify that the activity on a different display lost the top focused state
+            LifecycleVerifier.assertSequence(SingleTopActivity.class, getLifecycleLog(),
+                    Arrays.asList(ON_TOP_POSITION_LOST), "destructionOnDifferentDisplay");
+        }
+    }
+
+    @FlakyTest(bugId=137329632)
+    @Test
+    public void testFinishOnDifferentDisplay_focused() throws Exception {
+        assumeTrue(supportsMultiDisplay());
+
+        // Launch activity on some display.
+        final Activity bottomActivity = mSecondActivityTestRule.launchActivity(new Intent());
+        final Activity callbackTrackingActivity =
+                mCallbackTrackingActivityTestRule.launchActivity(new Intent());
+
+        waitAndAssertTopResumedActivity(getComponentName(CallbackTrackingActivity.class),
+                DEFAULT_DISPLAY, "Activity launched on default display must be focused");
+
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new simulated display
+            final ActivityManagerState.ActivityDisplay newDisplay
+                    = virtualDisplaySession.setSimulateDisplay(true).createDisplay();
+
+            // Launch another activity on new secondary display.
+            getLifecycleLog().clear();
+            final ActivityOptions launchOptions = ActivityOptions.makeBasic();
+            launchOptions.setLaunchDisplayId(newDisplay.mId);
+            final Intent newDisplayIntent = new Intent(mContext, SingleTopActivity.class);
+            newDisplayIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
+            mTargetContext.startActivity(newDisplayIntent, launchOptions.toBundle());
+            waitAndAssertTopResumedActivity(getComponentName(SingleTopActivity.class),
+                    newDisplay.mId, "Activity launched on secondary display must be focused");
+
+            // Bring the focus back
+            final Intent sameInstanceIntent = new Intent(mContext, CallbackTrackingActivity.class);
+            sameInstanceIntent.setFlags(FLAG_ACTIVITY_REORDER_TO_FRONT);
+            bottomActivity.startActivity(sameInstanceIntent, null);
+            waitAndAssertActivityStates(
+                    state(CallbackTrackingActivity.class, ON_TOP_POSITION_GAINED));
+
+            // Finish the focused activity
+            getLifecycleLog().clear();
+            callbackTrackingActivity.finish();
+
+            // Verify that lifecycle of the activity on a different display did not change.
+            // Top resumed state will be given to home activity on that display.
+            waitAndAssertActivityStates(state(CallbackTrackingActivity.class, ON_DESTROY),
+                    state(SecondActivity.class, ON_RESUME));
+            LifecycleVerifier.assertEmptySequence(SingleTopActivity.class, getLifecycleLog(),
+                    "destructionOnDifferentDisplay");
+        }
+    }
+
     @Test
     public void testTopPositionNotSwitchedToPip() throws Exception {
         assumeTrue(supportsPip());
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java
index b6fb802..86c3d93 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java
@@ -31,12 +31,16 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
 
 import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.os.Bundle;
 import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.FlakyTest;
+
 import android.server.wm.ActivityLauncher;
 
 import org.junit.Test;
@@ -206,6 +210,7 @@
      * - Instance of single task activity is only one in its task.
      */
     @Test
+    @FlakyTest(bugId = 127741025)
     public void testLaunchSingleTaskActivity() {
         // Launch a standard activity.
         launchActivity(STANDARD_ACTIVITY);
@@ -354,11 +359,9 @@
                 mAmWmState.getAmState().getTaskByActivity(STANDARD_ACTIVITY).getTaskId());
         // Make sure the second standard activity is finished.
         final String waitFinishMsg = "Instance of second standard activity must not exist";
-        mAmWmState.waitForWithAmState((amState) ->
-            0 == amState.getActivityCountInTask(taskId, SECOND_STANDARD_ACTIVITY),
-            waitFinishMsg);
-        assertEquals(waitFinishMsg, 0,
-            mAmWmState.getAmState().getActivityCountInTask(taskId, SECOND_STANDARD_ACTIVITY));
+        assertTrue(waitFinishMsg, mAmWmState.waitForWithAmState(
+                amState -> 0 == amState.getActivityCountInTask(taskId, SECOND_STANDARD_ACTIVITY),
+                waitFinishMsg));
     }
 
     @Test
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityTests.java
new file mode 100644
index 0000000..6d2bab9
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityTests.java
@@ -0,0 +1,81 @@
+/*
+ * 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.server.wm.lifecycle;
+
+import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_DESTROY;
+import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_RESUME;
+import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_STOP;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+import android.app.Activity;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.MediumTest;
+
+import org.junit.Test;
+
+/**
+ * Tests for {@link Activity} class APIs.
+ *
+ * Build/Install/Run:
+ *      atest CtsWindowManagerDeviceTestCases:ActivityTests
+ */
+@Presubmit
+@MediumTest
+@FlakyTest(bugId=137329632)
+public class ActivityTests extends ActivityLifecycleClientTestBase {
+    @Test
+    public void testReleaseActivityInstance_visible() {
+        final Activity activity = launchActivity(FirstActivity.class);
+        waitAndAssertActivityStates(state(activity, ON_RESUME));
+
+        getLifecycleLog().clear();
+        assertFalse("Launched and visible activity must be released", activity.releaseInstance());
+        LifecycleVerifier.assertEmptySequence(FirstActivity.class, getLifecycleLog(),
+                "tryReleaseInstance");
+    }
+
+    @Test
+    public void testReleaseActivityInstance_invisible() {
+        // Launch two activities - second one to cover the first one and make it invisible.
+        final Activity firstActivity = launchActivity(FirstActivity.class);
+        final Activity secondActivity = launchActivity(SecondActivity.class);
+        waitAndAssertActivityStates(state(secondActivity, ON_RESUME),
+                state(firstActivity, ON_STOP));
+        // Wait for activity to report saved state to the server.
+        getInstrumentation().waitForIdleSync();
+
+        // Release the instance of the non-visible activity below.
+        getLifecycleLog().clear();
+        assertTrue("It must be possible to release an instance of an invisible activity",
+                firstActivity.releaseInstance());
+        waitAndAssertActivityStates(state(firstActivity, ON_DESTROY));
+        LifecycleVerifier.assertEmptySequence(SecondActivity.class, getLifecycleLog(),
+                "releaseInstance");
+
+        // Finish the top activity to navigate back to the first one and re-create it.
+        getLifecycleLog().clear();
+        secondActivity.finish();
+        waitAndAssertActivityStates(state(secondActivity, ON_DESTROY));
+        LifecycleVerifier.assertLaunchSequence(FirstActivity.class, getLifecycleLog());
+    }
+}
\ No newline at end of file
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/LifecycleVerifier.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/LifecycleVerifier.java
index 32df6b7..2aecea9 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/LifecycleVerifier.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/LifecycleVerifier.java
@@ -72,6 +72,14 @@
                 : Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START, ON_RESUME);
     }
 
+    static List<ActivityCallback> getLaunchAndDestroySequence(
+            Class<? extends Activity> activityClass) {
+        final List<ActivityCallback> expectedTransitions = new ArrayList<>();
+        expectedTransitions.addAll(getLaunchSequence(activityClass));
+        expectedTransitions.addAll(getResumeToDestroySequence(activityClass));
+        return expectedTransitions;
+    }
+
     static void assertLaunchSequence(Class<? extends Activity> launchingActivity,
             Class<? extends Activity> existingActivity, LifecycleLog lifecycleLog,
             boolean launchingIsTranslucent) {
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityAndWindowManagersState.java b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityAndWindowManagersState.java
index a3102c5..928d618 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityAndWindowManagersState.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityAndWindowManagersState.java
@@ -46,7 +46,6 @@
 
 import android.content.ComponentName;
 import android.graphics.Rect;
-import android.os.SystemClock;
 import android.server.wm.ActivityManagerState.ActivityStack;
 import android.server.wm.ActivityManagerState.ActivityTask;
 import android.server.wm.WindowManagerState.Display;
@@ -59,9 +58,7 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.function.BiPredicate;
-import java.util.function.BooleanSupplier;
 import java.util.function.Predicate;
-import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
 /**
@@ -143,53 +140,45 @@
      */
     private void waitForValidState(boolean compareTaskAndStackBounds,
             WaitForValidActivityState... waitForActivitiesVisible) {
-        for (int retry = 1; retry <= 5; retry++) {
+        if (!Condition.waitFor("valid stacks and activities states", () -> {
             // TODO: Get state of AM and WM at the same time to avoid mismatches caused by
             // requesting dump in some intermediate state.
             mAmState.computeState();
             mWmState.computeState();
-            if (shouldWaitForSanityCheck(compareTaskAndStackBounds)
+            return !(shouldWaitForSanityCheck(compareTaskAndStackBounds)
                     || shouldWaitForValidStacks(compareTaskAndStackBounds)
                     || shouldWaitForActivities(waitForActivitiesVisible)
-                    || shouldWaitForWindows()) {
-                logAlways("***Waiting for valid stacks and activities states... retry=" + retry);
-                SystemClock.sleep(1000);
-            } else {
-                return;
-            }
+                    || shouldWaitForWindows());
+        })) {
+            logE("***Waiting for states failed: " + Arrays.toString(waitForActivitiesVisible));
         }
-        logE("***Waiting for states failed: " + Arrays.toString(waitForActivitiesVisible));
     }
 
     /**
      * Ensures all exiting windows have been removed.
      */
     void waitForAllExitingWindows() {
-        List<WindowState> exitingWindows = null;
-        for (int retry = 1; retry <= 5; retry++) {
-            mWmState.computeState();
-            exitingWindows = mWmState.getExitingWindows();
-            if (exitingWindows.isEmpty()) {
-                return;
-            }
-            logAlways("***Waiting for all exiting windows have been removed... retry=" + retry);
-            SystemClock.sleep(1000);
-        }
-        fail("All exiting windows have been removed, actual=" + exitingWindows.stream()
-                .map(WindowState::getName)
-                .collect(Collectors.joining(",")));
+        Condition.<List<WindowState>>waitForResult("all exiting windows to be removed",
+                condition -> condition
+                        .setResultSupplier(() -> {
+                            mWmState.computeState();
+                            return mWmState.getExitingWindows();
+                        })
+                        .setResultValidator(List<WindowState>::isEmpty)
+                        .setOnFailure(exitingWindows -> {
+                            fail("All exiting windows have been removed, actual="
+                                    + exitingWindows.stream().map(WindowState::getName)
+                                            .collect(Collectors.joining(",")));
+                        }));
     }
 
     void waitForAllStoppedActivities() {
-        for (int retry = 1; retry <= 5; retry++) {
+        if (!Condition.waitFor("all started activities have been removed", () -> {
             mAmState.computeState();
-            if (!mAmState.containsStartedActivities()) {
-                return;
-            }
-            logAlways("***Waiting for all started activities have been removed... retry=" + retry);
-            SystemClock.sleep(1500);
+            return !mAmState.containsStartedActivities();
+        })) {
+            fail("All started activities have been removed");
         }
-        fail("All started activities have been removed");
     }
 
     /**
@@ -201,33 +190,12 @@
      * for debugger.
      */
     void waitForDebuggerWindowVisible(ComponentName activityName) {
-        for (int retry = 1; retry <= 5; retry++) {
+        Condition.waitFor("debugger window", () -> {
             mAmState.computeState();
             mWmState.computeState();
-            if (shouldWaitForDebuggerWindow(activityName)
-                    || shouldWaitForActivityRecords(activityName)) {
-                logAlways("***Waiting for debugger window... retry=" + retry);
-                SystemClock.sleep(1000);
-            } else {
-                return;
-            }
-        }
-        logE("***Waiting for debugger window failed");
-    }
-
-    <T> T waitForValidProduct(Supplier<T> supplier, String productName, Predicate<T> tester) {
-        T product = null;
-        for (int retry = 1; retry <= 5; retry++) {
-            product = supplier.get();
-            if (product != null) {
-                if (tester.test(product)) {
-                    break;
-                }
-            }
-            logAlways("***Waiting for valid " + productName + "... retry=" + retry);
-            SystemClock.sleep(1000);
-        }
-        return product;
+            return !shouldWaitForDebuggerWindow(activityName)
+                    && !shouldWaitForActivityRecords(activityName);
+        });
     }
 
     void waitForHomeActivityVisible() {
@@ -247,37 +215,34 @@
             waitForHomeActivityVisible();
         } else {
             waitForWithAmState(ActivityManagerState::isRecentsActivityVisible,
-                    "***Waiting for recents activity to be visible...");
+                    "recents activity to be visible");
         }
     }
 
     void waitForKeyguardShowingAndNotOccluded() {
         waitForWithAmState(state -> state.getKeyguardControllerState().keyguardShowing
                         && !state.getKeyguardControllerState().isKeyguardOccluded(DEFAULT_DISPLAY),
-                "***Waiting for Keyguard showing...");
+                "Keyguard showing");
     }
 
     void waitForKeyguardShowingAndOccluded() {
         waitForWithAmState(state -> state.getKeyguardControllerState().keyguardShowing
                         && state.getKeyguardControllerState().isKeyguardOccluded(DEFAULT_DISPLAY),
-                "***Waiting for Keyguard showing and occluded...");
+                "Keyguard showing and occluded");
     }
 
     void waitForAodShowing() {
-        waitForWithAmState(state -> state.getKeyguardControllerState().aodShowing,
-                "***Waiting for AOD showing...");
-
+        waitForWithAmState(state -> state.getKeyguardControllerState().aodShowing, "AOD showing");
     }
 
     void waitForKeyguardGone() {
         waitForWithAmState(state -> !state.getKeyguardControllerState().keyguardShowing,
-                "***Waiting for Keyguard gone...");
+                "Keyguard gone");
     }
 
     /** Wait for specific rotation for the default display. Values are Surface#Rotation */
     void waitForRotation(int rotation) {
-        waitForWithWmState(state -> state.getRotation() == rotation,
-                "***Waiting for Rotation: " + rotation);
+        waitForWithWmState(state -> state.getRotation() == rotation, "Rotation: " + rotation);
     }
 
     /**
@@ -286,7 +251,7 @@
      */
     void waitForLastOrientation(int orientation) {
         waitForWithWmState(state -> state.getLastOrientation() == orientation,
-                "***Waiting for LastOrientation: " + orientation);
+                "LastOrientation: " + orientation);
     }
 
     /**
@@ -299,30 +264,28 @@
                 return false;
             }
             return task.mFullConfiguration.orientation == orientation;
-        }, "***Waiting for Activity orientation: " + orientation);
+        }, "orientation of " + getActivityName(activityName) + " to be " + orientation);
     }
 
     void waitForDisplayUnfrozen() {
-        waitForWithWmState(state -> !state.isDisplayFrozen(),
-                "***Waiting for Display unfrozen");
+        waitForWithWmState(state -> !state.isDisplayFrozen(), "Display unfrozen");
     }
 
     public void waitForActivityState(ComponentName activityName, String activityState) {
         waitForWithAmState(state -> state.hasActivityState(activityName, activityState),
-                "***Waiting for Activity State: " + activityState);
+                "state of " + getActivityName(activityName) + " to be " + activityState);
     }
 
     public void waitForActivityRemoved(ComponentName activityName) {
         waitForWithAmState((state) -> !state.containsActivity(activityName),
-                "Waiting for activity to be removed");
+                "activity to be removed");
         waitForWithWmState((state) -> !state.containsWindow(getWindowName(activityName)),
-                "Waiting for activity window to be gone");
+                "activity window to be gone");
     }
 
     @Deprecated
     void waitForFocusedStack(int stackId) {
-        waitForWithAmState(state -> state.getFocusedStackId() == stackId,
-                "***Waiting for focused stack...");
+        waitForWithAmState(state -> state.getFocusedStackId() == stackId, "focused stack");
     }
 
     void waitForFocusedStack(int windowingMode, int activityType) {
@@ -331,59 +294,46 @@
                                 || state.getFocusedStackActivityType() == activityType)
                         && (windowingMode == WINDOWING_MODE_UNDEFINED
                                 || state.getFocusedStackWindowingMode() == windowingMode),
-                "***Waiting for focused stack...");
+                "focused stack");
     }
 
     void waitForPendingActivityContain(ComponentName activity) {
         waitForWithAmState(state -> state.pendingActivityContain(activity),
-                "***Waiting for activity in pending list...");
+                getActivityName(activity) + " in pending list");
     }
 
     void waitForAppTransitionIdleOnDisplay(int displayId) {
         waitForWithWmState(
                 state -> WindowManagerState.APP_STATE_IDLE.equals(
                         state.getDisplay(displayId).getAppTransitionState()),
-                "***Waiting for app transition idle on Display " + displayId + " ...");
+                "app transition idle on Display " + displayId);
     }
 
-
     void waitAndAssertNavBarShownOnDisplay(int displayId) {
-        waitForWithWmState(
+        assertTrue(waitForWithWmState(
                 state -> state.getAndAssertSingleNavBarWindowOnDisplay(displayId) != null,
-                "***Waiting for navigation bar #" + displayId + " show...");
-        final WindowState ws = getWmState().getAndAssertSingleNavBarWindowOnDisplay(displayId);
-
-        assertNotNull(ws);
+                "navigation bar #" + displayId + " show"));
     }
 
-    public void waitForWithAmState(Predicate<ActivityManagerState> waitCondition, String message) {
-        waitFor((amState, wmState) -> waitCondition.test(amState), message);
+    public boolean waitForWithAmState(Predicate<ActivityManagerState> waitCondition,
+            String message) {
+        return waitFor((amState, wmState) -> waitCondition.test(amState), message);
     }
 
-    public void waitForWithWmState(Predicate<WindowManagerState> waitCondition, String message) {
-        waitFor((amState, wmState) -> waitCondition.test(wmState), message);
+    public boolean waitForWithWmState(Predicate<WindowManagerState> waitCondition, String message) {
+        return waitFor((amState, wmState) -> waitCondition.test(wmState), message);
     }
 
-    void waitFor(
+    /** @return {@code true} if the wait is successful; {@code false} if timeout occurs. */
+    boolean waitFor(
             BiPredicate<ActivityManagerState, WindowManagerState> waitCondition, String message) {
-        waitFor(message, () -> {
+        return Condition.waitFor(message, () -> {
             mAmState.computeState();
             mWmState.computeState();
             return waitCondition.test(mAmState, mWmState);
         });
     }
 
-    void waitFor(String message, BooleanSupplier waitCondition) {
-        for (int retry = 1; retry <= 5; retry++) {
-            if (waitCondition.getAsBoolean()) {
-                return;
-            }
-            logAlways(message + " retry=" + retry);
-            SystemClock.sleep(1000);
-        }
-        logE(message + " failed");
-    }
-
     /**
      * @return true if should wait for valid stacks state.
      */
@@ -955,9 +905,12 @@
     }
 
     void waitAndAssertImeWindowShownOnDisplay(int displayId) {
-        final WindowManagerState.WindowState imeWinState = waitForValidProduct(
-                this::getImeWindowState, "IME window",
-                w -> w.isShown() && w.getDisplayId() == displayId);
+        final WindowManagerState.WindowState imeWinState = Condition.waitForResult("IME window",
+                condition -> condition
+                        .setResultSupplier(this::getImeWindowState)
+                        .setResultValidator(
+                                w -> w != null && w.isShown() && w.getDisplayId() == displayId));
+
         assertNotNull("IME window must exist", imeWinState);
         assertTrue("IME window must be shown", imeWinState.isShown());
         assertEquals("IME window must be on the given display", displayId,
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityLauncher.java b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityLauncher.java
index b75eeea..0259e60 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityLauncher.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityLauncher.java
@@ -67,6 +67,11 @@
      */
     public static final String KEY_REORDER_TO_FRONT = "reorder_to_front";
     /**
+     * Key for boolean extra, indicates if launch task without presented to user.
+     * {@link ActivityOptions#makeTaskLaunchBehind()}.
+     */
+    public static final String KEY_LAUNCH_TASK_BEHIND = "launch_task_behind";
+    /**
      * Key for string extra with string representation of target component.
      */
     public static final String KEY_TARGET_COMPONENT = "target_component";
@@ -167,12 +172,15 @@
             newIntent.putExtras(intentExtras);
         }
 
-        ActivityOptions options = null;
+        ActivityOptions options = extras.getBoolean(KEY_LAUNCH_TASK_BEHIND)
+                ? ActivityOptions.makeTaskLaunchBehind() : null;
         final int displayId = extras.getInt(KEY_DISPLAY_ID, -1);
         if (displayId != -1) {
-            options = ActivityOptions.makeBasic();
+            if (options == null) {
+                options = ActivityOptions.makeBasic();
+            }
             options.setLaunchDisplayId(displayId);
-            if (extras.getBoolean(KEY_MULTIPLE_INSTANCES, true)) {
+            if (extras.getBoolean(KEY_MULTIPLE_INSTANCES)) {
                 newIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK);
             }
         }
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 f7337e9..f9c66f7 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
@@ -47,6 +47,7 @@
 import static android.server.wm.ActivityLauncher.KEY_INTENT_EXTRAS;
 import static android.server.wm.ActivityLauncher.KEY_INTENT_FLAGS;
 import static android.server.wm.ActivityLauncher.KEY_LAUNCH_ACTIVITY;
+import static android.server.wm.ActivityLauncher.KEY_LAUNCH_TASK_BEHIND;
 import static android.server.wm.ActivityLauncher.KEY_LAUNCH_TO_SIDE;
 import static android.server.wm.ActivityLauncher.KEY_MULTIPLE_INSTANCES;
 import static android.server.wm.ActivityLauncher.KEY_MULTIPLE_TASK;
@@ -62,7 +63,6 @@
 import static android.server.wm.ComponentNameUtils.getActivityName;
 import static android.server.wm.ComponentNameUtils.getLogTag;
 import static android.server.wm.StateLogger.log;
-import static android.server.wm.StateLogger.logAlways;
 import static android.server.wm.StateLogger.logE;
 import static android.server.wm.UiDeviceUtils.pressAppSwitchButton;
 import static android.server.wm.UiDeviceUtils.pressBackButton;
@@ -93,7 +93,7 @@
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.Surface.ROTATION_0;
 
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -123,7 +123,6 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.SystemClock;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.server.wm.CommandSession.ActivityCallback;
 import android.server.wm.CommandSession.ActivitySession;
@@ -163,7 +162,6 @@
 import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.Callable;
-import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.BooleanSupplier;
 import java.util.function.Consumer;
@@ -450,6 +448,15 @@
 
     }
 
+    /**
+     * After home key is pressed ({@link #pressHomeButton} is called), the later launch may be
+     * deferred if the calling uid doesn't have android.permission.STOP_APP_SWITCHES. This method
+     * will resume the temporary stopped state, so the launch won't be affected.
+     */
+    protected void resumeAppSwitches() {
+        SystemUtil.runWithShellPermissionIdentity(ActivityManager::resumeAppSwitches);
+    }
+
     protected void moveTopActivityToPinnedStack(int stackId) {
         SystemUtil.runWithShellPermissionIdentity(
                 () -> mAtm.moveTopActivityToPinnedStack(stackId, new Rect(0, 0, 500, 500))
@@ -498,6 +505,13 @@
 
         final long upTime = SystemClock.uptimeMillis();
         injectMotion(downTime, upTime, MotionEvent.ACTION_UP, x, y, displayId);
+
+        mAmWmState.waitForWithWmState(state -> state.getFocusedDisplayId() == displayId,
+                "top focused displayId: " + displayId);
+        // This is needed after a tap in multi-display to ensure that the display focus has really
+        // changed, if needed. The call to syncInputTransaction will wait until focus change has
+        // propagated from WMS to native input before returning.
+        getInstrumentation().getUiAutomation().syncInputTransactions();
     }
 
     protected void tapOnStackCenter(ActivityManagerState.ActivityStack stack) {
@@ -563,6 +577,12 @@
         getInstrumentation().waitForIdleSync();
     }
 
+    static void waitForOrFail(String message, BooleanSupplier condition) {
+        Condition.waitFor(new Condition<>(message, condition)
+                .setRetryIntervalMs(500)
+                .setOnFailure(unusedResult -> fail("FAILED because unsatisfied: " + message)));
+    }
+
     /** Returns the set of stack ids. */
     private HashSet<Integer> getStackIds() {
         mAmWmState.computeState(true);
@@ -833,9 +853,8 @@
         mAmWmState.waitForValidState(activityName);
         mAmWmState.waitForActivityState(activityName, STATE_RESUMED);
         final String activityClassName = getActivityName(activityName);
-        mAmWmState.waitForWithAmState(state ->
-                        activityClassName.equals(state.getFocusedActivity()),
-                "Waiting for activity to be on top");
+        mAmWmState.waitForWithAmState(state -> activityClassName.equals(state.getFocusedActivity()),
+                "activity to be on top");
 
         mAmWmState.assertSanity();
         mAmWmState.assertFocusedActivity(message, activityName);
@@ -1092,10 +1111,7 @@
                     android.os.Process.myUserHandle().getIdentifier())) {
                 mAmWmState.waitForAodShowing();
             } else {
-                for (int retry = 1; isDisplayOn(DEFAULT_DISPLAY) && retry <= 5; retry++) {
-                    logAlways("***Waiting for display to turn off... retry=" + retry);
-                    SystemClock.sleep(TimeUnit.SECONDS.toMillis(1));
-                }
+                Condition.waitFor("display to turn off", () -> !isDisplayOn(DEFAULT_DISPLAY));
             }
             return this;
         }
@@ -1215,7 +1231,10 @@
             mPreviousDegree = value;
 
             if (waitSystemUI) {
-                waitForRotationNotified();
+                Condition.waitFor(new Condition<>("rotation notified",
+                        // There will receive USER_ROTATION changed twice because when the device
+                        // rotates to 0deg, RotationContextButton will also set ROTATION_0 again.
+                        () -> mRotationObserver.count == 2).setRetryIntervalMs(500));
             }
             // Wait for settling rotation.
             mAmWmState.waitForRotation(value);
@@ -1233,19 +1252,6 @@
             super.close();
         }
 
-        private void waitForRotationNotified() {
-            for (int retry = 1; retry <= 5; retry++) {
-                // There will receive USER_ROTATION changed twice because when the device rotates to
-                // 0deg, RotationContextButton will also set ROTATION_0 again.
-                if (mRotationObserver.count == 2) {
-                    return;
-                }
-                logAlways("waitForRotationNotified retry=" + retry);
-                SystemClock.sleep(500);
-            }
-            logE("waitForRotationNotified skip");
-        }
-
         private class SettingsObserver extends ContentObserver {
             int count;
 
@@ -1404,40 +1410,6 @@
                 FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
     }
 
-    /**
-     * Base helper class for retrying validator success.
-     */
-    private abstract static class RetryValidator {
-
-        private static final int RETRY_LIMIT = 5;
-        private static final long RETRY_INTERVAL = TimeUnit.SECONDS.toMillis(1);
-
-        /**
-         * @return Error string if validation is failed, null if everything is fine.
-         **/
-        @Nullable
-        protected abstract String validate();
-
-        /**
-         * Executes {@link #validate()}. Retries {@link #RETRY_LIMIT} times with
-         * {@link #RETRY_INTERVAL} interval.
-         *
-         * @param waitingMessage logging message while waiting validation.
-         */
-        void assertValidator(String waitingMessage) {
-            String resultString = null;
-            for (int retry = 1; retry <= RETRY_LIMIT; retry++) {
-                resultString = validate();
-                if (resultString == null) {
-                    return;
-                }
-                logAlways(waitingMessage + ": " + resultString);
-                SystemClock.sleep(RETRY_INTERVAL);
-            }
-            fail(resultString);
-        }
-    }
-
     static class CountSpec<T> {
         static final int DONT_CARE = Integer.MIN_VALUE;
         static final int EQUALS = 1;
@@ -1458,13 +1430,13 @@
             } else {
                 switch (rule) {
                     case EQUALS:
-                        mMessage = event + " + must equal to " + count;
+                        mMessage = event + " must equal to " + count;
                         break;
                     case GREATER_THAN:
-                        mMessage = event + " + must be greater than " + count;
+                        mMessage = event + " must be greater than " + count;
                         break;
                     case LESS_THAN:
-                        mMessage = event + " + must be less than " + count;
+                        mMessage = event + " must be less than " + count;
                         break;
                     default:
                         mMessage = "Don't care";
@@ -1522,7 +1494,7 @@
 
     static void assertSingleLaunch(ComponentName activityName) {
         assertLifecycleCounts(activityName,
-                "***Waiting for activity create, start, and resume",
+                "activity create, start, and resume",
                 1 /* createCount */, 1 /* startCount */, 1 /* resumeCount */,
                 0 /* pauseCount */, 0 /* stopCount */, 0 /* destroyCount */,
                 CountSpec.DONT_CARE /* configChangeCount */);
@@ -1530,7 +1502,7 @@
 
     static void assertSingleLaunchAndStop(ComponentName activityName) {
         assertLifecycleCounts(activityName,
-                "***Waiting for activity create, start, resume, pause, and stop",
+                "activity create, start, resume, pause, and stop",
                 1 /* createCount */, 1 /* startCount */, 1 /* resumeCount */,
                 1 /* pauseCount */, 1 /* stopCount */, 0 /* destroyCount */,
                 CountSpec.DONT_CARE /* configChangeCount */);
@@ -1538,7 +1510,7 @@
 
     static void assertSingleStartAndStop(ComponentName activityName) {
         assertLifecycleCounts(activityName,
-                "***Waiting for activity start, resume, pause, and stop",
+                "activity start, resume, pause, and stop",
                 0 /* createCount */, 1 /* startCount */, 1 /* resumeCount */,
                 1 /* pauseCount */, 1 /* stopCount */, 0 /* destroyCount */,
                 CountSpec.DONT_CARE /* configChangeCount */);
@@ -1546,7 +1518,7 @@
 
     static void assertSingleStart(ComponentName activityName) {
         assertLifecycleCounts(activityName,
-                "***Waiting for activity start and resume",
+                "activity start and resume",
                 0 /* createCount */, 1 /* startCount */, 1 /* resumeCount */,
                 0 /* pauseCount */, 0 /* stopCount */, 0 /* destroyCount */,
                 CountSpec.DONT_CARE /* configChangeCount */);
@@ -1554,20 +1526,12 @@
 
     /** Assert the activity is either relaunched or received configuration changed. */
     static void assertActivityLifecycle(ComponentName activityName, boolean relaunched) {
-        new RetryValidator() {
-
-            @Nullable
-            @Override
-            protected String validate() {
-                final String failedReason = checkActivityIsRelaunchedOrConfigurationChanged(
+        Condition.<String>waitForResult(activityName + " relaunched", condition -> condition
+                .setResultSupplier(() -> checkActivityIsRelaunchedOrConfigurationChanged(
                         getActivityName(activityName),
-                        TestJournalContainer.get(activityName).callbacks, relaunched);
-                if (failedReason != null) {
-                    return failedReason;
-                }
-                return null;
-            }
-        }.assertValidator("***Waiting for valid lifecycle state");
+                        TestJournalContainer.get(activityName).callbacks, relaunched))
+                .setResultValidator(failedReasons -> failedReasons == null)
+                .setOnFailure(failedReasons -> fail(failedReasons)));
     }
 
     /** Assert the activity is either relaunched or received configuration changed. */
@@ -1604,8 +1568,7 @@
 
     static void assertRelaunchOrConfigChanged(ComponentName activityName, int numRelaunch,
             int numConfigChange) {
-        new ActivityLifecycleCounts(activityName).assertCountWithRetry(
-                "***Waiting for relaunch or config changed",
+        new ActivityLifecycleCounts(activityName).assertCountWithRetry("relaunch or config changed",
                 countSpec(ActivityCallback.ON_DESTROY, CountSpec.EQUALS, numRelaunch),
                 countSpec(ActivityCallback.ON_CREATE, CountSpec.EQUALS, numRelaunch),
                 countSpec(ActivityCallback.ON_CONFIGURATION_CHANGED, CountSpec.EQUALS,
@@ -1613,8 +1576,7 @@
     }
 
     static void assertActivityDestroyed(ComponentName activityName) {
-        new ActivityLifecycleCounts(activityName).assertCountWithRetry(
-                "***Waiting for activity destroyed",
+        new ActivityLifecycleCounts(activityName).assertCountWithRetry("activity destroyed",
                 countSpec(ActivityCallback.ON_DESTROY, CountSpec.EQUALS, 1),
                 countSpec(ActivityCallback.ON_CREATE, CountSpec.EQUALS, 0),
                 countSpec(ActivityCallback.ON_CONFIGURATION_CHANGED, CountSpec.EQUALS, 0));
@@ -1626,16 +1588,11 @@
 
     @Nullable
     SizeInfo getLastReportedSizesForActivity(ComponentName activityName) {
-        for (int retry = 1; retry <= 5; retry++) {
-            final ConfigInfo result = TestJournalContainer.get(activityName).lastConfigInfo;
-            if (result != null && result.sizeInfo != null) {
-                return result.sizeInfo;
-            }
-            logAlways("***Waiting for sizes to be reported... retry=" + retry);
-            SystemClock.sleep(1000);
-        }
-        logE("***Waiting for activity size failed: activityName=" + getActivityName(activityName));
-        return null;
+        return Condition.waitForResult("sizes of " + activityName + " to be reported",
+                condition -> condition.setResultSupplier(() -> {
+                    final ConfigInfo info = TestJournalContainer.get(activityName).lastConfigInfo;
+                    return info != null ? info.sizeInfo : null;
+                }).setResultValidator(sizeInfo -> sizeInfo != null));
     }
 
     /** Check if a device has display cutout. */
@@ -1652,7 +1609,7 @@
         mBroadcastActionTrigger.finishBroadcastReceiverActivity();
         mAmWmState.waitForWithAmState(
                 (state) -> !state.containsActivity(BROADCAST_RECEIVER_ACTIVITY),
-                "Waiting for activity to be removed");
+                "activity to be removed");
 
         return displayCutoutPresent;
     }
@@ -1663,37 +1620,26 @@
      */
     @Nullable
     private Boolean getCutoutStateForActivity(ComponentName activityName) {
-        final String logTag = getLogTag(activityName);
-        for (int retry = 1; retry <= 5; retry++) {
-            final Bundle extras = TestJournalContainer.get(activityName).extras;
-            if (extras.containsKey(EXTRA_CUTOUT_EXISTS)) {
-                return extras.getBoolean(EXTRA_CUTOUT_EXISTS);
-            }
-            logAlways("***Waiting for cutout state to be reported... retry=" + retry);
-            SystemClock.sleep(1000);
-        }
-        logE("***Waiting for activity cutout state failed: activityName=" + logTag);
-        return null;
+        return Condition.waitForResult("cutout state to be reported", condition -> condition
+                .setResultSupplier(() -> {
+                    final Bundle extras = TestJournalContainer.get(activityName).extras;
+                    return extras.containsKey(EXTRA_CUTOUT_EXISTS)
+                            ? extras.getBoolean(EXTRA_CUTOUT_EXISTS)
+                            : null;
+                }).setResultValidator(cutoutExists -> cutoutExists != null));
     }
 
     /** Waits for at least one onMultiWindowModeChanged event. */
     ActivityLifecycleCounts waitForOnMultiWindowModeChanged(ComponentName activityName) {
-        int retry = 1;
-        ActivityLifecycleCounts result;
-        do {
-            result = new ActivityLifecycleCounts(activityName);
-            if (result.getCount(ActivityCallback.ON_MULTI_WINDOW_MODE_CHANGED) >= 1) {
-                return result;
-            }
-            logAlways("***waitForOnMultiWindowModeChanged... retry=" + retry);
-            SystemClock.sleep(TimeUnit.SECONDS.toMillis(1));
-        } while (retry++ <= 5);
-        return result;
+        final ActivityLifecycleCounts counts = new ActivityLifecycleCounts(activityName);
+        Condition.waitFor(counts.countWithRetry("waitForOnMultiWindowModeChanged", countSpec(
+                ActivityCallback.ON_MULTI_WINDOW_MODE_CHANGED, CountSpec.GREATER_THAN, 0)));
+        return counts;
     }
 
     static class ActivityLifecycleCounts {
-        final int[] mCounts = new int[ActivityCallback.SIZE];
-        final int[] mLastIndexes = new int[ActivityCallback.SIZE];
+        private final int[] mCounts = new int[ActivityCallback.SIZE];
+        private final int[] mLastIndexes = new int[ActivityCallback.SIZE];
         private ComponentName mActivityName;
 
         ActivityLifecycleCounts(ComponentName componentName) {
@@ -1727,20 +1673,30 @@
         }
 
         @SafeVarargs
+        final Condition<String> countWithRetry(String message,
+                CountSpec<ActivityCallback>... countSpecs) {
+            if (mActivityName == null) {
+                throw new IllegalStateException(
+                        "It is meaningless to retry without specified activity");
+            }
+            return new Condition<String>(message)
+                    .setOnRetry(() -> {
+                        Arrays.fill(mCounts, 0);
+                        Arrays.fill(mLastIndexes, 0);
+                        updateCount(TestJournalContainer.get(mActivityName).callbacks);
+                    })
+                    .setResultSupplier(() -> validateCount(countSpecs))
+                    .setResultValidator(failedReasons -> failedReasons == null);
+        }
+
+        @SafeVarargs
         final void assertCountWithRetry(String message, CountSpec<ActivityCallback>... countSpecs) {
             if (mActivityName == null) {
                 throw new IllegalStateException(
                         "It is meaningless to retry without specified activity");
             }
-            new RetryValidator() {
-                @Override
-                protected String validate() {
-                    Arrays.fill(mCounts, 0);
-                    Arrays.fill(mLastIndexes, 0);
-                    updateCount(TestJournalContainer.get(mActivityName).callbacks);
-                    return validateCount(countSpecs);
-                }
-            }.assertValidator(message);
+            Condition.<String>waitForResult(countWithRetry(message, countSpecs)
+                    .setOnFailure(failedReasons -> fail(message + ": " + failedReasons)));
         }
 
         @SafeVarargs
@@ -1752,7 +1708,7 @@
                     if (failedReasons == null) {
                         failedReasons = new ArrayList<>();
                     }
-                    failedReasons.add(spec.mMessage);
+                    failedReasons.add(spec.mMessage + " (got " + realCount + ")");
                 }
             }
             return failedReasons == null ? null : String.join("\n", failedReasons);
@@ -1778,6 +1734,7 @@
         private boolean mNewTask;
         private boolean mMultipleTask;
         private boolean mAllowMultipleInstances = true;
+        private boolean mLaunchTaskBehind;
         private int mDisplayId = INVALID_DISPLAY;
         private int mActivityType = ACTIVITY_TYPE_UNDEFINED;
         // A proxy activity that launches other activities including mTargetActivityName
@@ -1831,6 +1788,11 @@
             return this;
         }
 
+        public LaunchActivityBuilder setLaunchTaskBehind(boolean launchTaskBehind) {
+            mLaunchTaskBehind = launchTaskBehind;
+            return this;
+        }
+
         public LaunchActivityBuilder setReorderToFront(boolean reorderToFront) {
             mReorderToFront = reorderToFront;
             return this;
@@ -1961,6 +1923,7 @@
             b.putBoolean(KEY_NEW_TASK, mNewTask);
             b.putBoolean(KEY_MULTIPLE_TASK, mMultipleTask);
             b.putBoolean(KEY_MULTIPLE_INSTANCES, mAllowMultipleInstances);
+            b.putBoolean(KEY_LAUNCH_TASK_BEHIND, mLaunchTaskBehind);
             b.putBoolean(KEY_REORDER_TO_FRONT, mReorderToFront);
             b.putInt(KEY_DISPLAY_ID, mDisplayId);
             b.putInt(KEY_ACTIVITY_TYPE, mActivityType);
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/Condition.java b/tests/framework/base/windowmanager/util/src/android/server/wm/Condition.java
new file mode 100644
index 0000000..3680ce3
--- /dev/null
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/Condition.java
@@ -0,0 +1,215 @@
+/*
+ * 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.server.wm;
+
+import static android.server.wm.StateLogger.logAlways;
+import static android.server.wm.StateLogger.logE;
+
+import android.os.SystemClock;
+
+import java.util.concurrent.TimeUnit;
+import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+/**
+ * The utility class to wait a condition with customized options.
+ * The default retry policy is 5 times with interval 1 second.
+ *
+ * @param <T> The type of the object to validate.
+ *
+ * <p>Sample:</p>
+ * <pre>
+ * // Simple case.
+ * if (Condition.waitFor("true value", () -> true)) {
+ *     println("Success");
+ * }
+ * // Wait for customized result with customized validation.
+ * String result = Condition.waitForResult(new Condition<String>("string comparison")
+ *         .setResultSupplier(() -> "Result string")
+ *         .setResultValidator(str -> str.equals("Expected string"))
+ *         .setRetryIntervalMs(500)
+ *         .setRetryLimit(3)
+ *         .setOnFailure(str -> println("Failed on " + str)));
+ * </pre>
+ */
+public class Condition<T> {
+    private final String mMessage;
+
+    // TODO(b/112837428): Implement a incremental retry policy to reduce the unnecessary constant
+    // time, currently keep the default as 5*1s because most of the original code uses it, and some
+    // tests might be sensitive to the waiting interval.
+    private long mRetryIntervalMs = TimeUnit.SECONDS.toMillis(1);
+    private int mRetryLimit = 5;
+    private boolean mReturnLastResult;
+
+    /** It decides whether this condition is satisfied. */
+    private BooleanSupplier mSatisfier;
+    /**
+     * It is used when the condition is not a simple boolean expression, such as the caller may want
+     * to get the validated product as the return value.
+     */
+    private Supplier<T> mResultSupplier;
+    /** It validates the result from {@link #mResultSupplier}. */
+    private Predicate<T> mResultValidator;
+    private Consumer<T> mOnFailure;
+    private Runnable mOnRetry;
+    private T mLastResult;
+    private T mValidatedResult;
+
+    /**
+     * When using this constructor, it is expected that the condition will be configured with
+     * {@link #setResultSupplier} and {@link #setResultValidator}.
+     */
+    public Condition(String message) {
+        this(message, null /* satisfier */);
+    }
+
+    /**
+     * Constructs with a simple boolean condition.
+     *
+     * @param message The message to show what is waiting for.
+     * @param satisfier If it returns true, that means the condition is satisfied.
+     */
+    public Condition(String message, BooleanSupplier satisfier) {
+        mMessage = message;
+        mSatisfier = satisfier;
+    }
+
+    /** Set the supplier which provides the result object to validate. */
+    public Condition<T> setResultSupplier(Supplier<T> supplier) {
+        mResultSupplier = supplier;
+        return this;
+    }
+
+    /** Set the validator which tests the object provided by the supplier. */
+    public Condition<T> setResultValidator(Predicate<T> validator) {
+        mResultValidator = validator;
+        return this;
+    }
+
+    /**
+     * If true, when using {@link #waitForResult(Condition)}, the method will return the last result
+     * provided by {@link #mResultSupplier} even it is not valid (by {@link #mResultValidator}).
+     */
+    public Condition<T> setReturnLastResult(boolean returnLastResult) {
+        mReturnLastResult = returnLastResult;
+        return this;
+    }
+
+    /**
+     * Executes the action when the condition does not satisfy within the time limit. The passed
+     * object to the consumer will be the last result from the supplier.
+     */
+    public Condition<T> setOnFailure(Consumer<T> onFailure) {
+        mOnFailure = onFailure;
+        return this;
+    }
+
+    public Condition<T> setOnRetry(Runnable onRetry) {
+        mOnRetry = onRetry;
+        return this;
+    }
+
+    public Condition<T> setRetryIntervalMs(long millis) {
+        mRetryIntervalMs = millis;
+        return this;
+    }
+
+    public Condition<T> setRetryLimit(int limit) {
+        mRetryLimit = limit;
+        return this;
+    }
+
+    /** Build the condition by {@link #mResultSupplier} and {@link #mResultValidator}. */
+    private void prepareSatisfier() {
+        if (mResultSupplier == null || mResultValidator == null) {
+            throw new IllegalArgumentException("No specified condition");
+        }
+
+        mSatisfier = () -> {
+            final T result = mResultSupplier.get();
+            mLastResult = result;
+            if (mResultValidator.test(result)) {
+                mValidatedResult = result;
+                return true;
+            }
+            return false;
+        };
+    }
+
+    /**
+     * @see #waitFor(Condition)
+     * @see #Condition(String, BooleanSupplier)
+     */
+    public static boolean waitFor(String message, BooleanSupplier satisfier) {
+        return waitFor(new Condition<>(message, satisfier));
+    }
+
+    /** @return {@code false} if the condition does not satisfy within the time limit. */
+    public static <T> boolean waitFor(Condition<T> condition) {
+        if (condition.mSatisfier == null) {
+            condition.prepareSatisfier();
+        }
+
+        final long startTime = SystemClock.elapsedRealtime();
+        for (int i = 1; i <= condition.mRetryLimit; i++) {
+            if (condition.mSatisfier.getAsBoolean()) {
+                return true;
+            } else {
+                SystemClock.sleep(condition.mRetryIntervalMs);
+                logAlways("***Waiting for " + condition.mMessage + " ... retry=" + i
+                        + " elapsed=" + (SystemClock.elapsedRealtime() - startTime) + "ms");
+                if (condition.mOnRetry != null && i < condition.mRetryLimit) {
+                    condition.mOnRetry.run();
+                }
+            }
+        }
+        if (condition.mSatisfier.getAsBoolean()) {
+            return true;
+        }
+
+        if (condition.mOnFailure == null) {
+            logE("Condition is not satisfied: " + condition.mMessage);
+        } else {
+            condition.mOnFailure.accept(condition.mLastResult);
+        }
+        return false;
+    }
+
+    /** @see #waitForResult(Condition) */
+    public static <T> T waitForResult(String message, Consumer<Condition<T>> setup) {
+        final Condition<T> condition = new Condition<>(message);
+        setup.accept(condition);
+        return waitForResult(condition);
+    }
+
+    /**
+     * @return {@code null} if the condition does not satisfy within the time limit or the result
+     *         supplier returns {@code null}.
+     */
+    public static <T> T waitForResult(Condition<T> condition) {
+        condition.mLastResult = condition.mValidatedResult = null;
+        condition.prepareSatisfier();
+        waitFor(condition);
+        if (condition.mValidatedResult != null) {
+            return condition.mValidatedResult;
+        }
+        return condition.mReturnLastResult ? condition.mLastResult : null;
+    }
+}
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
index 0ca2748..2af720d 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
@@ -117,6 +117,7 @@
     private List<Display> mDisplays = new ArrayList();
     private String mFocusedWindow = null;
     private String mFocusedApp = null;
+    private int mFocusedDisplayId = DEFAULT_DISPLAY;
     private String mInputMethodWindowAppToken = null;
     private Rect mDefaultPinnedStackBounds = new Rect();
     private Rect mPinnedStackMovementBounds = new Rect();
@@ -235,6 +236,7 @@
         mDisplayFrozen = state.displayFrozen;
         mRotation = state.rotation;
         mLastOrientation = state.lastOrientation;
+        mFocusedDisplayId = state.focusedDisplayId;
     }
 
     static String appStateToString(int appState) {
@@ -445,6 +447,10 @@
         return mLastOrientation;
     }
 
+    int getFocusedDisplayId() {
+        return mFocusedDisplayId;
+    }
+
     boolean containsStack(int stackId) {
         for (WindowStack stack : mStacks) {
             if (stackId == stack.mStackId) {
@@ -595,6 +601,7 @@
         mPinnedStackMovementBounds.setEmpty();
         mRotation = 0;
         mLastOrientation = 0;
+        mFocusedDisplayId = DEFAULT_DISPLAY;
         mDisplayFrozen = false;
     }
 
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/settings/SettingsSession.java b/tests/framework/base/windowmanager/util/src/android/server/wm/settings/SettingsSession.java
index fa27ee3..e5194c8 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/settings/SettingsSession.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/settings/SettingsSession.java
@@ -80,10 +80,10 @@
     private static final SessionCounters sSessionCounters = new SessionCounters();
 
     protected final Uri mUri;
+    protected final boolean mHasInitialValue;
+    protected final T mInitialValue;
     private final SettingsGetter<T> mGetter;
     private final SettingsSetter<T> mSetter;
-    private final boolean mHasInitialValue;
-    private final T mInitialValue;
 
     public SettingsSession(final Uri uri, final SettingsGetter<T> getter,
             final SettingsSetter<T> setter) {
@@ -152,7 +152,7 @@
         return getter.get(getContentResolver(), uri.getLastPathSegment());
     }
 
-    private static void delete(final Uri uri) {
+    protected static void delete(final Uri uri) {
         final List<String> segments = uri.getPathSegments();
         if (segments.size() != 2) {
             Log.w(TAG, "Unsupported uri for deletion: " + uri, new Throwable());
diff --git a/tests/inputmethod/AndroidTest.xml b/tests/inputmethod/AndroidTest.xml
index f64bd17..ce5e679 100644
--- a/tests/inputmethod/AndroidTest.xml
+++ b/tests/inputmethod/AndroidTest.xml
@@ -20,6 +20,7 @@
     <option name="config-descriptor:metadata" key="component" value="inputmethod" />
     <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
     <!--
         TODO(yukawa): come up with a proper way to take care of devices that do not support
         installable IMEs.  Ideally target_preparer should have an option to annotate required
diff --git a/tests/inputmethod/mockime/Android.bp b/tests/inputmethod/mockime/Android.bp
index 8a9e49c..1b68764 100644
--- a/tests/inputmethod/mockime/Android.bp
+++ b/tests/inputmethod/mockime/Android.bp
@@ -34,7 +34,7 @@
     optimize: {
         enabled: false,
     },
-    sdk_version: "test_current",
+    sdk_version: "current",
     min_sdk_version: "19",
     // tag this module as a cts test artifact
     test_suites: [
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
index 8ad306b..5e653c6 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
@@ -18,7 +18,7 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
-import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
 
 import android.app.UiAutomation;
 import android.content.BroadcastReceiver;
@@ -26,6 +26,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -40,7 +41,6 @@
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputContentInfo;
 import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSystemProperty;
 
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
@@ -48,6 +48,8 @@
 
 import com.android.compatibility.common.util.PollingCheck;
 
+import org.junit.AssumptionViolatedException;
+
 import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
@@ -238,10 +240,10 @@
             @NonNull Context context,
             @NonNull UiAutomation uiAutomation,
             @Nullable ImeSettings.Builder imeSettings) throws Exception {
-        // Currently, MockIme doesn't fully support multi-client IME. Skip tests until it does.
-        // TODO: Re-enable when MockIme supports multi-client IME.
-        assumeFalse("MockIme session doesn't support Multi-Client IME, skip it",
-                InputMethodSystemProperty.MULTI_CLIENT_IME_ENABLED);
+        final String unavailabilityReason = getUnavailabilityReason(context);
+        if (unavailabilityReason != null) {
+            throw new AssumptionViolatedException(unavailabilityReason);
+        }
 
         final MockImeSession client = new MockImeSession(context, uiAutomation);
         client.initialize(imeSettings);
@@ -249,6 +251,19 @@
     }
 
     /**
+     * Checks if the {@link MockIme} can be used in this device.
+     *
+     * @return {@code null} if it can be used, or message describing why if it cannot.
+     */
+    @Nullable
+    public static String getUnavailabilityReason(@NonNull Context context) {
+        if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_INPUT_METHODS)) {
+            return "Device must support installable IMEs that implement InputMethodService API";
+        }
+        return null;
+    }
+
+    /**
      * @return {@link ImeEventStream} object that stores events sent from {@link MockIme} since the
      *         session is created.
      */
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSessionRule.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSessionRule.java
index 041d2b8..3eccac3 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSessionRule.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSessionRule.java
@@ -62,7 +62,12 @@
                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
                     Log.v(TAG, "Creating MockImeSession on " + description.getDisplayName());
                 }
-                mMockImeSession = MockImeSession.create(mContext, mUiAutomation, mImeSettings);
+                final String errorMsg = MockImeSession.getUnavailabilityReason(mContext);
+                if (errorMsg != null) {
+                    Log.w(TAG, "Mock IME not available: " + errorMsg);
+                } else {
+                    mMockImeSession = MockImeSession.create(mContext, mUiAutomation, mImeSettings);
+                }
                 try {
                     base.evaluate();
                 } finally {
diff --git a/tests/leanbackjank/OWNERS b/tests/leanbackjank/OWNERS
new file mode 100644
index 0000000..729b9b6
--- /dev/null
+++ b/tests/leanbackjank/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 188489
+dake@google.com
\ No newline at end of file
diff --git a/tests/perfetto/OWNERS b/tests/perfetto/OWNERS
new file mode 100644
index 0000000..05798d7
--- /dev/null
+++ b/tests/perfetto/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 323270
+primiano@google.com
+lalitm@google.com
diff --git a/tests/rollback/Android.bp b/tests/rollback/Android.bp
index 080a2d5..3b3e617 100644
--- a/tests/rollback/Android.bp
+++ b/tests/rollback/Android.bp
@@ -15,7 +15,7 @@
 android_test {
     name: "CtsRollbackManagerTestCases",
     srcs: ["src/**/*.java"],
-    static_libs: ["androidx.test.rules", "cts-rollback-lib"],
+    static_libs: ["androidx.test.rules", "cts-rollback-lib", "cts-install-lib"],
     test_suites: ["general-tests"],
     sdk_version: "test_current",
 }
diff --git a/tests/rollback/AndroidManifest.xml b/tests/rollback/AndroidManifest.xml
index 5de7f87..3203f25 100644
--- a/tests/rollback/AndroidManifest.xml
+++ b/tests/rollback/AndroidManifest.xml
@@ -19,7 +19,7 @@
           package="com.android.cts.rollback" >
 
     <application>
-        <receiver android:name="com.android.cts.rollback.lib.LocalIntentSender"
+        <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
                   android:exported="true" />
         <uses-library android:name="android.test.runner" />
     </application>
diff --git a/tests/rollback/AndroidTest.xml b/tests/rollback/AndroidTest.xml
index 2fca9d0..534ed1e 100644
--- a/tests/rollback/AndroidTest.xml
+++ b/tests/rollback/AndroidTest.xml
@@ -23,8 +23,8 @@
         <option name="test-file-name" value="CtsRollbackManagerTestCases.apk"/>
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <option name="run-command" value="pm uninstall com.android.cts.rollback.lib.testapp.A" />
-        <option name="teardown-command" value="pm uninstall com.android.cts.rollback.lib.testapp.A" />
+        <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
+        <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
         <option name="package" value="com.android.cts.rollback"/>
diff --git a/tests/rollback/OWNERS b/tests/rollback/OWNERS
new file mode 100644
index 0000000..5a631b6
--- /dev/null
+++ b/tests/rollback/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 557916
+include /hostsidetests/rollback/OWNERS
diff --git a/tests/rollback/src/android/server/cts/rollback/RollbackManagerTest.java b/tests/rollback/src/com/android/cts/rollback/RollbackManagerTest.java
similarity index 70%
rename from tests/rollback/src/android/server/cts/rollback/RollbackManagerTest.java
rename to tests/rollback/src/com/android/cts/rollback/RollbackManagerTest.java
index f7ab15d..2df3912 100644
--- a/tests/rollback/src/android/server/cts/rollback/RollbackManagerTest.java
+++ b/tests/rollback/src/com/android/cts/rollback/RollbackManagerTest.java
@@ -25,10 +25,12 @@
 
 import androidx.test.InstrumentationRegistry;
 
-import com.android.cts.rollback.lib.Install;
+import com.android.cts.install.lib.Install;
+import com.android.cts.install.lib.InstallUtils;
+import com.android.cts.install.lib.TestApp;
+import com.android.cts.install.lib.Uninstall;
 import com.android.cts.rollback.lib.Rollback;
-import com.android.cts.rollback.lib.TestApp;
-import com.android.cts.rollback.lib.Utils;
+import com.android.cts.rollback.lib.RollbackUtils;
 
 import org.junit.After;
 import org.junit.Before;
@@ -56,7 +58,7 @@
                     Manifest.permission.DELETE_PACKAGES,
                     Manifest.permission.TEST_MANAGE_ROLLBACKS);
 
-        Utils.uninstall(TestApp.A);
+        Uninstall.packages(TestApp.A);
     }
 
     /**
@@ -64,7 +66,7 @@
      */
     @After
     public void teardown() throws InterruptedException, IOException {
-        Utils.uninstall(TestApp.A);
+        Uninstall.packages(TestApp.A);
 
         InstrumentationRegistry.getInstrumentation().getUiAutomation()
                 .dropShellPermissionIdentity();
@@ -76,23 +78,24 @@
     @Test
     public void testBasic() throws Exception {
         Install.single(TestApp.A1).commit();
-        assertThat(Utils.getInstalledVersion(TestApp.A)).isEqualTo(1);
-        assertThat(Utils.getAvailableRollback(TestApp.A)).isNull();
-        assertThat(Utils.getCommittedRollback(TestApp.A)).isNull();
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+        assertThat(RollbackUtils.getAvailableRollback(TestApp.A)).isNull();
+        assertThat(RollbackUtils.getCommittedRollback(TestApp.A)).isNull();
 
         Install.single(TestApp.A2).setEnableRollback().commit();
-        assertThat(Utils.getInstalledVersion(TestApp.A)).isEqualTo(2);
-        RollbackInfo available = Utils.getAvailableRollback(TestApp.A);
-        assertThat(available).isNotNull();
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+        InstallUtils.processUserData(TestApp.A);
+        RollbackInfo available = RollbackUtils.waitForAvailableRollback(TestApp.A);
         assertThat(available).isNotStaged();
         assertThat(available).packagesContainsExactly(
                 Rollback.from(TestApp.A2).to(TestApp.A1));
-        assertThat(Utils.getCommittedRollback(TestApp.A)).isNull();
+        assertThat(RollbackUtils.getCommittedRollback(TestApp.A)).isNull();
 
-        Utils.rollback(available.getRollbackId(), TestApp.A2);
-        assertThat(Utils.getInstalledVersion(TestApp.A)).isEqualTo(1);
-        assertThat(Utils.getAvailableRollback(TestApp.A)).isNull();
-        RollbackInfo committed = Utils.getCommittedRollback(TestApp.A);
+        RollbackUtils.rollback(available.getRollbackId(), TestApp.A2);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+        InstallUtils.processUserData(TestApp.A);
+        RollbackUtils.waitForUnavailableRollback(TestApp.A);
+        RollbackInfo committed = RollbackUtils.getCommittedRollback(TestApp.A);
         assertThat(committed).isNotNull();
         assertThat(committed).hasRollbackId(available.getRollbackId());
         assertThat(committed).isNotStaged();
diff --git a/tests/sample/OWNERS b/tests/sample/OWNERS
new file mode 100644
index 0000000..7782fbe
--- /dev/null
+++ b/tests/sample/OWNERS
@@ -0,0 +1,10 @@
+# Bug component: 346961
+diqian@google.com
+fdeng@google.com
+moonk@google.com
+normancheung@google.com
+williamgoh@google.com
+peykov@google.com
+yichunli@google.com
+yimingpan@google.com
+
diff --git a/tests/security/src/android/keystore/cts/KeyGenerationUtils.java b/tests/security/src/android/keystore/cts/KeyGenerationUtils.java
index 27c9a8c..feadc04 100644
--- a/tests/security/src/android/keystore/cts/KeyGenerationUtils.java
+++ b/tests/security/src/android/keystore/cts/KeyGenerationUtils.java
@@ -43,18 +43,28 @@
                 .build();
     }
 
+    private static AttestedKeyPair generateRsaKeyPair(DevicePolicyManager dpm, ComponentName admin,
+            int deviceIdAttestationFlags, String alias) {
+        return  dpm.generateKeyPair(
+                admin, "RSA", buildRsaKeySpecWithKeyAttestation(alias),
+                deviceIdAttestationFlags);
+    }
+
     private static void generateKeyWithIdFlagsExpectingSuccess(DevicePolicyManager dpm,
             ComponentName admin, int deviceIdAttestationFlags) {
         try {
-            AttestedKeyPair generated = dpm.generateKeyPair(
-                    admin, "RSA", buildRsaKeySpecWithKeyAttestation(ALIAS),
-                    deviceIdAttestationFlags);
+            AttestedKeyPair generated =
+                    generateRsaKeyPair(dpm, admin, deviceIdAttestationFlags, ALIAS);
             assertThat(generated).isNotNull();
         } finally {
             assertThat(dpm.removeKeyPair(admin, ALIAS)).isTrue();
         }
     }
 
+    public static void generateRsaKey(DevicePolicyManager dpm, ComponentName admin, String alias) {
+        assertThat(generateRsaKeyPair(dpm, admin, 0, alias)).isNotNull();
+    }
+
     public static void generateKeyWithDeviceIdAttestationExpectingSuccess(DevicePolicyManager dpm,
             ComponentName admin) {
         generateKeyWithIdFlagsExpectingSuccess(dpm, admin, ID_TYPE_SERIAL);
diff --git a/tests/signature/api-check/hidden-api-blacklist-27-api/OWNERS b/tests/signature/api-check/hidden-api-blacklist-27-api/OWNERS
index d658bff..e840a10 100644
--- a/tests/signature/api-check/hidden-api-blacklist-27-api/OWNERS
+++ b/tests/signature/api-check/hidden-api-blacklist-27-api/OWNERS
@@ -1,4 +1,2 @@
-# Bug component: 86431
-dbrazdil@google.com
-mathewi@google.com
-ngeoffray@google.com
+# Bug component: 610774
+include ../hidden-api-blacklist-current-api/OWNERS
\ No newline at end of file
diff --git a/tests/signature/api-check/hidden-api-blacklist-28-api/OWNERS b/tests/signature/api-check/hidden-api-blacklist-28-api/OWNERS
index d658bff..e840a10 100644
--- a/tests/signature/api-check/hidden-api-blacklist-28-api/OWNERS
+++ b/tests/signature/api-check/hidden-api-blacklist-28-api/OWNERS
@@ -1,4 +1,2 @@
-# Bug component: 86431
-dbrazdil@google.com
-mathewi@google.com
-ngeoffray@google.com
+# Bug component: 610774
+include ../hidden-api-blacklist-current-api/OWNERS
\ No newline at end of file
diff --git a/tests/signature/api-check/hidden-api-blacklist-current-api/OWNERS b/tests/signature/api-check/hidden-api-blacklist-current-api/OWNERS
index d658bff..39dbe17 100644
--- a/tests/signature/api-check/hidden-api-blacklist-current-api/OWNERS
+++ b/tests/signature/api-check/hidden-api-blacklist-current-api/OWNERS
@@ -1,4 +1,7 @@
-# Bug component: 86431
-dbrazdil@google.com
+# Bug component: 610774
+platform-compat-eng+reviews@google.com
+andreionea@google.com
+atrost@google.com
 mathewi@google.com
 ngeoffray@google.com
+satayev@google.com
diff --git a/tests/signature/api-check/hidden-api-blacklist-debug-class/OWNERS b/tests/signature/api-check/hidden-api-blacklist-debug-class/OWNERS
index d658bff..e840a10 100644
--- a/tests/signature/api-check/hidden-api-blacklist-debug-class/OWNERS
+++ b/tests/signature/api-check/hidden-api-blacklist-debug-class/OWNERS
@@ -1,4 +1,2 @@
-# Bug component: 86431
-dbrazdil@google.com
-mathewi@google.com
-ngeoffray@google.com
+# Bug component: 610774
+include ../hidden-api-blacklist-current-api/OWNERS
\ No newline at end of file
diff --git a/tests/signature/api-check/hidden-api-killswitch-debug-class/OWNERS b/tests/signature/api-check/hidden-api-killswitch-debug-class/OWNERS
index d658bff..e840a10 100644
--- a/tests/signature/api-check/hidden-api-killswitch-debug-class/OWNERS
+++ b/tests/signature/api-check/hidden-api-killswitch-debug-class/OWNERS
@@ -1,4 +1,2 @@
-# Bug component: 86431
-dbrazdil@google.com
-mathewi@google.com
-ngeoffray@google.com
+# Bug component: 610774
+include ../hidden-api-blacklist-current-api/OWNERS
\ No newline at end of file
diff --git a/tests/signature/api-check/hidden-api-killswitch-whitelist/OWNERS b/tests/signature/api-check/hidden-api-killswitch-whitelist/OWNERS
index d658bff..e840a10 100644
--- a/tests/signature/api-check/hidden-api-killswitch-whitelist/OWNERS
+++ b/tests/signature/api-check/hidden-api-killswitch-whitelist/OWNERS
@@ -1,4 +1,2 @@
-# Bug component: 86431
-dbrazdil@google.com
-mathewi@google.com
-ngeoffray@google.com
+# Bug component: 610774
+include ../hidden-api-blacklist-current-api/OWNERS
\ No newline at end of file
diff --git a/tests/signature/api-check/hidden-api-killswitch-wildcard/OWNERS b/tests/signature/api-check/hidden-api-killswitch-wildcard/OWNERS
index d658bff..e840a10 100644
--- a/tests/signature/api-check/hidden-api-killswitch-wildcard/OWNERS
+++ b/tests/signature/api-check/hidden-api-killswitch-wildcard/OWNERS
@@ -1,4 +1,2 @@
-# Bug component: 86431
-dbrazdil@google.com
-mathewi@google.com
-ngeoffray@google.com
+# Bug component: 610774
+include ../hidden-api-blacklist-current-api/OWNERS
\ No newline at end of file
diff --git a/tests/tests/animation/OWNERS b/tests/tests/animation/OWNERS
new file mode 100644
index 0000000..475f1b8
--- /dev/null
+++ b/tests/tests/animation/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 47085
+tianliu@google.com
+mount@google.com
+andreykulikov@google.com
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
index 5d666d2..7fe1b0a 100644
--- a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
+++ b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
@@ -587,6 +587,20 @@
         mUiDevice.pressBack();
     }
 
+    @AppModeFull(reason = "No usage events access in instant apps")
+    @Test
+    public void testUserUnlockedEventExists() throws Exception {
+        final UsageEvents events = mUsageStatsManager.queryEvents(0, System.currentTimeMillis());
+        while (events.hasNextEvent()) {
+            final Event event = new Event();
+            events.getNextEvent(event);
+            if (event.mEventType == Event.USER_UNLOCKED) {
+                return;
+            }
+        }
+        fail("Couldn't find a user unlocked event.");
+    }
+
     static final int[] INTERACTIVE_EVENTS = new int[] {
             Event.SCREEN_INTERACTIVE,
             Event.SCREEN_NON_INTERACTIVE
diff --git a/tests/tests/appenumeration/Android.bp b/tests/tests/appenumeration/Android.bp
new file mode 100644
index 0000000..5bb2e24
--- /dev/null
+++ b/tests/tests/appenumeration/Android.bp
@@ -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.
+
+android_test {
+    name: "CtsAppEnumerationTestCases",
+    defaults: ["cts_defaults"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    static_libs: [
+        "ctstestrunner-axt",
+        "compatibility-device-util-axt",
+	    "androidx.test.ext.junit",
+    ],
+
+    srcs: ["src/**/*.java"],
+    sdk_version: "test_current",
+}
diff --git a/tests/tests/appenumeration/AndroidManifest.xml b/tests/tests/appenumeration/AndroidManifest.xml
new file mode 100644
index 0000000..7777a9f7
--- /dev/null
+++ b/tests/tests/appenumeration/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?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.appenumeration.cts">
+
+    <application>
+      <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.appenumeration.cts"
+                     android:label="CTS tests for app enumeration">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+</manifest>
+
diff --git a/tests/tests/appenumeration/AndroidTest.xml b/tests/tests/appenumeration/AndroidTest.xml
new file mode 100644
index 0000000..8f6d37b
--- /dev/null
+++ b/tests/tests/appenumeration/AndroidTest.xml
@@ -0,0 +1,36 @@
+<?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 app enumeration CTS test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="framework" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+
+    <!-- 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="CtsAppEnumerationTestCases.apk" />
+        <option name="test-file-name" value="CtsAppEnumerationForceQueryableApp.apk" />
+        <option name="test-file-name" value="CtsAppEnumerationFiltersApp.apk" />
+        <option name="test-file-name" value="CtsAppEnumerationNoApiApp.apk" />
+        <option name="test-file-name" value="CtsAppEnumerationQueriesNothingApp.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.appenumeration.cts" />
+        <option name="runtime-hint" value="12m30s" />
+    </test>
+</configuration>
diff --git a/tests/tests/appenumeration/OWNERS b/tests/tests/appenumeration/OWNERS
new file mode 100644
index 0000000..8a44fb2
--- /dev/null
+++ b/tests/tests/appenumeration/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 36137
+patb@google.com
+toddke@google.com
+chiuwinson@google.com
+rtmitchell@google.com
diff --git a/common/device-side/util/tests/Android.bp b/tests/tests/appenumeration/app/source/Android.bp
similarity index 65%
rename from common/device-side/util/tests/Android.bp
rename to tests/tests/appenumeration/app/source/Android.bp
index 2abba37..8919822 100644
--- a/common/device-side/util/tests/Android.bp
+++ b/tests/tests/appenumeration/app/source/Android.bp
@@ -4,7 +4,7 @@
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
 //
-// http://www.apache.org/licenses/LICENSE-2.0
+//      http://www.apache.org/licenses/LICENSE-2.0
 //
 // Unless required by applicable law or agreed to in writing, software
 // distributed under the License is distributed on an "AS IS" BASIS,
@@ -12,17 +12,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-java_test {
-    name: "compatibility-device-util-tests",
-
+android_test_helper_app {
+    name: "CtsAppEnumerationQueriesNothingApp",
+    manifest: "AndroidManifest-queriesNothing.xml",
+    defaults: ["cts_support_defaults"],
     srcs: ["src/**/*.java"],
-
-    static_libs: [
-        "compatibility-device-util",
-        "junit",
-        "testng", // TODO: remove once Android migrates to JUnit 4.12, which provide assertThrows
-        "truth-prebuilt"
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
     ],
-
     sdk_version: "test_current",
-}
+}
\ No newline at end of file
diff --git a/tests/tests/appenumeration/app/source/AndroidManifest-queriesNothing.xml b/tests/tests/appenumeration/app/source/AndroidManifest-queriesNothing.xml
new file mode 100644
index 0000000..ce56a77
--- /dev/null
+++ b/tests/tests/appenumeration/app/source/AndroidManifest-queriesNothing.xml
@@ -0,0 +1,25 @@
+<?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.appenumeration.queries.nothing">
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="android.appenumeration.cts.query.TestActivity"
+                  android:exported="true" />
+    </application>
+</manifest>
diff --git a/tests/tests/appenumeration/app/source/src/android/appenumeration/cts/query/TestActivity.java b/tests/tests/appenumeration/app/source/src/android/appenumeration/cts/query/TestActivity.java
new file mode 100644
index 0000000..260014d
--- /dev/null
+++ b/tests/tests/appenumeration/app/source/src/android/appenumeration/cts/query/TestActivity.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.appenumeration.cts.query;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+
+public class TestActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        RemoteCallback remoteCallback =
+                getIntent().getParcelableExtra("remoteCallback");
+        Bundle result = new Bundle();
+        try {
+
+            final String action = getIntent().getAction();
+            switch (action) {
+                case "android.appenumeration.cts.action.GET_PACKAGE_INFO":
+                    final String packageName = getIntent().getStringExtra(
+                            Intent.EXTRA_PACKAGE_NAME);
+                    final PackageInfo pi = getPackageManager().getPackageInfo(packageName, 0);
+                    result.putParcelable(Intent.EXTRA_RETURN_RESULT, pi);
+                    break;
+                default:
+                    result.putSerializable("error",
+                            new Exception("unknown action " + action));
+                    break;
+            }
+        } catch (Exception failure) {
+            result.putSerializable("error", failure);
+        } finally {
+            remoteCallback.sendResult(result);
+        }
+        finish();
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/appenumeration/app/target/Android.bp b/tests/tests/appenumeration/app/target/Android.bp
new file mode 100644
index 0000000..e4d494a
--- /dev/null
+++ b/tests/tests/appenumeration/app/target/Android.bp
@@ -0,0 +1,55 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+    name: "CtsAppEnumerationForceQueryableApp",
+    manifest: "AndroidManifest-forceQueryable.xml",
+    defaults: ["cts_support_defaults"],
+    srcs: ["src/**/*.java"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    sdk_version: "test_current",
+}
+
+android_test_helper_app {
+    name: "CtsAppEnumerationFiltersApp",
+    manifest: "AndroidManifest-filters.xml",
+    defaults: ["cts_support_defaults"],
+    srcs: ["src/**/*.java"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    sdk_version: "test_current",
+}
+
+android_test_helper_app {
+    name: "CtsAppEnumerationNoApiApp",
+    manifest: "AndroidManifest-noapi.xml",
+    defaults: ["cts_support_defaults"],
+    srcs: ["src/**/*.java"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    sdk_version: "test_current",
+}
\ No newline at end of file
diff --git a/tests/tests/appenumeration/app/target/AndroidManifest-filters.xml b/tests/tests/appenumeration/app/target/AndroidManifest-filters.xml
new file mode 100644
index 0000000..04aae68
--- /dev/null
+++ b/tests/tests/appenumeration/app/target/AndroidManifest-filters.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.appenumeration.filters">
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="android.appenumeration.testapp.DummyActivity">
+            <intent-filter>
+                <action android:name="android.appenumeration.action.ACTIVITY" />
+            </intent-filter>
+        </activity>
+        <service android:name="android.appenumeration.testapp.DummyService">
+            <intent-filter>
+                <action android:name="android.appenumeration.action.SERVICE" />
+            </intent-filter>
+        </service>
+        <provider android:name="android.appenumeration.testapp.DummyProvider"
+                  android:authorities="android.appenumeration.testapp" />
+        <receiver android:name="android.appenumeration.testapp.DummyReceiver">
+            <intent-filter>
+                <action android:name="android.appenumeration.action.BROADCAST" />
+            </intent-filter>
+        </receiver>
+    </application>
+</manifest>
diff --git a/tests/tests/appenumeration/app/target/AndroidManifest-forceQueryable.xml b/tests/tests/appenumeration/app/target/AndroidManifest-forceQueryable.xml
new file mode 100644
index 0000000..e6535b3
--- /dev/null
+++ b/tests/tests/appenumeration/app/target/AndroidManifest-forceQueryable.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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.appenumeration.forcequeryable">
+    <application android:forceQueryable="true">
+        <uses-library android:name="android.test.runner" />
+    </application>
+</manifest>
diff --git a/tests/tests/appenumeration/app/target/AndroidManifest-noapi.xml b/tests/tests/appenumeration/app/target/AndroidManifest-noapi.xml
new file mode 100644
index 0000000..9b25acc
--- /dev/null
+++ b/tests/tests/appenumeration/app/target/AndroidManifest-noapi.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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.appenumeration.noapi">
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+</manifest>
diff --git a/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java b/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java
new file mode 100644
index 0000000..775b4a8
--- /dev/null
+++ b/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java
@@ -0,0 +1,142 @@
+/*
+ * 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.appenumeration.cts;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.RemoteCallback;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+@RunWith(AndroidJUnit4.class)
+public class AppEnumerationTests {
+
+    private static final String TARGET_NO_API_NAME = "android.appenumeration.noapi";
+    private static final String TARGET_FORCEQUERYABLE_NAME = "android.appenumeration.forcequeryable";
+    private static final String TARGET_FILTERS_NAME = "android.appenumeration.filters";
+
+    private static final String QUERIES_NOTHING_NAME = "android.appenumeration.queries.nothing";
+
+    private Context mContext;
+    private Handler mResponseHandler;
+    private HandlerThread mResponseThread;
+
+    private boolean mGlobalFeatureEnabled;
+
+    @Before
+    public void setup() {
+        mGlobalFeatureEnabled = Boolean.parseBoolean(SystemUtil.runShellCommand(
+                "device_config get package_manager_service package_query_filtering_enabled"));
+        if (!mGlobalFeatureEnabled) return;
+
+        mContext = InstrumentationRegistry.getInstrumentation().getContext();
+        mResponseThread = new HandlerThread("response");
+        mResponseThread.start();
+        mResponseHandler = new Handler(mResponseThread.getLooper());
+
+        enableFeatureForPackage(QUERIES_NOTHING_NAME);
+    }
+
+    private void enableFeatureForPackage(String packageName) {
+        final String response =
+                SystemUtil.runShellCommand("am compat enable 135549675 " + packageName);
+        assertTrue(response.contains("Enabled"));
+    }
+
+    @After
+    public void tearDown() {
+        if (!mGlobalFeatureEnabled) return;
+        mResponseThread.quit();
+    }
+
+    @Test
+    public void queriesNothing_canSeeForceQueryable() throws Exception {
+        if (!mGlobalFeatureEnabled) return;
+        final PackageInfo packageInfo =
+                getPackageInfo(QUERIES_NOTHING_NAME, TARGET_FORCEQUERYABLE_NAME);
+        Assert.assertTrue(packageInfo != null);
+    }
+
+    @Test
+    public void queriesNothing_cannotSeeNoApi() throws Exception {
+        if (!mGlobalFeatureEnabled) return;
+        try {
+            getPackageInfo(QUERIES_NOTHING_NAME, TARGET_NO_API_NAME);
+            fail("App that queries nothing should not see other packages.");
+        } catch (PackageManager.NameNotFoundException e) {
+        }
+    }
+
+    private PackageInfo getPackageInfo(String sourcePackageName, String targetPackageName)
+            throws Exception {
+        Bundle response = sendCommand(sourcePackageName, targetPackageName,
+                "android.appenumeration.cts.action.GET_PACKAGE_INFO");
+        return response.getParcelable(Intent.EXTRA_RETURN_RESULT);
+    }
+
+    private Bundle sendCommand(String sourcePackageName, String targetPackageName, String action)
+            throws Exception {
+        Intent intent = new Intent(action)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                .putExtra(Intent.EXTRA_PACKAGE_NAME, targetPackageName)
+                .setComponent(new ComponentName(
+                        sourcePackageName, "android.appenumeration.cts.query.TestActivity"));
+        final CountDownLatch countDownLatch = new CountDownLatch(1);
+        final AtomicReference<Bundle> resultReference = new AtomicReference<>();
+        RemoteCallback callback = new RemoteCallback(
+                bundle -> {
+                    resultReference.set(bundle);
+                    countDownLatch.countDown();
+                },
+                mResponseHandler);
+        intent.putExtra("remoteCallback", callback);
+        mContext.startActivity(intent);
+        if (!countDownLatch.await(5, TimeUnit.SECONDS)) {
+            throw new TimeoutException("Latch timed out!");
+        }
+        final Bundle bundle = resultReference.get();
+        if (bundle != null && bundle.containsKey("error")) {
+            throw (Exception) Objects.requireNonNull(bundle.getSerializable("error"));
+        }
+        return bundle;
+    }
+
+}
diff --git a/tests/tests/appop/Android.bp b/tests/tests/appop/Android.bp
index e4ca806..301f288 100644
--- a/tests/tests/appop/Android.bp
+++ b/tests/tests/appop/Android.bp
@@ -12,13 +12,36 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+cc_test_library {
+    name: "libCtsAppOpsTestCases_jni",
+
+    stl: "libc++_static",
+    gtest: false,
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+    ],
+
+    srcs: ["jni/**/*.cpp"],
+
+    shared_libs: [
+        "libbinder",
+        "libutils",
+        "liblog",
+    ],
+}
+
 android_test {
     name: "CtsAppOpsTestCases",
-    sdk_version: "test_current",
+
+    compile_multilib: "both",
 
     srcs: ["src/**/*.kt"],
 
     static_libs: [
+        "appops-test-util-lib",
+        "AppOpsUserServiceAidl",
         "androidx.test.rules",
         "compatibility-device-util-axt",
         "androidx.legacy_legacy-support-v4",
@@ -27,9 +50,50 @@
         "androidx.test.uiautomator_uiautomator"
     ],
 
+    jni_libs: [
+        "ld-android",
+        "libartbase",
+        "libbacktrace",
+        "libbase",
+        "libbinder",
+        "libbinderthreadstate",
+        "libbpf",
+        "libbpf_android",
+        "libc++",
+        "libcgrouprc",
+        "libcrypto",
+        "libcutils",
+        "libdexfile",
+        "libdl_android",
+        "libhidl-gen-utils",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "libjsoncpp",
+        "liblog",
+        "liblzma",
+        "libnativehelper",
+        "libnetdbpf",
+        "libnetdutils",
+        "libnetworkstatsfactorytestjni",
+        "libpackagelistparser",
+        "libpcre2",
+        "libprocessgroup",
+        "libselinux",
+        "libtinyxml2",
+        "libui",
+        "libunwindstack",
+        "libutils",
+        "libutilscallstack",
+        "libvndksupport",
+        "libziparchive",
+        "libz",
+        "libCtsAppOpsTestCases_jni",
+    ],
+
     test_suites: [
         "cts",
         "vts",
         "general-tests",
     ],
-}
\ No newline at end of file
+}
diff --git a/tests/tests/appop/AndroidTest.xml b/tests/tests/appop/AndroidTest.xml
index 9f489a9..18e4538 100644
--- a/tests/tests/appop/AndroidTest.xml
+++ b/tests/tests/appop/AndroidTest.xml
@@ -16,10 +16,11 @@
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
     <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
-    <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsAppOpsTestCases.apk" />
+        <option name="test-file-name" value="CtsAppThatUsesAppOps.apk" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="hidden-api-checks" value="true" />
diff --git a/tests/tests/appop/AppThatUsesAppOps/Android.bp b/tests/tests/appop/AppThatUsesAppOps/Android.bp
new file mode 100644
index 0000000..31f4a95
--- /dev/null
+++ b/tests/tests/appop/AppThatUsesAppOps/Android.bp
@@ -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.
+
+cc_test_library  {
+    name: "libAppThatUsesAppOps_jni",
+    sdk_version: "current",
+    stl: "c++_static",
+
+    srcs: [
+        "jni/**/*.cpp"
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter"
+    ],
+
+    shared_libs: [
+        "liblog",
+        "libbinder_ndk"
+    ],
+
+    static_libs: [
+        "AppOpsUserServiceAidlNative-ndk"
+    ]
+}
+
+android_test_helper_app {
+    name: "CtsAppThatUsesAppOps",
+
+    compile_multilib: "both",
+
+    srcs: ["src/**/*.kt"],
+
+    static_libs: [
+        "appops-test-util-lib",
+        "AppOpsUserServiceAidl",
+        "truth-prebuilt",
+        "junit",
+    ],
+
+    jni_libs: [
+        "libAppThatUsesAppOps_jni"
+    ],
+
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ]
+}
\ No newline at end of file
diff --git a/tests/tests/appop/AppThatUsesAppOps/AndroidManifest.xml b/tests/tests/appop/AppThatUsesAppOps/AndroidManifest.xml
new file mode 100644
index 0000000..0d524a9
--- /dev/null
+++ b/tests/tests/appop/AppThatUsesAppOps/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?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.app.appops.cts.appthatusesappops">
+
+  <application>
+      <service android:name=".AppOpsUserService" android:exported="true" />
+  </application>
+
+</manifest>
diff --git a/tests/tests/appop/AppThatUsesAppOps/jni/android/app/appops/cts/appthatusesappops/AppOpsUserService.cpp b/tests/tests/appop/AppThatUsesAppOps/jni/android/app/appops/cts/appthatusesappops/AppOpsUserService.cpp
new file mode 100644
index 0000000..8c97f65
--- /dev/null
+++ b/tests/tests/appop/AppThatUsesAppOps/jni/android/app/appops/cts/appthatusesappops/AppOpsUserService.cpp
@@ -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.
+ */
+
+#include <jni.h>
+#include <android/binder_ibinder_jni.h>
+
+#include "aidl/android/app/appops/cts/IAppOpsUserClient.h"
+
+using ::ndk::SpAIBinder;
+using ::aidl::android::app::appops::cts::IAppOpsUserClient;
+
+// Call binder method IAppOpsUserClient#noteSyncOp from native code
+extern "C" JNIEXPORT void JNICALL
+Java_android_app_appops_cts_appthatusesappops_AppOpsUserServiceKt_noteSyncOpFromNativeCode(
+        JNIEnv* env, jclass clazz, jobject binder) {
+    SpAIBinder native_binder = SpAIBinder(AIBinder_fromJavaBinder(env, binder));
+    std::shared_ptr<IAppOpsUserClient> service = IAppOpsUserClient::fromBinder(native_binder);
+
+    service->noteSyncOp();
+}
diff --git a/tests/tests/appop/AppThatUsesAppOps/src/android/app/appops/cts/appthatusesappops/AppOpsUserService.kt b/tests/tests/appop/AppThatUsesAppOps/src/android/app/appops/cts/appthatusesappops/AppOpsUserService.kt
new file mode 100644
index 0000000..de2729c
--- /dev/null
+++ b/tests/tests/appop/AppThatUsesAppOps/src/android/app/appops/cts/appthatusesappops/AppOpsUserService.kt
@@ -0,0 +1,390 @@
+/*
+ * 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.appops.cts.appthatusesappops
+
+import android.app.AppOpsManager
+import android.app.AppOpsManager.OPSTR_COARSE_LOCATION
+import android.app.AppOpsManager.OPSTR_FINE_LOCATION
+import android.app.AppOpsManager.OPSTR_GET_ACCOUNTS
+import android.app.AsyncNotedAppOp
+import android.app.Service
+import android.app.SyncNotedAppOp
+import android.app.appops.cts.IAppOpsUserClient
+import android.app.appops.cts.IAppOpsUserService
+import android.app.appops.cts.eventually
+import android.content.Intent
+import android.os.IBinder
+import android.os.Process.FIRST_APPLICATION_UID
+import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
+import java.lang.IllegalArgumentException
+
+private external fun noteSyncOpFromNativeCode(binder: IBinder)
+
+class AppOpsUserService : Service() {
+    override fun onCreate() {
+        super.onCreate()
+
+        System.loadLibrary("AppThatUsesAppOps_jni")
+    }
+
+    override fun onBind(intent: Intent?): IBinder? {
+        return object : IAppOpsUserService.Stub() {
+            private val appOpsManager = getSystemService(AppOpsManager::class.java)
+
+            // Collected note-op calls inside of this process
+            private val noted = mutableListOf<Pair<SyncNotedAppOp, Array<StackTraceElement>>>()
+            private val selfNoted = mutableListOf<Pair<SyncNotedAppOp, Array<StackTraceElement>>>()
+            private val asyncNoted = mutableListOf<AsyncNotedAppOp>()
+
+            private fun setNotedAppOpsCollector() {
+                appOpsManager.setNotedAppOpsCollector(
+                        object : AppOpsManager.AppOpsCollector() {
+                            override fun onNoted(op: SyncNotedAppOp) {
+                                noted.add(op to Throwable().stackTrace)
+                            }
+
+                            override fun onSelfNoted(op: SyncNotedAppOp) {
+                                selfNoted.add(op to Throwable().stackTrace)
+                            }
+
+                            override fun onAsyncNoted(asyncOp: AsyncNotedAppOp) {
+                                asyncNoted.add(asyncOp)
+                            }
+                        })
+            }
+
+            init {
+                setNotedAppOpsCollector()
+            }
+
+            /**
+             * Cheapo variant of {@link ParcelableException}
+             */
+            inline fun forwardThrowableFrom(r: () -> Unit) {
+                try {
+                    r()
+                } catch (t: Throwable) {
+                    val sw = StringWriter()
+                    t.printStackTrace(PrintWriter(sw))
+
+                    throw IllegalArgumentException("\n" + sw.toString() + "called by")
+                }
+            }
+
+            override fun disableCollectorAndCallSyncOpsWhichWillNotBeCollected(
+                client: IAppOpsUserClient
+            ) {
+                forwardThrowableFrom {
+                    appOpsManager.setNotedAppOpsCollector(null)
+
+                    client.noteSyncOp()
+
+                    assertThat(asyncNoted).isEmpty()
+
+                    setNotedAppOpsCollector()
+
+                    assertThat(noted).isEmpty()
+                    assertThat(selfNoted).isEmpty()
+                    eventually {
+                        assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
+                    }
+                }
+            }
+
+            override fun disableCollectorAndCallASyncOpsWhichWillBeCollected(
+                client: IAppOpsUserClient
+            ) {
+                forwardThrowableFrom {
+                    appOpsManager.setNotedAppOpsCollector(null)
+
+                    client.noteAsyncOp()
+
+                    setNotedAppOpsCollector()
+
+                    eventually {
+                        assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
+                    }
+                    assertThat(noted).isEmpty()
+                    assertThat(selfNoted).isEmpty()
+                }
+            }
+
+            override fun callApiThatNotesSyncOpAndCheckLog(client: IAppOpsUserClient) {
+                forwardThrowableFrom {
+                    client.noteSyncOp()
+
+                    assertThat(noted.map { it.first.op }).containsExactly(OPSTR_COARSE_LOCATION)
+                    assertThat(noted[0].second.map { it.methodName })
+                        .contains("callApiThatNotesSyncOpAndCheckLog")
+                    assertThat(selfNoted).isEmpty()
+                    assertThat(asyncNoted).isEmpty()
+                }
+            }
+
+            override fun callApiThatNotesSyncOpAndClearLog(client: IAppOpsUserClient) {
+                forwardThrowableFrom {
+                    client.noteSyncOp()
+
+                    assertThat(noted.map { it.first.op }).containsExactly(OPSTR_COARSE_LOCATION)
+                    assertThat(noted[0].second.map { it.methodName })
+                        .contains("callApiThatNotesSyncOpAndClearLog")
+                    assertThat(selfNoted).isEmpty()
+                    assertThat(asyncNoted).isEmpty()
+
+                    noted.clear()
+                }
+            }
+
+            override fun callApiThatCallsBackIntoServiceAndCheckLog(client: IAppOpsUserClient) {
+                forwardThrowableFrom {
+                    // This calls back into the service via callApiThatNotesSyncOpAndClearLog
+                    client.callBackIntoService()
+
+                    // The noteSyncOp called in callApiThatNotesSyncOpAndClearLog should not have
+                    // affected the callBackIntoService call
+                    assertThat(noted.map { it.first.op }).containsExactly(OPSTR_FINE_LOCATION)
+                    assertThat(noted[0].second.map { it.methodName })
+                        .contains("callApiThatCallsBackIntoServiceAndCheckLog")
+                    assertThat(selfNoted).isEmpty()
+                    assertThat(asyncNoted).isEmpty()
+                }
+            }
+
+            override fun callApiThatNotesSyncOpFromNativeCodeAndCheckLog(
+                client: IAppOpsUserClient
+            ) {
+                forwardThrowableFrom {
+                    noteSyncOpFromNativeCode(client.asBinder())
+
+                    eventually {
+                        assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
+                    }
+                    assertThat(noted).isEmpty()
+                    assertThat(selfNoted).isEmpty()
+                }
+            }
+
+            override fun callApiThatNotesSyncOpFromNativeCodeAndCheckMessage(
+                client: IAppOpsUserClient
+            ) {
+                forwardThrowableFrom {
+                    noteSyncOpFromNativeCode(client.asBinder())
+
+                    eventually {
+                        assertThat(asyncNoted[0].notingPackageName).isEqualTo(
+                            "android.app.appops.cts")
+                        assertThat(asyncNoted[0].notingUid).isAtLeast(FIRST_APPLICATION_UID)
+                        assertThat(asyncNoted[0].message).isNotEmpty()
+                    }
+                }
+            }
+
+            override fun callApiThatNotesSyncOpAndCheckStackTrace(client: IAppOpsUserClient) {
+                forwardThrowableFrom {
+                    client.noteSyncOp()
+                    assertThat(noted[0].second.map { it.methodName }).contains(
+                            "callApiThatNotesSyncOpAndCheckStackTrace")
+                }
+            }
+
+            override fun callApiThatNotesNonPermissionSyncOpAndCheckLog(
+                client: IAppOpsUserClient
+            ) {
+                forwardThrowableFrom {
+                    client.noteNonPermissionSyncOp()
+
+                    assertThat(selfNoted).isEmpty()
+                    assertThat(asyncNoted).isEmpty()
+                    assertThat(noted).isEmpty()
+                }
+            }
+
+            override fun callApiThatNotesTwiceSyncOpAndCheckLog(client: IAppOpsUserClient) {
+                forwardThrowableFrom {
+                    client.noteSyncOpTwice()
+
+                    // Ops noted twice are only reported once
+                    assertThat(noted.map { it.first.op }).containsExactly(OPSTR_COARSE_LOCATION)
+                    assertThat(noted[0].second.map { it.methodName })
+                        .contains("callApiThatNotesTwiceSyncOpAndCheckLog")
+                    assertThat(selfNoted).isEmpty()
+                    assertThat(asyncNoted).isEmpty()
+                }
+            }
+
+            override fun callApiThatNotesTwoSyncOpAndCheckLog(client: IAppOpsUserClient) {
+                forwardThrowableFrom {
+                    client.noteTwoSyncOp()
+
+                    assertThat(noted.map { it.first.op }).containsExactly(
+                            OPSTR_COARSE_LOCATION, OPSTR_GET_ACCOUNTS)
+                    assertThat(noted[0].second.map { it.methodName })
+                        .contains("callApiThatNotesTwoSyncOpAndCheckLog")
+                    assertThat(noted[1].second.map { it.methodName })
+                        .contains("callApiThatNotesTwoSyncOpAndCheckLog")
+                    assertThat(selfNoted).isEmpty()
+                    assertThat(asyncNoted).isEmpty()
+                }
+            }
+
+            override fun callApiThatNotesSyncOpNativelyAndCheckLog(client: IAppOpsUserClient) {
+                forwardThrowableFrom {
+                    client.noteSyncOpNative()
+
+                    // All native notes will be reported as async notes
+                    eventually {
+                        assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
+                    }
+                    assertThat(noted).isEmpty()
+                    assertThat(selfNoted).isEmpty()
+                }
+            }
+
+            override fun callApiThatNotesNonPermissionSyncOpNativelyAndCheckLog(
+                client: IAppOpsUserClient
+            ) {
+                forwardThrowableFrom {
+                    client.noteNonPermissionSyncOpNative()
+
+                    // All native notes will be reported as async notes
+                    assertThat(noted).isEmpty()
+                    assertThat(selfNoted).isEmpty()
+                    assertThat(asyncNoted).isEmpty()
+                }
+            }
+
+            override fun callOnewayApiThatNotesSyncOpAndCheckLog(client: IAppOpsUserClient) {
+                forwardThrowableFrom {
+                    client.noteSyncOpOneway()
+
+                    // There is not return value from a one-way call, hence async note is the only
+                    // option
+                    eventually {
+                        assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
+                    }
+                    assertThat(noted).isEmpty()
+                    assertThat(selfNoted).isEmpty()
+                }
+            }
+
+            override fun callOnewayApiThatNotesSyncOpNativelyAndCheckLog(
+                client: IAppOpsUserClient
+            ) {
+                forwardThrowableFrom {
+                    client.noteSyncOpOnewayNative()
+
+                    // There is not return value from a one-way call, hence async note is the only
+                    // option
+                    eventually {
+                        assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
+                    }
+                    assertThat(noted).isEmpty()
+                    assertThat(selfNoted).isEmpty()
+                }
+            }
+
+            override fun callApiThatNotesSyncOpOtherUidAndCheckLog(client: IAppOpsUserClient) {
+                forwardThrowableFrom {
+                    client.noteSyncOpOtherUid()
+
+                    assertThat(noted).isEmpty()
+                    assertThat(selfNoted).isEmpty()
+                    assertThat(asyncNoted).isEmpty()
+                }
+            }
+
+            override fun callApiThatNotesSyncOpOtherUidNativelyAndCheckLog(
+                client: IAppOpsUserClient
+            ) {
+                forwardThrowableFrom {
+                    client.noteSyncOpOtherUidNative()
+
+                    assertThat(noted).isEmpty()
+                    assertThat(selfNoted).isEmpty()
+                    assertThat(asyncNoted).isEmpty()
+                }
+            }
+
+            override fun callApiThatNotesAsyncOpAndCheckLog(client: IAppOpsUserClient) {
+                forwardThrowableFrom {
+                    client.noteAsyncOp()
+
+                    eventually {
+                        assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
+                    }
+                    assertThat(noted).isEmpty()
+                    assertThat(selfNoted).isEmpty()
+                }
+            }
+
+            override fun callApiThatNotesAsyncOpAndCheckDefaultMessage(client: IAppOpsUserClient) {
+                forwardThrowableFrom {
+                    client.noteAsyncOp()
+
+                    eventually {
+                        assertThat(asyncNoted[0].notingPackageName).isEqualTo(
+                                "android.app.appops.cts")
+                        assertThat(asyncNoted[0].notingUid).isAtLeast(FIRST_APPLICATION_UID)
+                        assertThat(asyncNoted[0].message).contains(
+                            "AppOpsLoggingTest\$AppOpsUserClient\$noteAsyncOp")
+                    }
+                }
+            }
+
+            override fun callApiThatNotesAsyncOpAndCheckCustomMessage(client: IAppOpsUserClient) {
+                forwardThrowableFrom {
+                    client.noteAsyncOpWithCustomMessage()
+
+                    eventually {
+                        assertThat(asyncNoted[0].notingPackageName).isEqualTo(
+                                "android.app.appops.cts")
+                        assertThat(asyncNoted[0].notingUid).isAtLeast(FIRST_APPLICATION_UID)
+                        assertThat(asyncNoted[0].message).isEqualTo("custom msg")
+                    }
+                }
+            }
+
+            override fun callApiThatNotesAsyncOpNativelyAndCheckCustomMessage(
+                client: IAppOpsUserClient
+            ) {
+                forwardThrowableFrom {
+                    client.noteAsyncOpNativeWithCustomMessage()
+
+                    eventually {
+                        assertThat(asyncNoted[0].notingPackageName).isNull()
+                        assertThat(asyncNoted[0].notingUid).isAtLeast(FIRST_APPLICATION_UID)
+                        assertThat(asyncNoted[0].message).isEqualTo("native custom msg")
+                    }
+                }
+            }
+
+            override fun callApiThatNotesAsyncOpNativelyAndCheckLog(client: IAppOpsUserClient) {
+                forwardThrowableFrom {
+                    client.noteAsyncOpNative()
+
+                    eventually {
+                        assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
+                    }
+                    assertThat(noted).isEmpty()
+                    assertThat(selfNoted).isEmpty()
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/appop/OWNERS b/tests/tests/appop/OWNERS
new file mode 100644
index 0000000..d21846d
--- /dev/null
+++ b/tests/tests/appop/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 137825
+include ../toast/OWNERS
diff --git a/common/device-side/util/tests/Android.bp b/tests/tests/appop/aidl/Android.bp
similarity index 60%
copy from common/device-side/util/tests/Android.bp
copy to tests/tests/appop/aidl/Android.bp
index 2abba37..ed5b3ff 100644
--- a/common/device-side/util/tests/Android.bp
+++ b/tests/tests/appop/aidl/Android.bp
@@ -1,4 +1,4 @@
-// Copyright (C) 2015 The Android Open Source Project
+// 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.
@@ -12,17 +12,26 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-java_test {
-    name: "compatibility-device-util-tests",
+aidl_interface {
+    name: "AppOpsUserServiceAidlNative",
 
-    srcs: ["src/**/*.java"],
+    local_include_dir: "src",
 
-    static_libs: [
-        "compatibility-device-util",
-        "junit",
-        "testng", // TODO: remove once Android migrates to JUnit 4.12, which provide assertThrows
-        "truth-prebuilt"
+    srcs: [
+        "src/**/*.aidl"
     ],
 
-    sdk_version: "test_current",
+    backend: {
+        java: {
+            sdk_version: "current",
+        }
+    }
 }
+
+java_library {
+    name: "AppOpsUserServiceAidl",
+
+    srcs: [
+        "src/**/*.aidl"
+    ],
+}
\ No newline at end of file
diff --git a/tests/tests/appop/aidl/src/android/app/appops/cts/IAppOpsUserClient.aidl b/tests/tests/appop/aidl/src/android/app/appops/cts/IAppOpsUserClient.aidl
new file mode 100644
index 0000000..76641ea
--- /dev/null
+++ b/tests/tests/appop/aidl/src/android/app/appops/cts/IAppOpsUserClient.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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.appops.cts;
+
+interface IAppOpsUserClient {
+    void noteSyncOp();
+    void callBackIntoService();
+    void noteNonPermissionSyncOp();
+    void noteSyncOpTwice();
+    void noteTwoSyncOp();
+    void noteSyncOpNative();
+    void noteNonPermissionSyncOpNative();
+    oneway void noteSyncOpOneway();
+    oneway void noteSyncOpOnewayNative();
+    void noteSyncOpOtherUid();
+    void noteSyncOpOtherUidNative();
+    void noteAsyncOp();
+    void noteAsyncOpWithCustomMessage();
+    void noteAsyncOpNative();
+    void noteAsyncOpNativeWithCustomMessage();
+}
diff --git a/tests/tests/appop/aidl/src/android/app/appops/cts/IAppOpsUserService.aidl b/tests/tests/appop/aidl/src/android/app/appops/cts/IAppOpsUserService.aidl
new file mode 100644
index 0000000..cddc91e
--- /dev/null
+++ b/tests/tests/appop/aidl/src/android/app/appops/cts/IAppOpsUserService.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.appops.cts;
+
+import android.app.appops.cts.IAppOpsUserClient;
+
+interface IAppOpsUserService {
+    void disableCollectorAndCallSyncOpsWhichWillNotBeCollected(in IAppOpsUserClient client);
+    void disableCollectorAndCallASyncOpsWhichWillBeCollected(in IAppOpsUserClient client);
+    void callApiThatNotesSyncOpAndCheckLog(in IAppOpsUserClient client);
+    void callApiThatNotesSyncOpAndClearLog(in IAppOpsUserClient client);
+    void callApiThatCallsBackIntoServiceAndCheckLog(in IAppOpsUserClient client);
+    void callApiThatNotesSyncOpFromNativeCodeAndCheckLog(in IAppOpsUserClient client);
+    void callApiThatNotesSyncOpFromNativeCodeAndCheckMessage(in IAppOpsUserClient client);
+    void callApiThatNotesSyncOpAndCheckStackTrace(in IAppOpsUserClient client);
+    void callApiThatNotesNonPermissionSyncOpAndCheckLog(in IAppOpsUserClient client);
+    void callApiThatNotesTwiceSyncOpAndCheckLog(in IAppOpsUserClient client);
+    void callApiThatNotesTwoSyncOpAndCheckLog(in IAppOpsUserClient client);
+    void callApiThatNotesSyncOpNativelyAndCheckLog(in IAppOpsUserClient client);
+    void callApiThatNotesNonPermissionSyncOpNativelyAndCheckLog(in IAppOpsUserClient client);
+    void callOnewayApiThatNotesSyncOpAndCheckLog(in IAppOpsUserClient client);
+    void callOnewayApiThatNotesSyncOpNativelyAndCheckLog(in IAppOpsUserClient client);
+    void callApiThatNotesSyncOpOtherUidAndCheckLog(in IAppOpsUserClient client);
+    void callApiThatNotesSyncOpOtherUidNativelyAndCheckLog(in IAppOpsUserClient client);
+    void callApiThatNotesAsyncOpAndCheckLog(in IAppOpsUserClient client);
+    void callApiThatNotesAsyncOpAndCheckDefaultMessage(in IAppOpsUserClient client);
+    void callApiThatNotesAsyncOpAndCheckCustomMessage(in IAppOpsUserClient client);
+    void callApiThatNotesAsyncOpNativelyAndCheckCustomMessage(in IAppOpsUserClient client);
+    void callApiThatNotesAsyncOpNativelyAndCheckLog(in IAppOpsUserClient client);
+}
diff --git a/common/device-side/util/tests/Android.bp b/tests/tests/appop/appopsTestUtilLib/Android.bp
similarity index 61%
copy from common/device-side/util/tests/Android.bp
copy to tests/tests/appop/appopsTestUtilLib/Android.bp
index 2abba37..935909c 100644
--- a/common/device-side/util/tests/Android.bp
+++ b/tests/tests/appop/appopsTestUtilLib/Android.bp
@@ -1,10 +1,10 @@
-// Copyright (C) 2015 The Android Open Source Project
+// 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
+//      http://www.apache.org/licenses/LICENSE-2.0
 //
 // Unless required by applicable law or agreed to in writing, software
 // distributed under the License is distributed on an "AS IS" BASIS,
@@ -12,16 +12,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-java_test {
-    name: "compatibility-device-util-tests",
+java_library {
+    name: "appops-test-util-lib",
 
-    srcs: ["src/**/*.java"],
+    srcs: ["src/**/*.kt"],
 
     static_libs: [
-        "compatibility-device-util",
-        "junit",
-        "testng", // TODO: remove once Android migrates to JUnit 4.12, which provide assertThrows
-        "truth-prebuilt"
+        "androidx.test.rules",
+        "compatibility-device-util-axt",
+        "androidx.legacy_legacy-support-v4",
+        "platform-test-annotations",
     ],
 
     sdk_version: "test_current",
diff --git a/tests/tests/appop/appopsTestUtilLib/src/android/app/appops/cts/AppOpsUtils.kt b/tests/tests/appop/appopsTestUtilLib/src/android/app/appops/cts/AppOpsUtils.kt
new file mode 100644
index 0000000..a62cc00
--- /dev/null
+++ b/tests/tests/appop/appopsTestUtilLib/src/android/app/appops/cts/AppOpsUtils.kt
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2018 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.app.appops.cts
+
+import android.app.AppOpsManager.MODE_ALLOWED
+import android.app.AppOpsManager.MODE_DEFAULT
+import android.app.AppOpsManager.MODE_ERRORED
+import android.app.AppOpsManager.MODE_IGNORED
+import android.util.Log
+import androidx.test.InstrumentationRegistry
+import com.android.compatibility.common.util.SystemUtil
+
+private const val LOG_TAG = "AppOpsUtils"
+private const val TIMEOUT_MILLIS = 10000L
+
+/**
+ * Resets a package's app ops configuration to the device default. See AppOpsManager for the
+ * default op settings.
+ *
+ * <p>
+ * It's recommended to call this in setUp() and tearDown() of your test so the test starts and
+ * ends with a reproducible default state, and so doesn't affect other tests.
+ *
+ * <p>
+ * Some app ops are configured to be non-resettable, which means that the state of these will
+ * not be reset even when calling this method.
+ */
+fun reset(packageName: String): String {
+    return runCommand("appops reset $packageName")
+}
+
+/**
+ * Sets the app op mode (e.g. allowed, denied) for a single package and operation.
+ */
+fun setOpMode(packageName: String, opStr: String, mode: Int): String {
+    val modeStr = when (mode) {
+        MODE_ALLOWED -> "allow"
+        MODE_ERRORED -> "deny"
+        MODE_IGNORED -> "ignore"
+        MODE_DEFAULT -> "default"
+        else -> throw IllegalArgumentException("Unexpected app op type")
+    }
+    val command = "appops set $packageName $opStr $modeStr"
+    return runCommand(command)
+}
+
+/**
+ * Get the app op mode (e.g. MODE_ALLOWED, MODE_DEFAULT) for a single package and operation.
+ */
+fun getOpMode(packageName: String, opStr: String): Int {
+    val opState = getOpState(packageName, opStr)
+    return when {
+        opState.contains(" allow") -> MODE_ALLOWED
+        opState.contains(" deny") -> MODE_ERRORED
+        opState.contains(" ignore") -> MODE_IGNORED
+        opState.contains(" default") -> MODE_DEFAULT
+        else -> throw IllegalStateException("Unexpected app op mode returned $opState")
+    }
+}
+
+/**
+ * Returns whether an allowed operation has been logged by the AppOpsManager for a
+ * package. Operations are noted when the app attempts to perform them and calls e.g.
+ * {@link AppOpsManager#noteOperation}.
+ *
+ * @param opStr The public string constant of the operation (e.g. OPSTR_READ_SMS).
+ */
+fun allowedOperationLogged(packageName: String, opStr: String): Boolean {
+    return getOpState(packageName, opStr).contains(" time=")
+}
+
+/**
+ * Returns whether a rejected operation has been logged by the AppOpsManager for a
+ * package. Operations are noted when the app attempts to perform them and calls e.g.
+ * {@link AppOpsManager#noteOperation}.
+ *
+ * @param opStr The public string constant of the operation (e.g. OPSTR_READ_SMS).
+ */
+fun rejectedOperationLogged(packageName: String, opStr: String): Boolean {
+    return getOpState(packageName, opStr).contains(" rejectTime=")
+}
+
+/**
+ * Runs a lambda adopting Shell's permissions.
+ */
+inline fun <T> runWithShellPermissionIdentity(runnable: () -> T): T {
+    val uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation()
+    uiAutomation.adoptShellPermissionIdentity()
+    try {
+        return runnable.invoke()
+    } finally {
+        uiAutomation.dropShellPermissionIdentity()
+    }
+}
+
+/**
+ * Returns the app op state for a package. Includes information on when the operation
+ * was last attempted to be performed by the package.
+ *
+ * Format: "SEND_SMS: allow; time=+23h12m54s980ms ago; rejectTime=+1h10m23s180ms"
+ */
+private fun getOpState(packageName: String, opStr: String): String {
+    return runCommand("appops get $packageName $opStr")
+}
+
+private fun runCommand(command: String): String {
+    return SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), command)
+}
+/**
+ * Make sure that a lambda eventually finishes without throwing an exception.
+ *
+ * @param r The lambda to run.
+ * @param timeout the maximum time to wait
+ *
+ * @return the return value from the lambda
+ *
+ * @throws NullPointerException If the return value never becomes non-null
+ */
+fun <T> eventually(timeout: Long = TIMEOUT_MILLIS, r: () -> T): T {
+    val start = System.currentTimeMillis()
+
+    while (true) {
+        try {
+            return r()
+        } catch (e: Throwable) {
+            val elapsed = System.currentTimeMillis() - start
+
+            if (elapsed < timeout) {
+                Log.d(LOG_TAG, "Ignoring exception", e)
+
+                Thread.sleep(minOf(100, timeout - elapsed))
+            } else {
+                throw e
+            }
+        }
+    }
+}
diff --git a/tests/tests/appop/jni/android/app/appops/cts/AppOpsLoggingTest.cpp b/tests/tests/appop/jni/android/app/appops/cts/AppOpsLoggingTest.cpp
new file mode 100644
index 0000000..83c2fd5
--- /dev/null
+++ b/tests/tests/appop/jni/android/app/appops/cts/AppOpsLoggingTest.cpp
@@ -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.
+ */
+
+#include <jni.h>
+#include <binder/AppOpsManager.h>
+#include <utils/String16.h>
+
+using namespace android;
+
+#include "android/log.h"
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "AppOpsLoggingTest"
+
+// Note op from native code without supplying a message
+extern "C" JNIEXPORT void JNICALL
+Java_android_app_appops_cts_AppOpsLoggingTestKt_nativeNoteOp(JNIEnv* env, jobject obj,
+        jint op, jint uid, jstring callingPackageName) {
+    AppOpsManager appOpsManager;
+
+    const char *nativeCallingPackageName = env->GetStringUTFChars(callingPackageName, 0);
+
+    appOpsManager.noteOp(op, uid, String16(nativeCallingPackageName));
+
+    env->ReleaseStringUTFChars(callingPackageName, nativeCallingPackageName);
+}
+
+// Note op from native code
+extern "C" JNIEXPORT void JNICALL
+Java_android_app_appops_cts_AppOpsLoggingTestKt_nativeNoteOpWithMessage(JNIEnv* env, jobject obj,
+        jint op, jint uid, jstring callingPackageName, jstring message) {
+    AppOpsManager appOpsManager;
+
+    const char *nativeCallingPackageName = env->GetStringUTFChars(callingPackageName, 0);
+    const char *nativeMessage = env->GetStringUTFChars(message, 0);
+
+    appOpsManager.noteOp(op, uid, String16(nativeCallingPackageName), String16(nativeMessage));
+
+    env->ReleaseStringUTFChars(callingPackageName, nativeCallingPackageName);
+    env->ReleaseStringUTFChars(message, nativeMessage);
+}
diff --git a/tests/tests/appop/src/android/app/appops/cts/AppOpsLoggingTest.kt b/tests/tests/appop/src/android/app/appops/cts/AppOpsLoggingTest.kt
new file mode 100644
index 0000000..985c73e
--- /dev/null
+++ b/tests/tests/appop/src/android/app/appops/cts/AppOpsLoggingTest.kt
@@ -0,0 +1,465 @@
+/*
+ * 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.appops.cts
+
+import android.app.AppOpsManager
+import android.app.AppOpsManager.AppOpsCollector
+import android.app.AppOpsManager.OPSTR_ACCESS_ACCESSIBILITY
+import android.app.AppOpsManager.OPSTR_COARSE_LOCATION
+import android.app.AppOpsManager.OPSTR_FINE_LOCATION
+import android.app.AppOpsManager.OPSTR_GET_ACCOUNTS
+import android.app.AppOpsManager.strOpToOp
+import android.app.AsyncNotedAppOp
+import android.app.SyncNotedAppOp
+import android.content.ComponentName
+import android.content.Context
+import android.content.Context.BIND_AUTO_CREATE
+import android.content.Intent
+import android.content.ServiceConnection
+import android.os.Handler
+import android.os.IBinder
+import android.os.Looper
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Assert.fail
+import org.junit.Before
+import org.junit.Test
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit.MILLISECONDS
+
+private const val TEST_SERVICE_PKG = "android.app.appops.cts.appthatusesappops"
+private const val TIMEOUT_MILLIS = 10000L
+
+private external fun nativeNoteOp(op: Int, uid: Int, packageName: String)
+private external fun nativeNoteOpWithMessage(
+    op: Int,
+    uid: Int,
+    packageName: String,
+    message: String
+)
+
+class AppOpsLoggingTest {
+    private val context = InstrumentationRegistry.getInstrumentation().targetContext
+    private val appOpsManager = context.getSystemService(AppOpsManager::class.java)
+
+    private val myUid = android.os.Process.myUid()
+    private val myPackage = context.packageName
+
+    private lateinit var testService: IAppOpsUserService
+    private lateinit var serviceConnection: ServiceConnection
+
+    // Collected note-op calls inside of this process
+    private val noted = mutableListOf<Pair<SyncNotedAppOp, Array<StackTraceElement>>>()
+    private val selfNoted = mutableListOf<Pair<SyncNotedAppOp, Array<StackTraceElement>>>()
+    private val asyncNoted = mutableListOf<AsyncNotedAppOp>()
+
+    @Before
+    fun loadNativeCode() {
+        System.loadLibrary("CtsAppOpsTestCases_jni")
+    }
+
+    @Before
+    fun setNotedAppOpsCollectorAndClearCollectedNoteOps() {
+        setNotedAppOpsCollector()
+        clearCollectedNotedOps()
+    }
+
+    @Before
+    fun connectToService() {
+        val serviceIntent = Intent()
+        serviceIntent.component = ComponentName(TEST_SERVICE_PKG,
+                TEST_SERVICE_PKG + ".AppOpsUserService")
+
+        val newService = CompletableFuture<IAppOpsUserService>()
+        serviceConnection = object : ServiceConnection {
+            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
+                newService.complete(IAppOpsUserService.Stub.asInterface(service))
+            }
+
+            override fun onServiceDisconnected(name: ComponentName?) {
+                fail("test service disconnected")
+            }
+        }
+
+        context.bindService(serviceIntent, serviceConnection, BIND_AUTO_CREATE)
+        testService = newService.get(TIMEOUT_MILLIS, MILLISECONDS)
+    }
+
+    private fun clearCollectedNotedOps() {
+        noted.clear()
+        selfNoted.clear()
+        asyncNoted.clear()
+    }
+
+    private fun setNotedAppOpsCollector() {
+        appOpsManager.setNotedAppOpsCollector(
+                object : AppOpsCollector() {
+                    override fun onNoted(op: SyncNotedAppOp) {
+                        noted.add(op to Throwable().stackTrace)
+                    }
+
+                    override fun onSelfNoted(op: SyncNotedAppOp) {
+                        selfNoted.add(op to Throwable().stackTrace)
+                    }
+
+                    override fun onAsyncNoted(asyncOp: AsyncNotedAppOp) {
+                        asyncNoted.add(asyncOp)
+                    }
+                })
+    }
+
+    private inline fun rethrowThrowableFrom(r: () -> Unit) {
+        try {
+            r()
+        } catch (e: Throwable) {
+            throw e.cause ?: e
+        }
+    }
+
+    @Test
+    fun selfNoteAndCheckLog() {
+        appOpsManager.noteOpNoThrow(OPSTR_COARSE_LOCATION, myUid, myPackage)
+
+        assertThat(noted).isEmpty()
+        assertThat(asyncNoted).isEmpty()
+
+        assertThat(selfNoted.map { it.first.op }).containsExactly(OPSTR_COARSE_LOCATION)
+    }
+
+    @Test
+    fun nativeSelfNoteAndCheckLog() {
+        nativeNoteOp(strOpToOp(OPSTR_COARSE_LOCATION), myUid, myPackage)
+
+        assertThat(noted).isEmpty()
+        assertThat(selfNoted).isEmpty()
+
+        // All native notes will be reported as async notes
+        eventually {
+            assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
+        }
+    }
+
+    @Test
+    fun selfNotesAreDeliveredAsAsyncOpsWhenCollectorIsRegistered() {
+        appOpsManager.setNotedAppOpsCollector(null)
+
+        appOpsManager.noteOpNoThrow(OPSTR_COARSE_LOCATION, myUid, myPackage)
+
+        assertThat(noted).isEmpty()
+        assertThat(selfNoted).isEmpty()
+        assertThat(asyncNoted).isEmpty()
+
+        setNotedAppOpsCollector()
+
+        assertThat(noted).isEmpty()
+        assertThat(selfNoted).isEmpty()
+        assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
+    }
+
+    @Test
+    fun disableCollectedAndNoteSyncOpAndCheckLog() {
+        rethrowThrowableFrom {
+            testService.disableCollectorAndCallSyncOpsWhichWillNotBeCollected(
+                    AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun disableCollectedAndNoteASyncOpAndCheckLog() {
+        rethrowThrowableFrom {
+            testService.disableCollectorAndCallASyncOpsWhichWillBeCollected(
+                    AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun noteSyncOpAndCheckLog() {
+        rethrowThrowableFrom {
+            testService.callApiThatNotesSyncOpAndCheckLog(AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun callsBackIntoServiceAndCheckLog() {
+        rethrowThrowableFrom {
+            testService.callApiThatCallsBackIntoServiceAndCheckLog(
+                AppOpsUserClient(context, testService))
+        }
+    }
+
+    @Test
+    fun noteSyncOpFromNativeCodeAndCheckLog() {
+        rethrowThrowableFrom {
+            testService.callApiThatNotesSyncOpFromNativeCodeAndCheckLog(
+                    AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun noteSyncOpFromNativeCodeAndCheckMessage() {
+        rethrowThrowableFrom {
+            testService.callApiThatNotesSyncOpFromNativeCodeAndCheckMessage(
+                    AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun noteSyncOpAndCheckStackTrace() {
+        rethrowThrowableFrom {
+            testService.callApiThatNotesSyncOpAndCheckStackTrace(AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun noteNonPermissionSyncOpAndCheckLog() {
+        rethrowThrowableFrom {
+            testService.callApiThatNotesNonPermissionSyncOpAndCheckLog(AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun noteSyncOpTwiceAndCheckLog() {
+        rethrowThrowableFrom {
+            testService.callApiThatNotesTwiceSyncOpAndCheckLog(AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun noteTwoSyncOpAndCheckLog() {
+        rethrowThrowableFrom {
+            testService.callApiThatNotesTwoSyncOpAndCheckLog(AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun noteSyncOpNativeAndCheckLog() {
+        rethrowThrowableFrom {
+            testService.callApiThatNotesSyncOpNativelyAndCheckLog(AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun noteNonPermissionSyncOpNativeAndCheckLog() {
+        rethrowThrowableFrom {
+            testService.callApiThatNotesNonPermissionSyncOpNativelyAndCheckLog(
+                    AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun noteSyncOpOneway() {
+        rethrowThrowableFrom {
+            testService.callOnewayApiThatNotesSyncOpAndCheckLog(AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun noteSyncOpOnewayNative() {
+        rethrowThrowableFrom {
+            testService.callOnewayApiThatNotesSyncOpNativelyAndCheckLog(AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun noteSyncOpOtherUidAndCheckLog() {
+        rethrowThrowableFrom {
+            testService.callApiThatNotesSyncOpOtherUidAndCheckLog(AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun noteSyncOpOtherUidNativeAndCheckLog() {
+        rethrowThrowableFrom {
+            testService.callApiThatNotesSyncOpOtherUidNativelyAndCheckLog(AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun noteAsyncOpAndCheckLog() {
+        rethrowThrowableFrom {
+            testService.callApiThatNotesAsyncOpAndCheckLog(AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun noteAsyncOpAndCheckDefaultMessage() {
+        rethrowThrowableFrom {
+            testService.callApiThatNotesAsyncOpAndCheckDefaultMessage(AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun noteAsyncOpAndCheckCustomMessage() {
+        rethrowThrowableFrom {
+            testService.callApiThatNotesAsyncOpAndCheckCustomMessage(AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun noteAsyncOpNativelyAndCheckCustomMessage() {
+        rethrowThrowableFrom {
+            testService.callApiThatNotesAsyncOpNativelyAndCheckCustomMessage(
+                    AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun noteAsyncOpNativeAndCheckLog() {
+        rethrowThrowableFrom {
+            testService.callApiThatNotesAsyncOpNativelyAndCheckLog(AppOpsUserClient(context))
+        }
+    }
+
+    @After
+    fun removeNotedAppOpsCollector() {
+        appOpsManager.setNotedAppOpsCollector(null)
+    }
+
+    @After
+    fun disconnectFromService() {
+        context.unbindService(serviceConnection)
+    }
+
+    /**
+     * Calls various noteOp-like methods in binder calls called by
+     * {@link android.app.appops.cts.appthatusesappops.AppOpsUserService}
+     */
+    private class AppOpsUserClient(
+        context: Context,
+        val testService: IAppOpsUserService? = null
+    ) : IAppOpsUserClient.Stub() {
+        private val handler = Handler(Looper.getMainLooper())
+        private val appOpsManager = context.getSystemService(AppOpsManager::class.java)
+
+        private val myUid = android.os.Process.myUid()
+        private val myPackage = context.packageName
+
+        override fun noteSyncOp() {
+            runWithShellPermissionIdentity {
+                appOpsManager.noteOpNoThrow(OPSTR_COARSE_LOCATION, getCallingUid(),
+                        TEST_SERVICE_PKG)
+            }
+        }
+
+        override fun callBackIntoService() {
+            runWithShellPermissionIdentity {
+                appOpsManager.noteOpNoThrow(OPSTR_FINE_LOCATION, getCallingUid(),
+                    TEST_SERVICE_PKG)
+            }
+
+            testService?.callApiThatNotesSyncOpAndClearLog(this)
+        }
+
+        override fun noteNonPermissionSyncOp() {
+            runWithShellPermissionIdentity {
+                appOpsManager.noteOpNoThrow(OPSTR_ACCESS_ACCESSIBILITY, getCallingUid(),
+                        TEST_SERVICE_PKG)
+            }
+        }
+
+        override fun noteSyncOpTwice() {
+            noteSyncOp()
+            noteSyncOp()
+        }
+
+        override fun noteTwoSyncOp() {
+            runWithShellPermissionIdentity {
+                appOpsManager.noteOpNoThrow(OPSTR_COARSE_LOCATION, getCallingUid(),
+                        TEST_SERVICE_PKG)
+
+                appOpsManager.noteOpNoThrow(OPSTR_GET_ACCOUNTS, getCallingUid(), TEST_SERVICE_PKG)
+            }
+        }
+
+        override fun noteSyncOpNative() {
+            runWithShellPermissionIdentity {
+                nativeNoteOp(strOpToOp(OPSTR_COARSE_LOCATION), getCallingUid(), TEST_SERVICE_PKG)
+            }
+        }
+
+        override fun noteNonPermissionSyncOpNative() {
+            runWithShellPermissionIdentity {
+                nativeNoteOp(strOpToOp(OPSTR_ACCESS_ACCESSIBILITY), getCallingUid(),
+                        TEST_SERVICE_PKG)
+            }
+        }
+
+        override fun noteSyncOpOneway() {
+            runWithShellPermissionIdentity {
+                appOpsManager.noteOpNoThrow(OPSTR_COARSE_LOCATION, getCallingUid(),
+                        TEST_SERVICE_PKG)
+            }
+        }
+
+        override fun noteSyncOpOnewayNative() {
+            runWithShellPermissionIdentity {
+                nativeNoteOp(strOpToOp(OPSTR_COARSE_LOCATION), getCallingUid(), TEST_SERVICE_PKG)
+            }
+        }
+
+        override fun noteSyncOpOtherUid() {
+            appOpsManager.noteOpNoThrow(OPSTR_COARSE_LOCATION, myUid, myPackage)
+        }
+
+        override fun noteSyncOpOtherUidNative() {
+            nativeNoteOp(strOpToOp(OPSTR_COARSE_LOCATION), myUid, myPackage)
+        }
+
+        override fun noteAsyncOp() {
+            val callingUid = getCallingUid()
+
+            handler.post {
+                runWithShellPermissionIdentity {
+                    appOpsManager.noteOpNoThrow(OPSTR_COARSE_LOCATION, callingUid, TEST_SERVICE_PKG)
+                }
+            }
+        }
+
+        override fun noteAsyncOpWithCustomMessage() {
+            val callingUid = getCallingUid()
+
+            handler.post {
+                runWithShellPermissionIdentity {
+                    appOpsManager.noteOpNoThrow(OPSTR_COARSE_LOCATION, callingUid, TEST_SERVICE_PKG,
+                            "custom msg")
+                }
+            }
+        }
+
+        override fun noteAsyncOpNative() {
+            val callingUid = getCallingUid()
+
+            handler.post {
+                runWithShellPermissionIdentity {
+                    nativeNoteOp(strOpToOp(OPSTR_COARSE_LOCATION), callingUid, TEST_SERVICE_PKG)
+                }
+            }
+        }
+
+        override fun noteAsyncOpNativeWithCustomMessage() {
+            val callingUid = getCallingUid()
+
+            handler.post {
+                runWithShellPermissionIdentity {
+                    nativeNoteOpWithMessage(strOpToOp(OPSTR_COARSE_LOCATION), callingUid,
+                            TEST_SERVICE_PKG, "native custom msg")
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt b/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt
index c358913..ea600d0 100644
--- a/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt
+++ b/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt
@@ -31,12 +31,7 @@
 import android.app.AppOpsManager.OPSTR_RECORD_AUDIO
 import android.app.AppOpsManager.OPSTR_WRITE_CALENDAR
 
-import android.app.appops.cts.AppOpsUtils.Companion.allowedOperationLogged
-import android.app.appops.cts.AppOpsUtils.Companion.rejectedOperationLogged
-import android.app.appops.cts.AppOpsUtils.Companion.setOpMode
-
 import org.mockito.Mockito.mock
-import org.mockito.Mockito.reset
 import org.mockito.Mockito.timeout
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyZeroInteractions
@@ -50,9 +45,9 @@
 import androidx.test.InstrumentationRegistry
 
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.Mockito
 
 import java.util.HashMap
 import java.util.HashSet
@@ -137,7 +132,7 @@
         mOpPackageName = mContext.opPackageName
         assertNotNull(mAppOps)
         // Reset app ops state for this test package to the system default.
-        AppOpsUtils.reset(mOpPackageName)
+        reset(mOpPackageName)
     }
 
     @Test
@@ -261,19 +256,19 @@
             mAppOps.startWatchingMode(OPSTR_WRITE_CALENDAR, mOpPackageName, watcher)
 
             // Make a change to the app op's mode.
-            reset(watcher)
+            Mockito.reset(watcher)
             setOpMode(mOpPackageName, OPSTR_WRITE_CALENDAR, MODE_ERRORED)
             verify(watcher, timeout(MODE_WATCHER_TIMEOUT_MS))
                     .onOpChanged(OPSTR_WRITE_CALENDAR, mOpPackageName)
 
             // Make another change to the app op's mode.
-            reset(watcher)
+            Mockito.reset(watcher)
             setOpMode(mOpPackageName, OPSTR_WRITE_CALENDAR, MODE_ALLOWED)
             verify(watcher, timeout(MODE_WATCHER_TIMEOUT_MS))
                     .onOpChanged(OPSTR_WRITE_CALENDAR, mOpPackageName)
 
             // Set mode to the same value as before - expect no call to the listener.
-            reset(watcher)
+            Mockito.reset(watcher)
             setOpMode(mOpPackageName, OPSTR_WRITE_CALENDAR, MODE_ALLOWED)
             verifyZeroInteractions(watcher)
 
@@ -281,7 +276,7 @@
 
             // Make a change to the app op's mode. Since we already stopped watching the mode, the
             // listener shouldn't be called.
-            reset(watcher)
+            Mockito.reset(watcher)
             setOpMode(mOpPackageName, OPSTR_WRITE_CALENDAR, MODE_ERRORED)
             verifyZeroInteractions(watcher)
         } finally {
@@ -404,6 +399,43 @@
         }
     }
 
+    @Test
+    fun noteOpForBadUid() {
+        runWithShellPermissionIdentity {
+            val mode = mAppOps.noteOpNoThrow(OPSTR_RECORD_AUDIO, Process.myUid() + 1,
+                    mOpPackageName)
+            assertEquals(mode, MODE_ERRORED)
+        }
+    }
+
+    @Test
+    fun startOpForBadUid() {
+        runWithShellPermissionIdentity {
+            val mode = mAppOps.startOpNoThrow(OPSTR_RECORD_AUDIO, Process.myUid() + 1,
+                    mOpPackageName)
+            assertEquals(mode, MODE_ERRORED)
+        }
+    }
+
+    @Test
+    fun checkOpForBadUid() {
+        val defaultMode = AppOpsManager.opToDefaultMode(OPSTR_RECORD_AUDIO)
+
+        runWithShellPermissionIdentity {
+            mAppOps.setUidMode(OPSTR_RECORD_AUDIO, Process.myUid(), MODE_ERRORED)
+            try {
+                val mode = mAppOps.unsafeCheckOpNoThrow(OPSTR_RECORD_AUDIO, Process.myUid() + 1,
+                        mOpPackageName)
+
+                // For invalid uids checkOp return the default mode
+                assertEquals(mode, defaultMode)
+            } finally {
+                // Clear the uid state
+                mAppOps.setUidMode(OPSTR_RECORD_AUDIO, Process.myUid(), defaultMode)
+            }
+        }
+    }
+
     private fun runWithShellPermissionIdentity(command: () -> Unit) {
         val uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation()
         uiAutomation.adoptShellPermissionIdentity()
diff --git a/tests/tests/appop/src/android/app/appops/cts/AppOpsUtils.kt b/tests/tests/appop/src/android/app/appops/cts/AppOpsUtils.kt
deleted file mode 100644
index abdee1e..0000000
--- a/tests/tests/appop/src/android/app/appops/cts/AppOpsUtils.kt
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2018 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.app.appops.cts
-
-import androidx.test.InstrumentationRegistry
-import com.android.compatibility.common.util.SystemUtil
-
-import android.app.AppOpsManager.MODE_ALLOWED
-import android.app.AppOpsManager.MODE_DEFAULT
-import android.app.AppOpsManager.MODE_ERRORED
-import android.app.AppOpsManager.MODE_IGNORED
-import com.android.compatibility.common.util.ThrowingRunnable
-
-/**
- * Utilities for controlling App Ops settings, and testing whether ops are logged.
- */
-class AppOpsUtils {
-    companion object {
-        /**
-         * Resets a package's app ops configuration to the device default. See AppOpsManager for the
-         * default op settings.
-         *
-         * <p>
-         * It's recommended to call this in setUp() and tearDown() of your test so the test starts and
-         * ends with a reproducible default state, and so doesn't affect other tests.
-         *
-         * <p>
-         * Some app ops are configured to be non-resettable, which means that the state of these will
-         * not be reset even when calling this method.
-         */
-        fun reset(packageName: String): String {
-            return runCommand("appops reset $packageName")
-        }
-
-        /**
-         * Sets the app op mode (e.g. allowed, denied) for a single package and operation.
-         */
-        fun setOpMode(packageName: String, opStr: String, mode: Int) : String {
-            val modeStr: String
-            when (mode) {
-                MODE_ALLOWED -> modeStr = "allow"
-                MODE_ERRORED -> modeStr = "deny"
-                MODE_IGNORED -> modeStr = "ignore"
-                MODE_DEFAULT -> modeStr = "default"
-                else -> throw IllegalArgumentException("Unexpected app op type")
-            }
-            val command = "appops set $packageName $opStr $modeStr"
-            return runCommand(command)
-        }
-
-        /**
-         * Get the app op mode (e.g. MODE_ALLOWED, MODE_DEFAULT) for a single package and operation.
-         */
-        fun getOpMode(packageName: String, opStr: String) : Int {
-            val opState = getOpState(packageName, opStr)
-            when {
-                opState.contains(" allow") -> return MODE_ALLOWED
-                opState.contains(" deny") -> return MODE_ERRORED
-                opState.contains(" ignore") -> return MODE_IGNORED
-                opState.contains(" default") -> return MODE_DEFAULT
-                else -> throw IllegalStateException ("Unexpected app op mode returned $opState")
-            }
-        }
-
-        /**
-         * Returns whether an allowed operation has been logged by the AppOpsManager for a
-         * package. Operations are noted when the app attempts to perform them and calls e.g.
-         * {@link AppOpsManager#noteOperation}.
-         *
-         * @param opStr The public string constant of the operation (e.g. OPSTR_READ_SMS).
-         */
-        fun allowedOperationLogged(packageName: String, opStr: String): Boolean {
-            return getOpState(packageName, opStr).contains(" time=")
-        }
-
-        /**
-         * Returns whether a rejected operation has been logged by the AppOpsManager for a
-         * package. Operations are noted when the app attempts to perform them and calls e.g.
-         * {@link AppOpsManager#noteOperation}.
-         *
-         * @param opStr The public string constant of the operation (e.g. OPSTR_READ_SMS).
-         */
-        fun rejectedOperationLogged(packageName: String, opStr: String) : Boolean {
-            return getOpState(packageName, opStr).contains(" rejectTime=")
-        }
-
-        /**
-         * Runs a [ThrowingRunnable] adopting Shell's permissions.
-         */
-        fun runWithShellPermissionIdentity(runnable: ThrowingRunnable) {
-            val uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation()
-            uiAutomation.adoptShellPermissionIdentity()
-            try {
-                runnable.run()
-            } catch (e: Exception) {
-                throw RuntimeException("Caught exception", e)
-            } finally {
-                uiAutomation.dropShellPermissionIdentity()
-            }
-        }
-
-        /**
-         * Returns the app op state for a package. Includes information on when the operation
-         * was last attempted to be performed by the package.
-         *
-         * Format: "SEND_SMS: allow; time=+23h12m54s980ms ago; rejectTime=+1h10m23s180ms"
-         */
-        private fun getOpState(packageName: String, opStr: String) : String {
-            return runCommand("appops get $packageName $opStr")
-        }
-
-        private fun runCommand(command: String ) : String {
-            return SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), command)
-        }
-    }
-}
diff --git a/tests/tests/appop/src/android/app/appops/cts/HistoricalAppopsTest.kt b/tests/tests/appop/src/android/app/appops/cts/HistoricalAppopsTest.kt
index 73a4516..ffb95e6 100644
--- a/tests/tests/appop/src/android/app/appops/cts/HistoricalAppopsTest.kt
+++ b/tests/tests/appop/src/android/app/appops/cts/HistoricalAppopsTest.kt
@@ -19,31 +19,27 @@
 import android.app.AppOpsManager
 import android.app.AppOpsManager.HistoricalOp
 import android.app.AppOpsManager.HistoricalOps
-import android.app.Instrumentation
-import android.content.Context
 import android.os.Process
 import android.os.SystemClock
 import androidx.test.InstrumentationRegistry
-import androidx.test.rule.ActivityTestRule
-import androidx.test.uiautomator.UiDevice
 import androidx.test.runner.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
 import org.junit.Before
-import org.junit.Ignore
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import java.util.ArrayList
 import java.util.concurrent.TimeUnit
 import java.util.concurrent.locks.ReentrantLock
 import java.util.function.Consumer
+import androidx.test.rule.ActivityTestRule
+import androidx.test.uiautomator.UiDevice
+import org.junit.Rule
 
 @RunWith(AndroidJUnit4::class)
 class HistoricalAppopsTest {
     private val uid = Process.myUid()
-    private var appOpsManager: AppOpsManager? = null
-    private var packageName: String? = null
+    private lateinit var appOpsManager: AppOpsManager
+    private lateinit var packageName: String
 
     // Start an activity to make sure this app counts as being in the foreground
     @Rule @JvmField
@@ -51,58 +47,51 @@
 
     @Before
     fun wakeScreenUp() {
-        val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
-        device.wakeUp()
-        device.executeShellCommand("wm dismiss-keyguard")
+        val uiDevice = UiDevice.getInstance(instrumentation)
+        uiDevice.wakeUp()
+        uiDevice.executeShellCommand("wm dismiss-keyguard")
     }
 
     @Before
     fun setUpTest() {
-        appOpsManager = getContext().getSystemService(AppOpsManager::class.java)
-        packageName = getContext().packageName
-        val uiAutomation = getInstrumentation().getUiAutomation()
+        appOpsManager = context.getSystemService(AppOpsManager::class.java)!!
+        packageName = context.packageName!!
         uiAutomation.adoptShellPermissionIdentity()
-        appOpsManager!!.clearHistory()
-        appOpsManager!!.resetHistoryParameters()
+        appOpsManager.clearHistory()
+        appOpsManager.resetHistoryParameters()
     }
 
     @After
     fun tearDownTest() {
-        appOpsManager!!.clearHistory()
-        appOpsManager!!.resetHistoryParameters()
-        val uiAutomation = getInstrumentation().getUiAutomation()
+        appOpsManager.clearHistory()
+        appOpsManager.resetHistoryParameters()
         uiAutomation.dropShellPermissionIdentity()
     }
 
-    @Ignore("Feature is disabled in Android Q")
     @Test
     fun testGetHistoricalPackageOpsForegroundAccessInMemoryBucket() {
         testGetHistoricalPackageOpsForegroundAtDepth(0)
     }
 
-    @Ignore("Feature is disabled in Android Q")
     @Test
     fun testGetHistoricalPackageOpsForegroundAccessFirstOnDiskBucket() {
         testGetHistoricalPackageOpsForegroundAtDepth(1)
     }
 
-    @Ignore("Feature is disabled in Android Q")
     @Test
     fun testHistoricalAggregationOneLevelsDeep() {
         testHistoricalAggregationSomeLevelsDeep(0)
     }
 
-    @Ignore("Feature is disabled in Android Q")
     @Test
     fun testHistoricalAggregationTwoLevelsDeep() {
         testHistoricalAggregationSomeLevelsDeep(1)
     }
 
-    @Ignore("Feature is disabled in Android Q")
     @Test
     fun testHistoricalAggregationOverflow() {
         // Configure historical registry behavior.
-        appOpsManager!!.setHistoryParameters(
+        appOpsManager.setHistoryParameters(
                 AppOpsManager.HISTORICAL_MODE_ENABLED_PASSIVE,
                 SNAPSHOT_INTERVAL_MILLIS,
                 INTERVAL_COMPRESSION_MULTIPLIER)
@@ -111,36 +100,35 @@
         val chunk = createDataChunk()
         val chunkCount = (INTERVAL_COMPRESSION_MULTIPLIER * 2) + 3
         for (i in 0 until chunkCount) {
-            appOpsManager!!.addHistoricalOps(chunk)
+            appOpsManager.addHistoricalOps(chunk)
         }
 
         // Validate the data for the first interval
         val firstIntervalBeginMillis = computeIntervalBeginRawMillis(0)
         val firstIntervalEndMillis = computeIntervalBeginRawMillis(1)
-        val firstOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!,
-                null /*opNames*/, firstIntervalBeginMillis, firstIntervalEndMillis)
+        val firstOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
+                firstIntervalBeginMillis, firstIntervalEndMillis)
         assertHasCounts(firstOps!!, 197)
 
         // Validate the data for the second interval
         val secondIntervalBeginMillis = computeIntervalBeginRawMillis(1)
         val secondIntervalEndMillis = computeIntervalBeginRawMillis(2)
-        val secondOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!,
-                null /*opNames*/, secondIntervalBeginMillis, secondIntervalEndMillis)
+        val secondOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
+                secondIntervalBeginMillis, secondIntervalEndMillis)
         assertHasCounts(secondOps!!, 33)
 
         // Validate the data for both intervals
         val thirdIntervalBeginMillis = firstIntervalEndMillis - SNAPSHOT_INTERVAL_MILLIS
         val thirdIntervalEndMillis = secondIntervalBeginMillis + SNAPSHOT_INTERVAL_MILLIS
-        val thirdOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!,
-                null /*opNames*/, thirdIntervalBeginMillis, thirdIntervalEndMillis)
+        val thirdOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
+                thirdIntervalBeginMillis, thirdIntervalEndMillis)
         assertHasCounts(thirdOps!!, 33)
     }
 
-    @Ignore("Feature is disabled in Android Q")
     @Test
     fun testHistoryTimeTravel() {
         // Configure historical registry behavior.
-        appOpsManager!!.setHistoryParameters(
+        appOpsManager.setHistoryParameters(
                 AppOpsManager.HISTORICAL_MODE_ENABLED_PASSIVE,
                 SNAPSHOT_INTERVAL_MILLIS,
                 INTERVAL_COMPRESSION_MULTIPLIER)
@@ -149,18 +137,18 @@
         val chunk = createDataChunk()
         val chunkCount = computeSlotCount(2) * SNAPSHOT_INTERVAL_MILLIS / chunk.endTimeMillis
         for (i in 0 until chunkCount) {
-            appOpsManager!!.addHistoricalOps(chunk)
+            appOpsManager.addHistoricalOps(chunk)
         }
 
         // Move history in past with the first interval duration
         val firstIntervalDurationMillis = computeIntervalDurationMillis(0)
-        appOpsManager!!.offsetHistory(firstIntervalDurationMillis)
+        appOpsManager.offsetHistory(firstIntervalDurationMillis)
 
         // Validate the data for the first interval
         val firstIntervalBeginMillis = computeIntervalBeginRawMillis(0)
         val firstIntervalEndMillis = firstIntervalBeginMillis + firstIntervalDurationMillis
-        val firstOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!,
-                null /*opNames*/, firstIntervalBeginMillis, firstIntervalEndMillis)
+        val firstOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
+                firstIntervalBeginMillis, firstIntervalEndMillis)
         assertThat(firstOps).isNotNull()
         assertThat(firstOps!!.uidCount).isEqualTo(0)
 
@@ -168,8 +156,8 @@
         val secondIntervalBeginMillis = computeIntervalBeginRawMillis(1)
         val secondIntervalDurationMillis = computeIntervalDurationMillis(1)
         val secondIntervalEndMillis = secondIntervalBeginMillis + secondIntervalDurationMillis
-        val secondOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!,
-                null /*opNames*/, secondIntervalBeginMillis, secondIntervalEndMillis)
+        val secondOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
+                secondIntervalBeginMillis, secondIntervalEndMillis)
         val secondChunkCount = ((computeSlotCount(2) - computeSlotCount(1))
             .times(SNAPSHOT_INTERVAL_MILLIS) / chunk.endTimeMillis)
         assertHasCounts(secondOps!!, 10 * secondChunkCount)
@@ -178,22 +166,22 @@
         val thirdIntervalBeginMillis = computeIntervalBeginRawMillis(2)
         val thirdIntervalDurationMillis = computeIntervalDurationMillis(2)
         val thirdIntervalEndMillis = thirdIntervalBeginMillis + thirdIntervalDurationMillis
-        val thirdOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!,
-                null /*opNames*/, thirdIntervalBeginMillis, thirdIntervalEndMillis)
+        val thirdOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
+                thirdIntervalBeginMillis, thirdIntervalEndMillis)
         val thirdChunkCount = secondChunkCount / INTERVAL_COMPRESSION_MULTIPLIER
         assertHasCounts(thirdOps!!, 10 * thirdChunkCount)
 
         // Move history in future with the first interval duration
-        appOpsManager!!.offsetHistory(- (2.5f * firstIntervalDurationMillis).toLong())
+        appOpsManager.offsetHistory(- (2.5f * firstIntervalDurationMillis).toLong())
 
         // Validate the data for the first interval
-        val fourthOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!,
-                null /*opNames*/, firstIntervalBeginMillis, firstIntervalEndMillis)
+        val fourthOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
+                firstIntervalBeginMillis, firstIntervalEndMillis)
         assertHasCounts(fourthOps!!, 194)
 
         // Validate the data for the second interval
-        val fifthOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!,
-                null /*opNames*/, secondIntervalBeginMillis, secondIntervalEndMillis)
+        val fifthOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
+                secondIntervalBeginMillis, secondIntervalEndMillis)
 
         assertThat(fifthOps).isNotNull()
         assertHasCounts(fifthOps!!, 1703)
@@ -201,7 +189,7 @@
 
     private fun testHistoricalAggregationSomeLevelsDeep(depth: Int) {
         // Configure historical registry behavior.
-        appOpsManager!!.setHistoryParameters(
+        appOpsManager.setHistoryParameters(
                 AppOpsManager.HISTORICAL_MODE_ENABLED_PASSIVE,
                 SNAPSHOT_INTERVAL_MILLIS,
                 INTERVAL_COMPRESSION_MULTIPLIER)
@@ -211,14 +199,14 @@
         val chunkCount = (computeSlotCount(depth + 1)
             .times(SNAPSHOT_INTERVAL_MILLIS) / chunk.endTimeMillis)
         for (i in 0 until chunkCount) {
-            appOpsManager!!.addHistoricalOps(chunk)
+            appOpsManager.addHistoricalOps(chunk)
         }
 
         // Validate the data for the full interval
         val intervalBeginMillis = computeIntervalBeginRawMillis(depth)
         val intervalEndMillis = computeIntervalBeginRawMillis(depth + 1)
-        val ops = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!,
-                null /*opNames*/, intervalBeginMillis, intervalEndMillis)
+        val ops = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
+                intervalBeginMillis, intervalEndMillis)
         val expectedOpCount = ((computeSlotCount(depth + 1) - computeSlotCount(depth))
             .times(SNAPSHOT_INTERVAL_MILLIS) / chunk.endTimeMillis) * 10
         assertHasCounts(ops!!, expectedOpCount)
@@ -226,14 +214,14 @@
 
     private fun testGetHistoricalPackageOpsForegroundAtDepth(depth: Int) {
         // Configure historical registry behavior.
-        appOpsManager!!.setHistoryParameters(
+        appOpsManager.setHistoryParameters(
                 AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE,
                 SNAPSHOT_INTERVAL_MILLIS,
                 INTERVAL_COMPRESSION_MULTIPLIER)
 
-        appOpsManager!!.setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, uid,
+        appOpsManager.setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, uid,
                 AppOpsManager.MODE_ALLOWED)
-        appOpsManager!!.setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, 2000,
+        appOpsManager.setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, 2000,
                 AppOpsManager.MODE_ALLOWED)
 
         activityRule.activity.waitForResumed()
@@ -247,7 +235,7 @@
             // Note ops such that we have data at all levels
             for (d in depth downTo 0) {
                 for (i in 0 until noteCount) {
-                    appOpsManager!!.noteOp(AppOpsManager.OPSTR_START_FOREGROUND, uid, packageName!!)
+                    appOpsManager.noteOp(AppOpsManager.OPSTR_START_FOREGROUND, uid, packageName)
                 }
 
                 if (d > 0) {
@@ -272,7 +260,7 @@
             }
 
             // Get all ops for the package
-            val allOps = getHistoricalOps(appOpsManager!!, uid, packageName!!,
+            val allOps = getHistoricalOps(appOpsManager, uid, packageName,
                     null, beginTimeMillis, endTimeMillis)
 
             assertThat(allOps).isNotNull()
@@ -287,7 +275,7 @@
 
             val packageOps = uidOps.getPackageOpsAt(0)
             assertThat(packageOps).isNotNull()
-            assertThat(packageOps.packageName).isEqualTo(getContext().packageName)
+            assertThat(packageOps.packageName).isEqualTo(packageName)
             assertThat(packageOps.opCount).isEqualTo(1)
 
             val op = packageOps.getOpAt(0)
@@ -337,9 +325,9 @@
             assertThat(getRejectCount(op, AppOpsManager.UID_STATE_BACKGROUND)).isEqualTo(0)
             assertThat(getRejectCount(op, AppOpsManager.UID_STATE_CACHED)).isEqualTo(0)
         } finally {
-            appOpsManager!!.setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, uid,
+            appOpsManager.setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, uid,
                     AppOpsManager.MODE_FOREGROUND)
-            appOpsManager!!.setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, 2000,
+            appOpsManager.setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, 2000,
                     AppOpsManager.MODE_FOREGROUND)
         }
     }
@@ -348,23 +336,28 @@
         val chunk = HistoricalOps(SNAPSHOT_INTERVAL_MILLIS / 4,
                 SNAPSHOT_INTERVAL_MILLIS / 2)
         chunk.increaseAccessCount(AppOpsManager.OP_START_FOREGROUND, uid,
-                packageName!!, AppOpsManager.UID_STATE_TOP, AppOpsManager.OP_FLAG_SELF, 10)
+                packageName, AppOpsManager.UID_STATE_TOP, AppOpsManager.OP_FLAG_SELF, 10)
         chunk.increaseAccessCount(AppOpsManager.OP_START_FOREGROUND, uid,
-                packageName!!, AppOpsManager.UID_STATE_BACKGROUND, AppOpsManager.OP_FLAG_SELF, 10)
+                packageName, AppOpsManager.UID_STATE_BACKGROUND, AppOpsManager.OP_FLAG_SELF, 10)
         chunk.increaseRejectCount(AppOpsManager.OP_START_FOREGROUND, uid,
-                packageName!!, AppOpsManager.UID_STATE_TOP, AppOpsManager.OP_FLAG_SELF, 10)
+                packageName, AppOpsManager.UID_STATE_TOP, AppOpsManager.OP_FLAG_SELF, 10)
         chunk.increaseRejectCount(AppOpsManager.OP_START_FOREGROUND, uid,
-                packageName!!, AppOpsManager.UID_STATE_BACKGROUND, AppOpsManager.OP_FLAG_SELF, 10)
+                packageName, AppOpsManager.UID_STATE_BACKGROUND, AppOpsManager.OP_FLAG_SELF, 10)
         chunk.increaseAccessDuration(AppOpsManager.OP_START_FOREGROUND, uid,
-                packageName!!, AppOpsManager.UID_STATE_TOP, AppOpsManager.OP_FLAG_SELF, 10)
+                packageName, AppOpsManager.UID_STATE_TOP, AppOpsManager.OP_FLAG_SELF, 10)
         chunk.increaseAccessDuration(AppOpsManager.OP_START_FOREGROUND, uid,
-                packageName!!, AppOpsManager.UID_STATE_BACKGROUND, AppOpsManager.OP_FLAG_SELF, 10)
+                packageName, AppOpsManager.UID_STATE_BACKGROUND, AppOpsManager.OP_FLAG_SELF, 10)
         return chunk
     }
 
-    private fun getHistoricalOps(appOpsManager: AppOpsManager, uid: Int,
-            packageName: String, opNames: List<String>?, beginTimeMillis: Long,
-            endTimeMillis: Long): HistoricalOps? {
+    private fun getHistoricalOps(
+        appOpsManager: AppOpsManager,
+        uid: Int,
+        packageName: String,
+        opNames: List<String>?,
+        beginTimeMillis: Long,
+        endTimeMillis: Long
+    ): HistoricalOps? {
         val array = arrayOfNulls<HistoricalOps>(1)
         val lock = ReentrantLock()
         val condition = lock.newCondition()
@@ -374,10 +367,9 @@
                     beginTimeMillis, endTimeMillis)
                     .setUid(uid)
                     .setPackageName(packageName)
-                    .setOpNames(if (opNames != null) ArrayList(opNames) else null)
+                    .setOpNames(opNames?.toList())
                     .build()
-            appOpsManager.getHistoricalOps(request, getContext().getMainExecutor(),
-                    Consumer { ops ->
+            appOpsManager.getHistoricalOps(request, context.mainExecutor, Consumer { ops ->
                 array[0] = ops
                 try {
                     lock.lock()
@@ -426,9 +418,13 @@
         return op.getAccessDuration(uidState, uidState, AppOpsManager.OP_FLAGS_ALL)
     }
 
-    private fun getHistoricalOpsFromDiskRaw(appOpsManager: AppOpsManager, uid: Int,
-            packageName: String, opNames: List<String>?, beginTimeMillis: Long,
-            endTimeMillis: Long): HistoricalOps? {
+    private fun getHistoricalOpsFromDiskRaw(
+        uid: Int,
+        packageName: String,
+        opNames: List<String>?,
+        beginTimeMillis: Long,
+        endTimeMillis: Long
+    ): HistoricalOps? {
         val array = arrayOfNulls<HistoricalOps>(1)
         val lock = ReentrantLock()
         val condition = lock.newCondition()
@@ -438,18 +434,18 @@
                     beginTimeMillis, endTimeMillis)
                     .setUid(uid)
                     .setPackageName(packageName)
-                    .setOpNames(if (opNames != null) ArrayList(opNames) else null)
+                    .setOpNames(opNames?.toList())
                     .build()
-            appOpsManager.getHistoricalOpsFromDiskRaw(request, getContext().getMainExecutor(),
-                Consumer { ops ->
-                  array[0] = ops
-                  try {
-                      lock.lock()
-                      condition.signalAll()
-                  } finally {
-                      lock.unlock()
-                  }
-              })
+            appOpsManager.getHistoricalOpsFromDiskRaw(request, context.mainExecutor,
+                    Consumer { ops ->
+                        array[0] = ops
+                        try {
+                            lock.lock()
+                            condition.signalAll()
+                        } finally {
+                            lock.unlock()
+                        }
+                    })
             condition.await(5, TimeUnit.SECONDS)
             return array[0]
         } finally {
@@ -461,6 +457,10 @@
         const val INTERVAL_COMPRESSION_MULTIPLIER = 10
         const val SNAPSHOT_INTERVAL_MILLIS = 1000L
 
+        val instrumentation get() = InstrumentationRegistry.getInstrumentation()
+        val context get() = instrumentation.context
+        val uiAutomation get() = instrumentation.uiAutomation
+
         private fun computeIntervalDurationMillis(depth: Int): Long {
             return Math.pow(INTERVAL_COMPRESSION_MULTIPLIER.toDouble(),
                     (depth + 1).toDouble()).toLong() * SNAPSHOT_INTERVAL_MILLIS
@@ -482,13 +482,5 @@
             }
             return beginTimeMillis * SNAPSHOT_INTERVAL_MILLIS
         }
-
-        private fun getInstrumentation(): Instrumentation {
-            return InstrumentationRegistry.getInstrumentation()
-        }
-
-        private fun getContext(): Context {
-            return getInstrumentation().context
-        }
     }
 }
diff --git a/tests/tests/assist/Android.bp b/tests/tests/assist/Android.bp
index 632c19e..83be674 100644
--- a/tests/tests/assist/Android.bp
+++ b/tests/tests/assist/Android.bp
@@ -25,11 +25,9 @@
         "CtsAssistCommon",
         "ctstestrunner-axt",
         "compatibility-device-util-axt",
+	"androidx.test.ext.junit",
     ],
-    libs: [
-        "android.test.runner.stubs",
-        "android.test.base.stubs",
-    ],
+
     srcs: ["src/**/*.java"],
     sdk_version: "test_current",
 }
diff --git a/tests/tests/assist/AndroidTest.xml b/tests/tests/assist/AndroidTest.xml
index 3c08038..c8014e6 100644
--- a/tests/tests/assist/AndroidTest.xml
+++ b/tests/tests/assist/AndroidTest.xml
@@ -30,9 +30,6 @@
         <option name="test-file-name" value="CtsAssistApp.apk" />
         <option name="test-file-name" value="CtsAssistTestCases.apk" />
     </target_preparer>
-    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <option name="run-command" value="settings put secure voice_interaction_service android.assist.service/.MainInteractionService" />
-    </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.assist.cts" />
         <option name="runtime-hint" value="12m30s" />
diff --git a/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java b/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
index 0506bc9..69150a4 100755
--- a/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
+++ b/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
@@ -16,12 +16,17 @@
 
 package android.assist.cts;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.fail;
+
 import android.assist.common.AutoResetLatch;
 import android.assist.common.Utils;
 import android.util.Log;
 
-import java.util.concurrent.TimeUnit;
+import org.junit.Test;
 
+import java.util.concurrent.TimeUnit;
 
 /**
  *  Test that the AssistStructure returned is properly formatted.
@@ -34,12 +39,12 @@
     private AutoResetLatch mHasDrawedLatch;
 
     @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+    protected void customSetup() throws Exception {
         mHasDrawedLatch = new AutoResetLatch(1);
         mActionLatchReceiver = new ActionLatchReceiver(Utils.APP_3P_HASDRAWED, mHasDrawedLatch);
         startTestActivity(TEST_CASE_TYPE);
     }
+
     private void waitForOnDraw() throws Exception {
         Log.i(TAG, "waiting for onDraw() before continuing");
         if (!mHasDrawedLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
@@ -47,6 +52,7 @@
         }
     }
 
+    @Test
     public void testAssistStructure() throws Throwable {
         if (!mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS)) {
             Log.d(TAG, "Not running assist tests - voice_recognizers feature is not supported");
@@ -59,9 +65,10 @@
         final AutoResetLatch latch = startSession();
         waitForContext(latch);
         getInstrumentation().waitForIdleSync();
-        runTestOnUiThread(() -> {
+        getInstrumentation().runOnMainSync(() -> {
             verifyAssistDataNullness(false, false, false, false);
-            verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE), false /*FLAG_SECURE set*/);
+            verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE),
+                    false /*FLAG_SECURE set*/);
         });
     }
 }
diff --git a/tests/tests/assist/src/android/assist/cts/AssistTestBase.java b/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
index 9971f74..967101f 100644
--- a/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
+++ b/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
@@ -16,8 +16,15 @@
 
 package android.assist.cts;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
 
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.fail;
+
 import android.app.ActivityManager;
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
@@ -34,7 +41,6 @@
 import android.os.LocaleList;
 import android.os.RemoteCallback;
 import android.provider.Settings;
-import android.test.ActivityInstrumentationTestCase2;
 import android.util.Log;
 import android.util.Pair;
 import android.view.Display;
@@ -45,19 +51,32 @@
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.rule.ActivityTestRule;
 
+import com.android.compatibility.common.util.SettingsStateChangerRule;
+import com.android.compatibility.common.util.SettingsStateManager;
 import com.android.compatibility.common.util.SettingsUtils;
+import com.android.compatibility.common.util.StateKeeperRule;
 import com.android.compatibility.common.util.ThrowingRunnable;
 import com.android.compatibility.common.util.Timeout;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
-import javax.annotation.Nullable;
-
-public class AssistTestBase extends ActivityInstrumentationTestCase2<TestStartActivity> {
+@RunWith(AndroidJUnit4.class)
+abstract class AssistTestBase {
     private static final String TAG = "AssistTestBase";
 
     protected static final String FEATURE_VOICE_RECOGNIZERS = "android.software.voice_recognizers";
@@ -66,10 +85,6 @@
     private static final String ASSIST_STRUCTURE_ENABLED = "assist_structure_enabled";
     private static final String ASSIST_SCREENSHOT_ENABLED = "assist_screenshot_enabled";
 
-    // TODO: once tests are migrated to JUnit 4, use a @BeforeClass method or StateChangerRule
-    // to avoid this hack
-    private static boolean mFirstTest = true;
-
     private static final Timeout TIMEOUT = new Timeout(
             "AssistTestBaseTimeout",
             10000,
@@ -79,6 +94,30 @@
 
     private static final long SLEEP_BEFORE_RETRY_MS = 250L;
 
+    private static final Context sContext = getInstrumentation().getTargetContext();
+
+    private static final SettingsStateManager sStructureEnabledMgr = new SettingsStateManager(
+            sContext, SettingsUtils.NAMESPACE_SECURE, ASSIST_STRUCTURE_ENABLED);
+    private static final SettingsStateManager sScreenshotEnabledMgr = new SettingsStateManager(
+            sContext, SettingsUtils.NAMESPACE_SECURE, ASSIST_SCREENSHOT_ENABLED);
+
+    private final SettingsStateChangerRule mServiceSetterRule = new SettingsStateChangerRule(
+            sContext, Settings.Secure.VOICE_INTERACTION_SERVICE,
+            "android.assist.service/.MainInteractionService");
+    private final StateKeeperRule<String> mStructureEnabledKeeperRule = new StateKeeperRule<>(
+            sStructureEnabledMgr);
+    private final StateKeeperRule<String> mScreenshotEnabledKeeperRule = new StateKeeperRule<>(
+            sScreenshotEnabledMgr);
+    private final ActivityTestRule<TestStartActivity> mActivityTestRule =
+            new ActivityTestRule<>(TestStartActivity.class, false, false);
+
+    @Rule
+    public final RuleChain mLookAllTheseRules = RuleChain
+            .outerRule(mServiceSetterRule)
+            .around(mStructureEnabledKeeperRule)
+            .around(mScreenshotEnabledKeeperRule)
+            .around(mActivityTestRule);
+
     protected ActivityManager mActivityManager;
     private TestStartActivity mTestActivity;
     protected AssistContent mAssistContent;
@@ -108,20 +147,15 @@
     private String mTestName;
     private View mView;
 
-    public AssistTestBase() {
-        super(TestStartActivity.class);
+    @BeforeClass
+    public static void setFeatures() {
+        setFeaturesEnabled(StructureEnabled.TRUE, ScreenshotEnabled.TRUE);
+        logContextAndScreenshotSetting();
     }
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mContext = getInstrumentation().getTargetContext();
-
-        if (mFirstTest) {
-            setFeaturesEnabled(StructureEnabled.TRUE, ScreenshotEnabled.TRUE);
-            logContextAndScreenshotSetting();
-            mFirstTest = false;
-        }
+    @Before
+    public final void setUp() throws Exception {
+        mContext = sContext;
 
         // reset old values
         mScreenshotMatches = false;
@@ -134,10 +168,19 @@
 
         prepareDevice();
         registerForAsyncReceivingCallback();
+
+        customSetup();
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    /**
+     * Test-specific setup - doesn't need to call {@code super} neither use <code>@Before</code>.
+     */
+    protected void customSetup() throws Exception {
+    }
+
+    @After
+    public final void tearDown() throws Exception {
+        customTearDown();
         mTestActivity.finish();
         mContext.sendBroadcast(new Intent(Utils.HIDE_SESSION));
 
@@ -146,10 +189,15 @@
             m3pActivityCallback.sendResult(Utils.bundleOfRemoteAction(Utils.ACTION_END_OF_TEST));
         }
 
-        super.tearDown();
         mSessionCompletedLatch.await(3, TimeUnit.SECONDS);
     }
 
+    /**
+     * Test-specific teardown - doesn't need to call {@code super} neither use <code>@After</code>.
+     */
+    protected void customTearDown() throws Exception {
+    }
+
     private void prepareDevice() throws Exception {
         Log.d(TAG, "prepareDevice()");
 
@@ -213,8 +261,7 @@
         intent.setAction("android.intent.action.TEST_START_ACTIVITY_" + testName);
         intent.putExtra(Utils.TESTCASE_TYPE, testName);
         intent.putExtra(Utils.EXTRA_REMOTE_CALLBACK, mRemoteCallback);
-        setActivityIntent(intent);
-        mTestActivity = getActivity();
+        mTestActivity = mActivityTestRule.launchActivity(intent);
         mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
     }
 
@@ -339,23 +386,21 @@
      */
     protected void verifyAssistStructure(ComponentName backgroundApp, boolean isSecureWindow) {
         // Check component name matches
-        assertEquals(backgroundApp.flattenToString(),
-                mAssistStructure.getActivityComponent().flattenToString());
+        assertThat(mAssistStructure.getActivityComponent().flattenToString())
+                .isEqualTo(backgroundApp.flattenToString());
         long acquisitionStart = mAssistStructure.getAcquisitionStartTime();
         long acquisitionEnd = mAssistStructure.getAcquisitionEndTime();
-        assertTrue(acquisitionStart > 0);
-        assertTrue(acquisitionEnd > 0);
-        assertTrue(acquisitionEnd >= acquisitionStart);
+        assertThat(acquisitionStart).isGreaterThan(0L);
+        assertThat(acquisitionEnd).isGreaterThan(0L);
+        assertThat(acquisitionEnd).isAtLeast(acquisitionStart);
         Log.i(TAG, "Traversing down structure for: " + backgroundApp.flattenToString());
         mView = mTestActivity.findViewById(android.R.id.content).getRootView();
         verifyHierarchy(mAssistStructure, isSecureWindow);
     }
 
-    protected void logContextAndScreenshotSetting() {
-        Log.i(TAG, "Context is: " + Settings.Secure.getString(
-                mContext.getContentResolver(), "assist_structure_enabled"));
-        Log.i(TAG, "Screenshot is: " + Settings.Secure.getString(
-                mContext.getContentResolver(), "assist_screenshot_enabled"));
+    protected static void logContextAndScreenshotSetting() {
+        Log.i(TAG, "Context is: " + sStructureEnabledMgr.get());
+        Log.i(TAG, "Screenshot is: " + sScreenshotEnabledMgr.get());
     }
 
     /**
@@ -366,7 +411,7 @@
 
         int numWindows = structure.getWindowNodeCount();
         // TODO: multiple windows?
-        assertEquals("Number of windows don't match", 1, numWindows);
+        assertWithMessage("Number of windows don't match").that(numWindows).isEqualTo(1);
         int[] appLocationOnScreen = new int[2];
         mView.getLocationOnScreen(appLocationOnScreen);
 
@@ -375,10 +420,10 @@
             Log.i(TAG, "Title: " + windowNode.getTitle());
             // Verify top level window bounds are as big as the app and pinned to its top-left
             // corner.
-            assertEquals("Window left position wrong: was " + windowNode.getLeft(),
-                    windowNode.getLeft(), appLocationOnScreen[0]);
-            assertEquals("Window top position wrong: was " + windowNode.getTop(),
-                    windowNode.getTop(), appLocationOnScreen[1]);
+            assertWithMessage("Window left position wrong: was %s", windowNode.getLeft())
+                    .that(appLocationOnScreen[0]).isEqualTo(windowNode.getLeft());
+            assertWithMessage("Window top position wrong: was %s", windowNode.getTop())
+                    .that(appLocationOnScreen[1]).isEqualTo(windowNode.getTop());
             traverseViewAndStructure(
                     mView,
                     windowNode.getRootViewNode(),
@@ -421,7 +466,7 @@
         }
         Log.i(TAG, "Node ID: " + parentNode.getIdEntry());
 
-        assertEquals("IDs do not match", parentViewId, parentNode.getIdEntry());
+        assertWithMessage("IDs do not match").that(parentNode.getIdEntry()).isEqualTo(parentViewId);
 
         int numViewChildren = 0;
         int numNodeChildren = 0;
@@ -431,22 +476,26 @@
         numNodeChildren = parentNode.getChildCount();
 
         if (isSecureWindow) {
-            assertTrue("ViewNode property isAssistBlocked is false", parentNode.isAssistBlocked());
-            assertEquals("Secure window should only traverse root node.", 0, numNodeChildren);
+            assertWithMessage("ViewNode property isAssistBlocked is false")
+                    .that(parentNode.isAssistBlocked()).isTrue();
+            assertWithMessage("Secure window should only traverse root node")
+                    .that(numNodeChildren).isEqualTo(0);
             isSecureWindow = false;
         } else if (parentNode.getClassName().equals("android.webkit.WebView")) {
             // WebView will also appear to have no children while the node does, traverse node
-            assertTrue("AssistStructure returned a WebView where the view wasn't one",
-                    parentView instanceof WebView);
+            assertWithMessage("AssistStructure returned a WebView where the view wasn't one").that(
+                    parentView instanceof WebView).isTrue();
 
             boolean textInWebView = false;
 
             for (int i = numNodeChildren - 1; i >= 0; i--) {
                textInWebView |= traverseWebViewForText(parentNode.getChildAt(i));
             }
-            assertTrue("Did not find expected strings inside WebView", textInWebView);
+            assertWithMessage("Did not find expected strings inside WebView").that(textInWebView)
+                    .isTrue();
         } else {
-            assertEquals("Number of children did not match.", numViewChildren, numNodeChildren);
+            assertWithMessage("Number of children did not match").that(numNodeChildren)
+                    .isEqualTo(numViewChildren);
 
             verifyViewProperties(parentView, parentNode);
 
@@ -459,7 +508,7 @@
                     ViewNode childNode = parentNode.getChildAt(i);
 
                     // if isSecureWindow, should not have reached this point.
-                    assertFalse(isSecureWindow);
+                    assertThat(isSecureWindow).isFalse();
                     traverseViewAndStructure(childView, childNode, isSecureWindow);
                 }
             }
@@ -485,18 +534,18 @@
      * Return true if the expected domain is found in the WebView, else fail.
      */
     protected void verifyAssistStructureHasWebDomain(String domain) {
-        assertTrue(traverse(mAssistStructure.getWindowNodeAt(0).getRootViewNode(), (n) -> {
+        assertThat(traverse(mAssistStructure.getWindowNodeAt(0).getRootViewNode(), (n) -> {
             return n.getWebDomain() != null && domain.equals(n.getWebDomain());
-        }));
+        })).isTrue();
     }
 
     /**
      * Return true if the expected LocaleList is found in the WebView, else fail.
      */
     protected void verifyAssistStructureHasLocaleList(LocaleList localeList) {
-        assertTrue(traverse(mAssistStructure.getWindowNodeAt(0).getRootViewNode(), (n) -> {
+        assertThat(traverse(mAssistStructure.getWindowNodeAt(0).getRootViewNode(), (n) -> {
             return n.getLocaleList() != null && localeList.equals(n.getLocaleList());
-        }));
+        })).isTrue();
     }
 
     interface ViewNodeVisitor {
@@ -515,30 +564,34 @@
         return false;
     }
 
-    protected void setFeaturesEnabled(StructureEnabled structure, ScreenshotEnabled screenshot) {
+    protected static void setFeaturesEnabled(StructureEnabled structure,
+            ScreenshotEnabled screenshot) {
         Log.i(TAG, "setFeaturesEnabled(" + structure + ", " + screenshot + ")");
-        SettingsUtils.syncSet(mContext, ASSIST_STRUCTURE_ENABLED, structure.value);
-        SettingsUtils.syncSet(mContext, ASSIST_SCREENSHOT_ENABLED, screenshot.value);
+        sStructureEnabledMgr.set(structure.value);
+        sScreenshotEnabledMgr.set(screenshot.value);
     }
 
     /**
      * Compare view properties of the view hierarchy with that reported in the assist structure.
      */
     private void verifyViewProperties(View parentView, ViewNode parentNode) {
-        assertEquals("Left positions do not match.", parentView.getLeft(), parentNode.getLeft());
-        assertEquals("Top positions do not match.", parentView.getTop(), parentNode.getTop());
-        assertEquals("Opaque flags do not match.", parentView.isOpaque(), parentNode.isOpaque());
+        assertWithMessage("Left positions do not match").that(parentNode.getLeft())
+                .isEqualTo(parentView.getLeft());
+        assertWithMessage("Top positions do not match").that(parentNode.getTop())
+                .isEqualTo(parentView.getTop());
+        assertWithMessage("Opaque flags do not match").that(parentNode.isOpaque())
+                .isEqualTo(parentView.isOpaque());
 
         int viewId = parentView.getId();
 
         if (viewId > 0) {
             if (parentNode.getIdEntry() != null) {
-                assertEquals("View IDs do not match.",
-                        mTestActivity.getResources().getResourceEntryName(viewId),
-                        parentNode.getIdEntry());
+                assertWithMessage("View IDs do not match.").that(parentNode.getIdEntry())
+                        .isEqualTo(mTestActivity.getResources().getResourceEntryName(viewId));
             }
         } else {
-            assertNull("View Node should not have an ID.", parentNode.getIdEntry());
+            assertWithMessage("View Node should not have an ID").that(parentNode.getIdEntry())
+                    .isNull();
         }
 
         Log.i(TAG, "parent text: " + parentNode.getText());
@@ -546,23 +599,26 @@
             Log.i(TAG, "view text: " + ((TextView) parentView).getText());
         }
 
-
-        assertEquals("Scroll X does not match.", parentView.getScrollX(), parentNode.getScrollX());
-        assertEquals("Scroll Y does not match.", parentView.getScrollY(), parentNode.getScrollY());
-        assertEquals("Heights do not match.", parentView.getHeight(), parentNode.getHeight());
-        assertEquals("Widths do not match.", parentView.getWidth(), parentNode.getWidth());
+        assertWithMessage("Scroll X does not match").that(parentNode.getScrollX())
+                .isEqualTo(parentView.getScrollX());
+        assertWithMessage("Scroll Y does not match").that(parentNode.getScrollY())
+                .isEqualTo(parentView.getScrollY());
+        assertWithMessage("Heights do not match").that(parentNode.getHeight())
+                .isEqualTo(parentView.getHeight());
+        assertWithMessage("Widths do not match").that(parentNode.getWidth())
+                .isEqualTo(parentView.getWidth());
 
         if (parentView instanceof TextView) {
             if (parentView instanceof EditText) {
-                assertEquals("Text selection start does not match",
-                        ((EditText) parentView).getSelectionStart(),
-                        parentNode.getTextSelectionStart());
-                assertEquals("Text selection end does not match",
-                        ((EditText) parentView).getSelectionEnd(),
-                        parentNode.getTextSelectionEnd());
+                assertWithMessage("Text selection start does not match",
+                        parentNode.getTextSelectionStart(),
+                        ((EditText) parentView).getSelectionStart());
+                assertWithMessage("Text selection end does not match",
+                        parentNode.getTextSelectionEnd(),
+                        ((EditText) parentView).getSelectionEnd());
             }
             TextView textView = (TextView) parentView;
-            assertEquals(textView.getTextSize(), parentNode.getTextSize());
+            assertThat(parentNode.getTextSize()).isWithin(0.01F).of(textView.getTextSize());
             String viewString = textView.getText().toString();
             String nodeString = parentNode.getText().toString();
 
@@ -570,23 +626,21 @@
                 Log.i(TAG, "Verifying text within TextView at the beginning");
                 Log.i(TAG, "view string: " + viewString);
                 Log.i(TAG, "node string: " + nodeString);
-                assertTrue("String length is unexpected: original string - " + viewString.length() +
-                                ", string in AssistData - " + nodeString.length(),
-                        viewString.length() >= nodeString.length());
-                assertTrue("Expected a longer string to be shown. expected: "
-                                + Math.min(viewString.length(), 30) + " was: " + nodeString
-                                .length(),
-                        nodeString.length() >= Math.min(viewString.length(), 30));
+                assertWithMessage("String length is unexpected: original string - %s, "
+                        + "string in AssistData - %s", viewString.length(), nodeString.length())
+                                .that(viewString.length()).isAtLeast(nodeString.length());
+                assertWithMessage("Expected a longer string to be shown").that(
+                        nodeString.length()).isAtLeast(Math.min(viewString.length(), 30));
                 for (int x = 0; x < parentNode.getText().length(); x++) {
-                    assertEquals("Char not equal at index: " + x,
-                            ((TextView) parentView).getText().toString().charAt(x),
-                            parentNode.getText().charAt(x));
+                    assertWithMessage("Char not equal at index: %s", x).that(
+                            parentNode.getText().charAt(x)).isEqualTo(
+                            ((TextView) parentView).getText().toString().charAt(x));
                 }
             } else if (parentNode.getScrollX() == parentView.getWidth()) {
 
             }
         } else {
-            assertNull(parentNode.getText());
+            assertThat(parentNode.getText()).isNull();
         }
     }
 
diff --git a/tests/tests/assist/src/android/assist/cts/AssistantContentViewTest.java b/tests/tests/assist/src/android/assist/cts/AssistantContentViewTest.java
index a4a28d7..cd1c8bc 100644
--- a/tests/tests/assist/src/android/assist/cts/AssistantContentViewTest.java
+++ b/tests/tests/assist/src/android/assist/cts/AssistantContentViewTest.java
@@ -16,12 +16,18 @@
 
 package android.assist.cts;
 
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
 import android.assist.common.AutoResetLatch;
 import android.assist.common.Utils;
 import android.graphics.Point;
 import android.os.Bundle;
 import android.util.Log;
 
+import org.junit.Test;
+
 import java.util.concurrent.TimeUnit;
 
 /** Test verifying the Content View of the Assistant */
@@ -31,16 +37,14 @@
     private Bundle mBundle;
 
     @Override
-    public void setUp() throws Exception {
-        super.setUp();
+    protected void customSetup() throws Exception {
         mActionLatchReceiver = new AssistantReceiver();
         startTestActivity(Utils.VERIFY_CONTENT_VIEW);
     }
 
     @Override
-    public void tearDown() throws Exception {
+    protected void customTearDown() throws Exception {
         mBundle = null;
-        super.tearDown();
     }
 
     private void waitForContentView() throws Exception {
@@ -50,6 +54,7 @@
         }
     }
 
+    @Test
     public void testAssistantContentViewDimens() throws Exception {
         if (mActivityManager.isLowRamDevice()) {
           Log.d(TAG, "Not running assist tests on low-RAM device.");
@@ -62,8 +67,8 @@
         int height = mBundle.getInt(Utils.EXTRA_CONTENT_VIEW_HEIGHT, 0);
         int width = mBundle.getInt(Utils.EXTRA_CONTENT_VIEW_WIDTH, 0);
         Point displayPoint = mBundle.getParcelable(Utils.EXTRA_DISPLAY_POINT);
-        assertEquals(displayPoint.y, height);
-        assertEquals(displayPoint.x, width);
+        assertThat(height).isEqualTo(displayPoint.y);
+        assertThat(width).isEqualTo(displayPoint.x);
     }
 
     private class AssistantReceiver extends ActionLatchReceiver {
diff --git a/tests/tests/assist/src/android/assist/cts/DisableContextTest.java b/tests/tests/assist/src/android/assist/cts/DisableContextTest.java
index 11e404f..912c5d7 100644
--- a/tests/tests/assist/src/android/assist/cts/DisableContextTest.java
+++ b/tests/tests/assist/src/android/assist/cts/DisableContextTest.java
@@ -19,6 +19,8 @@
 import android.assist.common.Utils;
 import android.util.Log;
 
+import org.junit.Test;
+
 /** Test we receive proper assist data when context is disabled or enabled */
 public class DisableContextTest extends AssistTestBase {
     static final String TAG = "DisableContextTest";
@@ -26,18 +28,17 @@
     private static final String TEST_CASE_TYPE = Utils.DISABLE_CONTEXT;
 
     @Override
-    public void setUp() throws Exception {
-        super.setUp();
+    protected void customSetup() throws Exception {
         startTestActivity(TEST_CASE_TYPE);
     }
 
     @Override
-    public void tearDown() throws Exception {
+    public void customTearDown() throws Exception {
         setFeaturesEnabled(StructureEnabled.TRUE, ScreenshotEnabled.TRUE);
         logContextAndScreenshotSetting();
-        super.tearDown();
     }
 
+    @Test
     public void testContextAndScreenshotOff() throws Exception {
         if (!mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS)) {
             Log.d(TAG, "Not running assist tests - voice_recognizers feature is not supported");
@@ -53,6 +54,7 @@
         verifyAssistDataNullness(true, true, true, true);
     }
 
+    @Test
     public void testContextOff() throws Exception {
         if (!mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS)) {
             Log.d(TAG, "Not running assist tests - voice_recognizers feature is not supported");
@@ -68,6 +70,7 @@
         verifyAssistDataNullness(false, false, false, true);
     }
 
+    @Test
     public void testScreenshotOff() throws Exception {
         if (!mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS)) {
             Log.d(TAG, "Not running assist tests - voice_recognizers feature is not supported");
diff --git a/tests/tests/assist/src/android/assist/cts/ExtraAssistDataTest.java b/tests/tests/assist/src/android/assist/cts/ExtraAssistDataTest.java
index 2acf4e3..d465880 100644
--- a/tests/tests/assist/src/android/assist/cts/ExtraAssistDataTest.java
+++ b/tests/tests/assist/src/android/assist/cts/ExtraAssistDataTest.java
@@ -21,16 +21,19 @@
 import android.os.Bundle;
 import android.util.Log;
 
+import org.junit.Test;
+
+import static com.google.common.truth.Truth.assertWithMessage;
 public class ExtraAssistDataTest extends AssistTestBase {
     private static final String TAG = "ExtraAssistDataTest";
     private static final String TEST_CASE_TYPE = Utils.EXTRA_ASSIST;
 
     @Override
-    public void setUp() throws Exception {
-        super.setUp();
+    protected void customSetup() throws Exception {
         startTestActivity(TEST_CASE_TYPE);
     }
 
+    @Test
     public void testAssistContentAndAssistData() throws Exception {
         if (mActivityManager.isLowRamDevice()) {
             Log.d(TAG, "Not running assist tests on low-RAM device.");
@@ -46,20 +49,22 @@
         Log.i(TAG, "assist bundle is: " + Utils.toBundleString(mAssistBundle));
 
         // first tests that the assist content's structured data is the expected
-        assertEquals("AssistContent structured data did not match data in onProvideAssistContent",
-                Utils.getStructuredJSON(), mAssistContent.getStructuredData());
+        assertWithMessage(
+                "AssistContent structured data did not match data in onProvideAssistContent").that(
+                        mAssistContent.getStructuredData()).isEqualTo(Utils.getStructuredJSON());
         Bundle extraExpectedBundle = Utils.getExtraAssistBundle();
         Bundle extraAssistBundle = mAssistBundle.getBundle(Intent.EXTRA_ASSIST_CONTEXT);
         for (String key : extraExpectedBundle.keySet()) {
-            assertTrue("Assist bundle does not contain expected extra context key: " + key,
-                    extraAssistBundle.containsKey(key));
-            assertEquals("Extra assist context bundle values do not match for key: " + key,
-                    extraExpectedBundle.get(key), extraAssistBundle.get(key));
+            assertWithMessage("Assist bundle does not contain expected extra context key: %s", key)
+                    .that(extraAssistBundle.containsKey(key)).isTrue();
+            assertWithMessage("Extra assist context bundle values do not match for key: %s", key)
+                    .that(extraAssistBundle.get(key)).isEqualTo(extraExpectedBundle.get(key));
         }
 
         // then test the EXTRA_ASSIST_UID
         int expectedUid = Utils.getExpectedUid(extraAssistBundle);
         int actualUid = mAssistBundle.getInt(Intent.EXTRA_ASSIST_UID);
-        assertEquals("Wrong value for EXTRA_ASSIST_UID", expectedUid, actualUid);
+        assertWithMessage("Wrong value for EXTRA_ASSIST_UID").that(actualUid)
+                .isEqualTo(expectedUid);
     }
 }
diff --git a/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java b/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java
index 97573d8..d5724b9 100644
--- a/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java
+++ b/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java
@@ -20,6 +20,8 @@
 import android.assist.common.Utils;
 import android.util.Log;
 
+import org.junit.Test;
+
 /**
  * Test we receive proper assist data (root assistStructure with no children) when the assistant is
  * invoked on an app with FLAG_SECURE set.
@@ -30,11 +32,11 @@
     private static final String TEST_CASE_TYPE = Utils.FLAG_SECURE;
 
     @Override
-    public void setUp() throws Exception {
-        super.setUp();
+    protected void customSetup() throws Exception {
         startTestActivity(TEST_CASE_TYPE);
     }
 
+    @Test
     public void testSecureActivity() throws Exception {
         if (!mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS)) {
             Log.d(TAG, "Not running assist tests - voice_recognizers feature is not supported");
diff --git a/tests/tests/assist/src/android/assist/cts/FocusChangeTest.java b/tests/tests/assist/src/android/assist/cts/FocusChangeTest.java
index bc63a43..e4e530a 100644
--- a/tests/tests/assist/src/android/assist/cts/FocusChangeTest.java
+++ b/tests/tests/assist/src/android/assist/cts/FocusChangeTest.java
@@ -16,11 +16,15 @@
 
 package android.assist.cts;
 
+import static org.junit.Assert.fail;
+
 import android.assist.common.AutoResetLatch;
 import android.assist.common.Utils;
 import android.util.Log;
 import android.util.Pair;
 
+import org.junit.Test;
+
 import java.util.concurrent.TimeUnit;
 
 /** Test that triggering the Assistant causes the underlying Activity to lose focus **/
@@ -32,8 +36,7 @@
     private AutoResetLatch mHasLostFocusLatch = new AutoResetLatch(1);
 
     @Override
-    public void setUp() throws Exception {
-        super.setUp();
+    protected void customSetup() throws Exception {
         mActionLatchReceiver = new ActionLatchReceiver(
                 Pair.create(Utils.GAINED_FOCUS, mHasGainedFocusLatch),
                 Pair.create(Utils.LOST_FOCUS, mHasLostFocusLatch)
@@ -56,6 +59,7 @@
         }
     }
 
+    @Test
     public void testLayerCausesUnderlyingActivityToLoseFocus() throws Exception {
         if (mActivityManager.isLowRamDevice()) {
             Log.d(TAG, "Not running assist tests on low-RAM device.");
diff --git a/tests/tests/assist/src/android/assist/cts/LargeViewHierarchyTest.java b/tests/tests/assist/src/android/assist/cts/LargeViewHierarchyTest.java
index 05a473e..80647e5 100644
--- a/tests/tests/assist/src/android/assist/cts/LargeViewHierarchyTest.java
+++ b/tests/tests/assist/src/android/assist/cts/LargeViewHierarchyTest.java
@@ -20,6 +20,8 @@
 import android.assist.common.Utils;
 import android.util.Log;
 
+import org.junit.Test;
+
 /**
  *  Test that the AssistStructure returned is properly formatted.
  */
@@ -28,11 +30,11 @@
     private static final String TEST_CASE_TYPE = Utils.LARGE_VIEW_HIERARCHY;
 
     @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+    protected void customSetup() throws Exception {
         startTestActivity(TEST_CASE_TYPE);
     }
 
+    @Test
     public void testTextView() throws Exception {
         if (mActivityManager.isLowRamDevice()) {
             Log.d(TAG, "Not running assist tests on low-RAM device.");
diff --git a/tests/tests/assist/src/android/assist/cts/LifecycleTest.java b/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
index c35f8f0..35cec57 100644
--- a/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
+++ b/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
@@ -16,11 +16,15 @@
 
 package android.assist.cts;
 
+import static org.junit.Assert.fail;
+
 import android.assist.common.AutoResetLatch;
 import android.assist.common.Utils;
 import android.os.Bundle;
 import android.util.Log;
 
+import org.junit.Test;
+
 import java.util.concurrent.TimeUnit;
 
 /** Test we receive proper assist data when context is disabled or enabled */
@@ -41,8 +45,7 @@
     private boolean mLostFocusIsLifecycle;
 
     @Override
-    public void setUp() throws Exception {
-        super.setUp();
+    protected void customSetup() throws Exception {
         mActionLatchReceiver = new LifecycleTestReceiver();
         mLostFocusIsLifecycle = false;
         startTestActivity(TEST_CASE_TYPE);
@@ -75,6 +78,7 @@
         }
     }
 
+    @Test
     public void testLayerDoesNotTriggerLifecycleMethods() throws Exception {
         if (!mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS)) {
             Log.d(TAG, "Not running assist tests - voice_recognizers feature is not supported");
@@ -100,6 +104,7 @@
         waitForDestroy();
     }
 
+    @Test
     public void testNoUiLayerDoesNotTriggerLifecycleMethods() throws Exception {
         if (mActivityManager.isLowRamDevice()) {
             Log.d(TAG, "Not running assist tests on low-RAM device.");
diff --git a/tests/tests/assist/src/android/assist/cts/ScreenshotTest.java b/tests/tests/assist/src/android/assist/cts/ScreenshotTest.java
index dcc3be0..c9d16c8 100644
--- a/tests/tests/assist/src/android/assist/cts/ScreenshotTest.java
+++ b/tests/tests/assist/src/android/assist/cts/ScreenshotTest.java
@@ -16,24 +16,28 @@
 
 package android.assist.cts;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import android.assist.common.AutoResetLatch;
 import android.assist.common.Utils;
 import android.graphics.Color;
 import android.os.Bundle;
 import android.util.Log;
 
+import org.junit.Test;
+
 public class ScreenshotTest extends AssistTestBase {
     static final String TAG = "ScreenshotTest";
 
     private static final String TEST_CASE_TYPE = Utils.SCREENSHOT;
 
     @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+    protected void customSetup() throws Exception {
         // start test start activity
         startTestActivity(TEST_CASE_TYPE);
     }
 
+    @Test
     public void testRedScreenshot() throws Throwable {
         if (mActivityManager.isLowRamDevice()) {
             Log.d(TAG, "Not running assist tests on low-RAM device.");
@@ -50,10 +54,11 @@
         eventuallyWithSessionClose(() -> {
             delayAndStartSession(Color.RED);
             verifyAssistDataNullness(false, false, false, false);
-            assertTrue(mScreenshotMatches);
+            assertThat(mScreenshotMatches).isTrue();
         });
     }
 
+    @Test
     public void testGreenScreenshot() throws Throwable {
         if (mActivityManager.isLowRamDevice()) {
             Log.d(TAG, "Not running assist tests on low-RAM device.");
@@ -70,10 +75,11 @@
         eventuallyWithSessionClose(() -> {
             delayAndStartSession(Color.GREEN);
             verifyAssistDataNullness(false, false, false, false);
-            assertTrue(mScreenshotMatches);
+            assertThat(mScreenshotMatches).isTrue();
         });
     }
 
+    @Test
     public void testBlueScreenshot() throws Throwable {
         if (mActivityManager.isLowRamDevice()) {
             Log.d(TAG, "Not running assist tests on low-RAM device.");
@@ -90,7 +96,7 @@
         eventuallyWithSessionClose(() -> {
             delayAndStartSession(Color.BLUE);
             verifyAssistDataNullness(false, false, false, false);
-            assertTrue(mScreenshotMatches);
+            assertThat(mScreenshotMatches).isTrue();
         });
     }
 
diff --git a/tests/tests/assist/src/android/assist/cts/TextViewTest.java b/tests/tests/assist/src/android/assist/cts/TextViewTest.java
index 6b06442..78c0a52 100644
--- a/tests/tests/assist/src/android/assist/cts/TextViewTest.java
+++ b/tests/tests/assist/src/android/assist/cts/TextViewTest.java
@@ -21,6 +21,8 @@
 import android.os.Bundle;
 import android.util.Log;
 
+import org.junit.Test;
+
 /**
  *  Test that the AssistStructure returned is properly formatted.
  */
@@ -29,11 +31,11 @@
     private static final String TEST_CASE_TYPE = Utils.TEXTVIEW;
 
     @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+    protected void customSetup() throws Exception {
         startTestActivity(TEST_CASE_TYPE);
     }
 
+    @Test
     public void testTextView() throws Exception {
         if (mActivityManager.isLowRamDevice()) {
             Log.d(TAG, "Not running assist tests on low-RAM device.");
diff --git a/tests/tests/assist/src/android/assist/cts/WebViewTest.java b/tests/tests/assist/src/android/assist/cts/WebViewTest.java
index 75fc0ba..a7f9329 100644
--- a/tests/tests/assist/src/android/assist/cts/WebViewTest.java
+++ b/tests/tests/assist/src/android/assist/cts/WebViewTest.java
@@ -16,11 +16,15 @@
 
 package android.assist.cts;
 
+import static org.junit.Assert.fail;
+
 import android.assist.common.AutoResetLatch;
 import android.assist.common.Utils;
 import android.content.pm.PackageManager;
 import android.util.Log;
 
+import org.junit.Test;
+
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -33,8 +37,7 @@
     private final AutoResetLatch mTestWebViewLatch = new AutoResetLatch();
 
     @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+    protected void customSetup() throws Exception {
         mActionLatchReceiver = new ActionLatchReceiver(Utils.TEST_ACTIVITY_WEBVIEW_LOADED, mTestWebViewLatch);
         startTestActivity(TEST_CASE_TYPE);
     }
@@ -46,6 +49,7 @@
         }
     }
 
+    @Test
     public void testWebView() throws Throwable {
         if (mActivityManager.isLowRamDevice()) {
             Log.d(TAG, "Not running assist tests on low-RAM device.");
diff --git a/tests/tests/background/OWNERS b/tests/tests/background/OWNERS
new file mode 100644
index 0000000..ec12f79
--- /dev/null
+++ b/tests/tests/background/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 330055
+include /tests/app/OWNERS
diff --git a/tests/tests/batterysaving/Android.bp b/tests/tests/batterysaving/Android.bp
index 032d2bf..b0007e2 100644
--- a/tests/tests/batterysaving/Android.bp
+++ b/tests/tests/batterysaving/Android.bp
@@ -22,6 +22,7 @@
         "mockito-target-minus-junit4",
         "compatibility-device-util-axt",
         "ctstestrunner-axt",
+        "platformprotosnano",
         "truth-prebuilt",
         "ub-uiautomator",
     ],
diff --git a/tests/tests/batterysaving/OWNERS b/tests/tests/batterysaving/OWNERS
new file mode 100644
index 0000000..1fe2293
--- /dev/null
+++ b/tests/tests/batterysaving/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 330055
+omakoto@google.com
+yamasani@google.com
diff --git a/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySavingTestBase.java b/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySavingTestBase.java
index e2cf479..3db9dbb 100644
--- a/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySavingTestBase.java
+++ b/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySavingTestBase.java
@@ -34,6 +34,9 @@
 import com.android.compatibility.common.util.BatteryUtils;
 import com.android.compatibility.common.util.BeforeAfterRule;
 import com.android.compatibility.common.util.OnFailureRule;
+import com.android.compatibility.common.util.ProtoUtils;
+import com.android.server.job.nano.JobSchedulerServiceDumpProto;
+import com.android.server.job.nano.StateControllerProto;
 
 import org.junit.Rule;
 import org.junit.rules.RuleChain;
@@ -96,9 +99,19 @@
     }
 
     public void waitUntilJobForceAppStandby(boolean expected) throws Exception {
-        waitUntil("Force all apps standby still " + !expected + " (job)", () ->
-                runShellCommand("dumpsys jobscheduler")
-                        .contains("Force all apps standby: " + expected));
+        waitUntil("Force all apps standby still " + !expected + " (job)", () -> {
+            JobSchedulerServiceDumpProto proto = ProtoUtils.getProto(
+                    InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+                    JobSchedulerServiceDumpProto.class,
+                    ProtoUtils.DUMPSYS_JOB_SCHEDULER);
+            for (StateControllerProto controllerProto : proto.controllers) {
+                if (controllerProto.hasBackground()) {
+                    return controllerProto.getBackground().appStateTracker.forceAllAppsStandby
+                            == expected;
+                }
+            }
+            return false;
+        });
     }
 
     public void waitUntilForceBackgroundCheck(boolean expected) throws Exception {
diff --git a/tests/tests/batterysaving/src/android/os/cts/deviceidle/DeviceIdleTest.java b/tests/tests/batterysaving/src/android/os/cts/deviceidle/DeviceIdleTest.java
new file mode 100644
index 0000000..20410e7
--- /dev/null
+++ b/tests/tests/batterysaving/src/android/os/cts/deviceidle/DeviceIdleTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.deviceidle;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.PowerManager;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DeviceIdleTest {
+    @Test
+    public void testDeviceIdleManager() {
+        Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        assertNotNull(context.getSystemService(Context.DEVICE_IDLE_CONTROLLER));
+    }
+
+    @Test
+    public void testPowerManagerIgnoringBatteryOptimizations() {
+        Context context = InstrumentationRegistry.getInstrumentation().getContext();
+
+        assertTrue(context.getSystemService(PowerManager.class)
+                .isIgnoringBatteryOptimizations("com.android.shell"));
+        assertFalse(context.getSystemService(PowerManager.class)
+                .isIgnoringBatteryOptimizations("no.such.package.!!!"));
+    }
+
+}
diff --git a/tests/tests/car/TEST_MAPPING b/tests/tests/car/TEST_MAPPING
index ef3ec4a..d4ff46d 100644
--- a/tests/tests/car/TEST_MAPPING
+++ b/tests/tests/car/TEST_MAPPING
@@ -1,8 +1,7 @@
 {
-  "presubmit": [
+  "auto-presubmit": [
     {
-      "name": "CtsCarTestCases",
-      "keywords": ["auto-cf"]
+      "name": "CtsCarTestCases"
     }
   ]
 }
diff --git a/tests/tests/car/src/android/car/cts/CarPropertyManagerTest.java b/tests/tests/car/src/android/car/cts/CarPropertyManagerTest.java
index 56e2401..ceabc3d 100644
--- a/tests/tests/car/src/android/car/cts/CarPropertyManagerTest.java
+++ b/tests/tests/car/src/android/car/cts/CarPropertyManagerTest.java
@@ -33,6 +33,8 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.compatibility.common.util.CddTest;
+
 import static com.google.common.truth.Truth.assertThat;
 import static org.junit.Assert.assertTrue;
 
@@ -117,7 +119,38 @@
         List<CarPropertyConfig> requiredConfigs = mCarPropertyManager.getPropertyList(mPropertyIds);
         // Vehicles need to implement all of those properties
         assertEquals(mPropertyIds.size(), requiredConfigs.size());
+    }
 
+    @CddTest(requirement="2.5.1")
+    @Test
+    public void testMustSupportGearSelection() throws Exception {
+        assertTrue("Must support GEAR_SELECTION",
+            mCarPropertyManager.getPropertyList().stream().anyMatch(cfg -> cfg.getPropertyId() ==
+                VehiclePropertyIds.GEAR_SELECTION));
+    }
+
+    @CddTest(requirement="2.5.1")
+    @Test
+    public void testMustSupportNightMode() {
+        assertTrue("Must support NIGHT_MODE",
+            mCarPropertyManager.getPropertyList().stream().anyMatch(cfg -> cfg.getPropertyId() ==
+                VehiclePropertyIds.NIGHT_MODE));
+    }
+
+    @CddTest(requirement="2.5.1")
+    @Test
+    public void testMustSupportPerfVehicleSpeed() throws Exception {
+        assertTrue("Must support PERF_VEHICLE_SPEED",
+            mCarPropertyManager.getPropertyList().stream().anyMatch(cfg -> cfg.getPropertyId() ==
+                VehiclePropertyIds.PERF_VEHICLE_SPEED));
+    }
+
+    @CddTest(requirement = "2.5.1")
+    @Test
+    public void testMustSupportParkingBrakeOn() throws Exception {
+        assertTrue("Must support PARKING_BRAKE_ON",
+            mCarPropertyManager.getPropertyList().stream().anyMatch(cfg -> cfg.getPropertyId() ==
+                VehiclePropertyIds.PARKING_BRAKE_ON));
     }
 
     @SuppressWarnings("unchecked")
@@ -297,5 +330,4 @@
             return config.getAreaIds();
         }
     }
-
 }
diff --git a/tests/tests/classloaderfactory/test-memcl/OWNERS b/tests/tests/classloaderfactory/test-memcl/OWNERS
index bda93bc..fa9c94d 100644
--- a/tests/tests/classloaderfactory/test-memcl/OWNERS
+++ b/tests/tests/classloaderfactory/test-memcl/OWNERS
@@ -1,2 +1,2 @@
 # Bug component: 86431
-dbrazdil@google.com
\ No newline at end of file
+ngeoffray@google.com
\ No newline at end of file
diff --git a/tests/tests/classloaderfactory/test-pathcl/OWNERS b/tests/tests/classloaderfactory/test-pathcl/OWNERS
index bda93bc..fa9c94d 100644
--- a/tests/tests/classloaderfactory/test-pathcl/OWNERS
+++ b/tests/tests/classloaderfactory/test-pathcl/OWNERS
@@ -1,2 +1,2 @@
 # Bug component: 86431
-dbrazdil@google.com
\ No newline at end of file
+ngeoffray@google.com
\ No newline at end of file
diff --git a/tests/tests/content/AndroidManifest.xml b/tests/tests/content/AndroidManifest.xml
index 7e5d2e9..66eb8a4 100644
--- a/tests/tests/content/AndroidManifest.xml
+++ b/tests/tests/content/AndroidManifest.xml
@@ -259,6 +259,11 @@
             android:process=":testpagingprovider"
             android:multiprocess="false" />
 
+        <provider android:name="android.content.cts.MockBuggyProvider"
+                  android:authorities="android.content.cts.mockbuggyprovider"
+                  android:process=":mockbuggyprovider"
+                  android:multiprocess="false" />
+
         <service android:name="android.content.cts.MockService" />
 
         <service android:name="android.content.cts.MockSyncAdapterService" android:exported="true">
diff --git a/tests/tests/content/AndroidTest.xml b/tests/tests/content/AndroidTest.xml
index 418c201..88a1aec 100644
--- a/tests/tests/content/AndroidTest.xml
+++ b/tests/tests/content/AndroidTest.xml
@@ -22,6 +22,8 @@
     <!-- The framework has some native code involved. -->
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
 
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="mkdir -p /data/local/tmp/cts/content" />
         <option name="teardown-command" value="rm -rf /data/local/tmp/cts"/>
diff --git a/tests/tests/content/CtsSyncAccountAccessOtherCertTests/OWNERS b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/OWNERS
new file mode 100644
index 0000000..c5fc344
--- /dev/null
+++ b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 197138
+include /tests/app/OWNERS
diff --git a/tests/tests/content/res/color-night/testcolor_daynight.xml b/tests/tests/content/res/color-night/testcolor_daynight.xml
new file mode 100644
index 0000000..a63f89a
--- /dev/null
+++ b/tests/tests/content/res/color-night/testcolor_daynight.xml
@@ -0,0 +1,19 @@
+<?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.
+  -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/black"/>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/content/res/color-notnight/testcolor_daynight.xml b/tests/tests/content/res/color-notnight/testcolor_daynight.xml
new file mode 100644
index 0000000..2f4ecb8
--- /dev/null
+++ b/tests/tests/content/res/color-notnight/testcolor_daynight.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 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.
+  -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/white"/>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/content/res/color/testcolor_daynight.xml b/tests/tests/content/res/color/testcolor_daynight.xml
new file mode 100644
index 0000000..0f13433
--- /dev/null
+++ b/tests/tests/content/res/color/testcolor_daynight.xml
@@ -0,0 +1,19 @@
+<?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.
+  -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="#777777"/>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/content/src/android/content/cts/BuggyProviderTest.java b/tests/tests/content/src/android/content/cts/BuggyProviderTest.java
new file mode 100644
index 0000000..0c17256
--- /dev/null
+++ b/tests/tests/content/src/android/content/cts/BuggyProviderTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.content.cts;
+
+import android.app.ActivityManager;
+import android.content.ContentResolver;
+import android.os.UserHandle;
+import android.test.AndroidTestCase;
+
+/**
+ * Test system behavior of a buggy provider.
+ *
+ * see @{@link MockBuggyProvider}
+ */
+public class BuggyProviderTest extends AndroidTestCase {
+
+    public void testGetTypeDoesntCrashSystem() {
+        // ensure the system doesn't crash when a provider takes too long to respond
+        try {
+            ActivityManager.getService().getProviderMimeType(
+                    MockBuggyProvider.CONTENT_URI, UserHandle.USER_CURRENT);
+        } catch (Exception e) {
+            fail("Unexpected exception while fetching type: " + e.getMessage());
+        }
+    }
+
+    public void testGetTypeViaResolverDoesntCrashSystem() {
+        // ensure the system doesn't crash when a provider takes too long to respond
+        ContentResolver resolver = mContext.getContentResolver();
+        try {
+            resolver.getType(MockBuggyProvider.CONTENT_URI);
+        } catch (Exception e) {
+            fail("Unexpected exception while fetching type: " + e.getMessage());
+        }
+    }
+}
diff --git a/tests/tests/content/src/android/content/cts/ContentProviderOperationTest.java b/tests/tests/content/src/android/content/cts/ContentProviderOperationTest.java
new file mode 100644
index 0000000..4f11d6d
--- /dev/null
+++ b/tests/tests/content/src/android/content/cts/ContentProviderOperationTest.java
@@ -0,0 +1,246 @@
+/*
+ * 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.content.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentProvider;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentValues;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.Bundle;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class ContentProviderOperationTest {
+    private static final Uri TEST_URI = Uri.parse("content://com.example");
+    private static final Uri TEST_URI_RESULT = Uri.parse("content://com.example/12");
+    private static final String TEST_SELECTION = "foo=?";
+    private static final String[] TEST_SELECTION_ARGS = new String[] { "bar" };
+    private static final String TEST_METHOD = "test_method";
+    private static final String TEST_ARG = "test_arg";
+
+    private static final ContentValues TEST_VALUES = new ContentValues();
+    static {
+        TEST_VALUES.put("test_key", "test_value");
+    }
+
+    private static final Bundle TEST_EXTRAS = new Bundle();
+    static {
+        TEST_EXTRAS.putString("test_key", "test_value");
+    }
+
+    private static final Bundle TEST_EXTRAS_RESULT = new Bundle();
+    static {
+        TEST_EXTRAS_RESULT.putString("test_result", "42");
+    }
+
+    private static final ContentProviderResult[] TEST_RESULTS = new ContentProviderResult[] {
+            new ContentProviderResult(TEST_URI_RESULT),
+            new ContentProviderResult(84),
+            new ContentProviderResult(TEST_EXTRAS_RESULT),
+            new ContentProviderResult(new IllegalArgumentException()),
+    };
+
+    private ContentProvider provider;
+
+    private ContentProviderOperation op;
+    private ContentProviderResult res;
+
+    @Before
+    public void setUp() throws Exception {
+        provider = mock(ContentProvider.class);
+    }
+
+    @Test
+    public void testInsert() throws Exception {
+        op = ContentProviderOperation.newInsert(TEST_URI)
+                .withValues(TEST_VALUES)
+                .build();
+
+        assertEquals(TEST_URI, op.getUri());
+        assertTrue(op.isInsert());
+        assertTrue(op.isWriteOperation());
+
+        when(provider.insert(eq(TEST_URI), eq(TEST_VALUES)))
+                .thenReturn(TEST_URI_RESULT);
+        res = op.apply(provider, null, 0);
+        assertEquals(TEST_URI_RESULT, res.uri);
+    }
+
+    @Test
+    public void testUpdate() throws Exception {
+        op = ContentProviderOperation.newUpdate(TEST_URI)
+                .withSelection(TEST_SELECTION, TEST_SELECTION_ARGS)
+                .withValues(TEST_VALUES)
+                .build();
+
+        assertEquals(TEST_URI, op.getUri());
+        assertTrue(op.isUpdate());
+        assertTrue(op.isWriteOperation());
+
+        when(provider.update(eq(TEST_URI), eq(TEST_VALUES),
+                eq(TEST_SELECTION), eq(TEST_SELECTION_ARGS)))
+                        .thenReturn(1);
+        res = op.apply(provider, null, 0);
+        assertEquals(1, (int) res.count);
+    }
+
+    @Test
+    public void testDelete() throws Exception {
+        op = ContentProviderOperation.newDelete(TEST_URI)
+                .withSelection(TEST_SELECTION, TEST_SELECTION_ARGS)
+                .build();
+
+        assertEquals(TEST_URI, op.getUri());
+        assertTrue(op.isDelete());
+        assertTrue(op.isWriteOperation());
+
+        when(provider.delete(eq(TEST_URI),
+                eq(TEST_SELECTION), eq(TEST_SELECTION_ARGS)))
+                        .thenReturn(1);
+        res = op.apply(provider, null, 0);
+        assertEquals(1, (int) res.count);
+    }
+
+    @Test
+    public void testAssertQuery() throws Exception {
+        op = ContentProviderOperation.newAssertQuery(TEST_URI)
+                .withSelection(TEST_SELECTION, TEST_SELECTION_ARGS)
+                .withValues(TEST_VALUES)
+                .build();
+
+        assertEquals(TEST_URI, op.getUri());
+        assertTrue(op.isAssertQuery());
+        assertTrue(op.isReadOperation());
+
+        final MatrixCursor cursor = new MatrixCursor(new String[] { "test_key" });
+        cursor.addRow(new Object[] { "test_value" });
+
+        when(provider.query(eq(TEST_URI), eq(new String[] { "test_key" }),
+              eq(TEST_SELECTION), eq(TEST_SELECTION_ARGS), eq(null)))
+                        .thenReturn(cursor);
+        op.apply(provider, null, 0);
+    }
+
+    @Test
+    public void testCall() throws Exception {
+        op = ContentProviderOperation.newCall(TEST_URI, TEST_METHOD, TEST_ARG)
+                .withExtras(TEST_EXTRAS)
+                .build();
+
+        assertEquals(TEST_URI, op.getUri());
+        assertTrue(op.isCall());
+
+        when(provider.call(eq(TEST_URI.getAuthority()), eq(TEST_METHOD),
+                eq(TEST_ARG), notNull()))
+                        .thenReturn(TEST_EXTRAS_RESULT);
+        res = op.apply(provider, null, 0);
+        assertEquals(TEST_EXTRAS_RESULT, res.extras);
+    }
+
+    @Test
+    public void testBackReferenceSelection() throws Exception {
+        op = ContentProviderOperation.newDelete(TEST_URI)
+                .withSelection(null, new String[] { "a", "b", "c", "d" })
+                .withSelectionBackReference(0, 0)
+                .withSelectionBackReference(1, 1)
+                .withSelectionBackReference(2, 2, "test_result")
+                .build();
+
+        final String[] res = op.resolveSelectionArgsBackReferences(TEST_RESULTS,
+                TEST_RESULTS.length);
+        assertEquals("12", res[0]);
+        assertEquals("84", res[1]);
+        assertEquals("42", res[2]);
+        assertEquals("d", res[3]);
+    }
+
+    @Test
+    public void testBackReferenceValue() throws Exception {
+        final ContentValues values = new ContentValues();
+        values.put("a", "a");
+        values.put("b", "b");
+        values.put("c", "c");
+        values.put("d", "d");
+
+        op = ContentProviderOperation.newUpdate(TEST_URI)
+                .withValues(values)
+                .withValueBackReference("a", 0)
+                .withValueBackReference("b", 1)
+                .withValueBackReference("c", 2, "test_result")
+                .build();
+
+        final ContentValues res = op.resolveValueBackReferences(TEST_RESULTS,
+                TEST_RESULTS.length);
+        assertEquals(12L, (long) res.get("a"));
+        assertEquals(84L, (long) res.get("b"));
+        assertEquals("42", res.get("c"));
+        assertEquals("d", res.get("d"));
+    }
+
+    @Test
+    public void testBackReferenceExtra() throws Exception {
+        final Bundle extras = new Bundle();
+        extras.putString("a", "a");
+        extras.putString("b", "b");
+        extras.putString("c", "c");
+        extras.putString("d", "d");
+
+        op = ContentProviderOperation.newCall(TEST_URI, TEST_METHOD, TEST_ARG)
+                .withExtras(extras)
+                .withExtraBackReference("a", 0)
+                .withExtraBackReference("b", 1)
+                .withExtraBackReference("c", 2, "test_result")
+                .build();
+
+        final Bundle res = op.resolveExtrasBackReferences(TEST_RESULTS,
+                TEST_RESULTS.length);
+        assertEquals(12L, (long) res.get("a"));
+        assertEquals(84L, (long) res.get("b"));
+        assertEquals("42", res.get("c"));
+        assertEquals("d", res.get("d"));
+    }
+
+    @Test
+    public void testExceptionAllowed() throws Exception {
+        op = ContentProviderOperation.newCall(TEST_URI, TEST_METHOD, TEST_ARG)
+                .withExtras(TEST_EXTRAS)
+                .withExceptionAllowed(true)
+                .build();
+
+        assertTrue(op.isExceptionAllowed());
+
+        when(provider.call(eq(TEST_URI.getAuthority()), eq(TEST_METHOD),
+                eq(TEST_ARG), notNull()))
+                        .thenThrow(new IllegalArgumentException());
+        res = op.apply(provider, null, 0);
+        assertTrue((res.exception instanceof IllegalArgumentException));
+    }
+}
diff --git a/tests/tests/content/src/android/content/cts/ContentProviderResultTest.java b/tests/tests/content/src/android/content/cts/ContentProviderResultTest.java
new file mode 100644
index 0000000..7e98296
--- /dev/null
+++ b/tests/tests/content/src/android/content/cts/ContentProviderResultTest.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.content.cts;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.ContentProviderResult;
+import android.net.Uri;
+import android.os.Bundle;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class ContentProviderResultTest {
+    private final Uri TEST_URI = Uri.EMPTY;
+    private final Bundle TEST_BUNDLE = Bundle.EMPTY;
+    private final Exception TEST_EXCEPTION = new IllegalArgumentException();
+
+    @Test
+    public void testUri() throws Exception {
+        assertEquals(TEST_URI, new ContentProviderResult(TEST_URI).uri);
+    }
+
+    @Test
+    public void testCount() throws Exception {
+        assertEquals(42, (int) new ContentProviderResult(42).count);
+    }
+
+    @Test
+    public void testExtras() throws Exception {
+        assertEquals(TEST_BUNDLE, new ContentProviderResult(TEST_BUNDLE).extras);
+    }
+
+    @Test
+    public void testException() throws Exception {
+        assertEquals(TEST_EXCEPTION, new ContentProviderResult(TEST_EXCEPTION).exception);
+    }
+}
diff --git a/tests/tests/content/src/android/content/cts/ContentValuesTest.java b/tests/tests/content/src/android/content/cts/ContentValuesTest.java
index ea5577d..e6bc2d6 100644
--- a/tests/tests/content/src/android/content/cts/ContentValuesTest.java
+++ b/tests/tests/content/src/android/content/cts/ContentValuesTest.java
@@ -551,4 +551,14 @@
             // expected, test success.
         }
     }
+
+    @Test
+    public void testIsEmpty() {
+        final ContentValues values = new ContentValues();
+        assertTrue(values.isEmpty());
+        values.put("k", "v");
+        assertFalse(values.isEmpty());
+        values.clear();
+        assertTrue(values.isEmpty());
+    }
 }
diff --git a/tests/tests/content/src/android/content/cts/MockBuggyProvider.java b/tests/tests/content/src/android/content/cts/MockBuggyProvider.java
new file mode 100644
index 0000000..47d83ee
--- /dev/null
+++ b/tests/tests/content/src/android/content/cts/MockBuggyProvider.java
@@ -0,0 +1,68 @@
+/*
+ * 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.content.cts;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Mocks a buggy provider which stalls on the {@link #getType(Uri)} call.
+ *
+ * see {@link BuggyProviderTest}
+ */
+public class MockBuggyProvider extends ContentProvider {
+    public static final String AUTHORITY = "android.content.cts.mockbuggyprovider";
+    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        return null;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        try {
+            TimeUnit.SECONDS.sleep(10); // stall for enough time such that an ANR is thrown
+        } catch (Exception ignore) { }
+        return "buggy";
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        return null;
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        return 0;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        return 0;
+    }
+}
diff --git a/tests/tests/content/src/android/content/cts/SharedPreferencesTest.java b/tests/tests/content/src/android/content/cts/SharedPreferencesTest.java
index 24b56c9..67ec257 100644
--- a/tests/tests/content/src/android/content/cts/SharedPreferencesTest.java
+++ b/tests/tests/content/src/android/content/cts/SharedPreferencesTest.java
@@ -47,8 +47,6 @@
 
     private File mPrefsFile;
 
-    private static volatile CountDownLatch sSharedPrefsListenerLatch;
-
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -356,35 +354,88 @@
 
     public void testSharedPrefsChangeListenerIsCalledOnCommit() throws InterruptedException {
         // Setup on change listener
+        final CountDownLatch latch = new CountDownLatch(2);
+        final SharedPreferences.OnSharedPreferenceChangeListener listener =
+                (sharedPreferences, key) -> latch.countDown();
         final SharedPreferences prefs = getPrefs();
-        prefs.registerOnSharedPreferenceChangeListener(
-                (sharedPreferences, key) -> sSharedPrefsListenerLatch.countDown());
 
-        // Verify listener is called for #putString
-        sSharedPrefsListenerLatch = new CountDownLatch(1);
-        prefs.edit().putString("test-key", "test-value").commit();
-        assertTrue(sSharedPrefsListenerLatch.await(1, TimeUnit.SECONDS));
+        try {
+            prefs.registerOnSharedPreferenceChangeListener(listener);
+            prefs.edit().putString("test-key", "test-value").commit(); // latch--
+            prefs.edit().remove("test-key").commit(); // latch--
 
-        // Verify listener is called for #remove
-        sSharedPrefsListenerLatch = new CountDownLatch(1);
-        prefs.edit().remove("test-key").commit();
-        assertTrue(sSharedPrefsListenerLatch.await(1, TimeUnit.SECONDS));
+            assertTrue("OnSharedPreferenceChangeListener was not fired on #commit",
+                    latch.await(10, TimeUnit.SECONDS));
+        } finally {
+            prefs.unregisterOnSharedPreferenceChangeListener(listener);
+        }
     }
 
     public void testSharedPrefsChangeListenerIsCalledOnApply() throws InterruptedException {
         // Setup on change listener
+        final CountDownLatch latch = new CountDownLatch(2);
+        final SharedPreferences.OnSharedPreferenceChangeListener listener =
+                (sharedPreferences, key) -> latch.countDown();
         final SharedPreferences prefs = getPrefs();
-        prefs.registerOnSharedPreferenceChangeListener(
-                (sharedPreferences, key) -> sSharedPrefsListenerLatch.countDown());
 
-        // Verify listener is called for #putString
-        sSharedPrefsListenerLatch = new CountDownLatch(1);
-        prefs.edit().putString("test-key", "test-value").apply();
-        assertTrue(sSharedPrefsListenerLatch.await(1, TimeUnit.SECONDS));
+        try {
+            prefs.registerOnSharedPreferenceChangeListener(listener);
+            prefs.edit().putString("test-key", "test-value").apply(); // latch--
+            prefs.edit().remove("test-key").apply(); // latch--
 
-        // Verify listener is called for #remove
-        sSharedPrefsListenerLatch = new CountDownLatch(1);
-        prefs.edit().remove("test-key").apply();
-        assertTrue(sSharedPrefsListenerLatch.await(1, TimeUnit.SECONDS));
+            assertTrue("OnSharedPreferenceChangeListener was not fired on #apply",
+                    latch.await(10, TimeUnit.SECONDS));
+        } finally {
+            prefs.unregisterOnSharedPreferenceChangeListener(listener);
+        }
+    }
+
+    public void testSharedPrefsChangeListenerIsCalledForClearOnCommit()
+            throws InterruptedException {
+        // Setup on change listener
+        final CountDownLatch latch = new CountDownLatch(1);
+        final SharedPreferences.OnSharedPreferenceChangeListener listener =
+                (sharedPreferences, key) -> {
+                    if (key == null) {
+                        latch.countDown();
+                    }
+                };
+        final SharedPreferences prefs = getPrefs();
+
+        try {
+            prefs.registerOnSharedPreferenceChangeListener(listener);
+            prefs.edit().putString("test-key", "test-value").commit();
+            assertEquals("test-value", prefs.getString("test-key", null));
+            prefs.edit().clear().commit(); // latch--
+
+            assertTrue("OnSharedPreferenceChangeListener was not fired for clear() on #commit",
+                    latch.await(10, TimeUnit.SECONDS));
+        } finally {
+            prefs.unregisterOnSharedPreferenceChangeListener(listener);
+        }
+    }
+
+    public void testSharedPrefsChangeListenerIsCalledForClearOnApply() throws InterruptedException {
+        // Setup on change listener
+        final CountDownLatch latch = new CountDownLatch(1);
+        final SharedPreferences.OnSharedPreferenceChangeListener listener =
+                (sharedPreferences, key) -> {
+                    if (key == null) {
+                        latch.countDown();
+                    }
+                };
+        final SharedPreferences prefs = getPrefs();
+
+        try {
+            prefs.registerOnSharedPreferenceChangeListener(listener);
+            prefs.edit().putString("test-key", "test-value").commit();
+            assertEquals("test-value", prefs.getString("test-key", null));
+            prefs.edit().clear().apply(); // latch--
+
+            assertTrue("OnSharedPreferenceChangeListener was not fired for clear() on #apply",
+                    latch.await(10, TimeUnit.SECONDS));
+        } finally {
+            prefs.unregisterOnSharedPreferenceChangeListener(listener);
+        }
     }
 }
diff --git a/tests/tests/content/src/android/content/res/cts/ResourcesTest.java b/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
index 7d0a276..81116e5 100644
--- a/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
+++ b/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
@@ -35,7 +35,6 @@
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.LocaleList;
-import android.platform.test.annotations.AppModeFull;
 import android.test.AndroidTestCase;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
@@ -46,8 +45,6 @@
 import android.view.View;
 import android.view.WindowManager;
 
-import androidx.test.InstrumentationRegistry;
-
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -338,6 +335,60 @@
         assertNull(mResources.getDrawable(R.drawable.fake_image_will_not_decode));
     }
 
+    public void testGetDrawable_ColorResource() {
+        final Drawable drawable = mResources.getDrawable(R.color.testcolor1, null);
+        assertTrue(drawable instanceof ColorDrawable);
+        assertEquals(
+                mResources.getColor(R.color.testcolor1, null),
+                ((ColorDrawable) drawable).getColor()
+        );
+    }
+
+    public void testGetDrawable_ColorStateListResource() {
+        final Drawable drawable = mResources.getDrawable(R.color.testcolor, null);
+        assertTrue(drawable instanceof ColorStateListDrawable);
+
+        final ColorStateList colorStateList = mResources.getColorStateList(
+                R.color.testcolor, null);
+        assertEquals(
+                colorStateList.getDefaultColor(),
+                ((ColorStateListDrawable) drawable).getColorStateList().getDefaultColor());
+    }
+
+    public void testGetDrawable_ColorStateListConfigurations() {
+        final Configuration dayConfiguration = new Configuration(mResources.getConfiguration());
+        final Configuration nightConfiguration = new Configuration(mResources.getConfiguration());
+
+        dayConfiguration.uiMode = dayConfiguration.uiMode
+                & (~Configuration.UI_MODE_NIGHT_MASK)
+                | Configuration.UI_MODE_NIGHT_NO;
+
+        nightConfiguration.uiMode = nightConfiguration.uiMode
+                & (~Configuration.UI_MODE_NIGHT_MASK)
+                | Configuration.UI_MODE_NIGHT_YES;
+
+        final ColorStateListDrawable dayDrawable = (ColorStateListDrawable) getContext()
+                .createConfigurationContext(dayConfiguration)
+                .getResources()
+                .getDrawable(R.color.testcolor_daynight, null);
+
+        final ColorStateListDrawable nightDrawable = (ColorStateListDrawable) getContext()
+                .createConfigurationContext(nightConfiguration)
+                .getResources()
+                .getDrawable(R.color.testcolor_daynight, null);
+
+        assertEquals(
+                mResources.getColor(android.R.color.white, null),
+                dayDrawable.getColorStateList().getDefaultColor());
+
+        assertEquals(
+                mResources.getColor(android.R.color.black, null),
+                nightDrawable.getColorStateList().getDefaultColor());
+
+        assertEquals(ActivityInfo.CONFIG_UI_MODE, dayDrawable.getChangingConfigurations());
+        assertEquals(ActivityInfo.CONFIG_UI_MODE, nightDrawable.getChangingConfigurations());
+    }
+
     public void testGetDrawable_StackOverflowErrorDrawable() {
         try {
             mResources.getDrawable(R.drawable.drawable_recursive);
@@ -958,14 +1009,12 @@
                 mResources.getFont(R.font.sample_bolditalic_family).getStyle());
     }
 
-    // TODO Figure out why it fails in the instant mode.
-    @AppModeFull
-    public void testComplextColorDrawableAttrInflation() {
-        Context context = InstrumentationRegistry.getTargetContext();
-        LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(
+    public void testComplexColorDrawableAttributeInflation() {
+        final LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(
                 Context.LAYOUT_INFLATER_SERVICE);
 
-        View view = layoutInflater.inflate(R.layout.complex_color_drawable_attr_layout, null);
+        final View view = layoutInflater.inflate(
+                R.layout.complex_color_drawable_attr_layout, null);
         assertTrue(view.getBackground() instanceof ColorStateListDrawable);
     }
 
diff --git a/tests/tests/database/src/android/database/sqlite/cts/SQLiteQueryBuilderTest.java b/tests/tests/database/src/android/database/sqlite/cts/SQLiteQueryBuilderTest.java
index 2475af8..f197968 100644
--- a/tests/tests/database/src/android/database/sqlite/cts/SQLiteQueryBuilderTest.java
+++ b/tests/tests/database/src/android/database/sqlite/cts/SQLiteQueryBuilderTest.java
@@ -40,6 +40,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
@@ -48,6 +49,8 @@
 @RunWith(AndroidJUnit4.class)
 public class SQLiteQueryBuilderTest {
     private SQLiteDatabase mDatabase;
+    private SQLiteQueryBuilder mStrictBuilder;
+
     private final String TEST_TABLE_NAME = "test";
     private final String EMPLOYEE_TABLE_NAME = "employee";
     private static final String DATABASE_FILE = "database_test.db";
@@ -59,6 +62,9 @@
         context.deleteDatabase(DATABASE_FILE);
         mDatabase = Objects.requireNonNull(
                 context.openOrCreateDatabase(DATABASE_FILE, Context.MODE_PRIVATE, null));
+
+        createEmployeeTable();
+        createStrictQueryBuilder();
     }
 
     @After
@@ -238,8 +244,6 @@
 
     @Test
     public void testQuery() {
-        createEmployeeTable();
-
         SQLiteQueryBuilder sqliteQueryBuilder = new SQLiteQueryBuilder();
         sqliteQueryBuilder.setTables("Employee");
         Cursor cursor = sqliteQueryBuilder.query(mDatabase,
@@ -314,8 +318,6 @@
 
     @Test
     public void testCancelableQuery_WhenNotCanceled_ReturnsResultSet() {
-        createEmployeeTable();
-
         CancellationSignal cancellationSignal = new CancellationSignal();
         SQLiteQueryBuilder sqliteQueryBuilder = new SQLiteQueryBuilder();
         sqliteQueryBuilder.setTables("Employee");
@@ -328,8 +330,6 @@
 
     @Test
     public void testCancelableQuery_WhenCanceledBeforeQuery_ThrowsImmediately() {
-        createEmployeeTable();
-
         CancellationSignal cancellationSignal = new CancellationSignal();
         SQLiteQueryBuilder sqliteQueryBuilder = new SQLiteQueryBuilder();
         sqliteQueryBuilder.setTables("Employee");
@@ -347,8 +347,6 @@
 
     @Test
     public void testCancelableQuery_WhenCanceledAfterQuery_ThrowsWhenExecuted() {
-        createEmployeeTable();
-
         CancellationSignal cancellationSignal = new CancellationSignal();
         SQLiteQueryBuilder sqliteQueryBuilder = new SQLiteQueryBuilder();
         sqliteQueryBuilder.setTables("Employee");
@@ -368,8 +366,6 @@
 
     @Test
     public void testCancelableQuery_WhenCanceledDueToContention_StopsWaitingAndThrows() {
-        createEmployeeTable();
-
         for (int i = 0; i < 5; i++) {
             final CancellationSignal cancellationSignal = new CancellationSignal();
             final Semaphore barrier1 = new Semaphore(0);
@@ -504,8 +500,6 @@
 
     @Test
     public void testUpdate() throws Exception {
-        createEmployeeTable();
-
         final ContentValues values = new ContentValues();
         values.put("name", "Anonymous");
         values.put("salary", 0);
@@ -525,8 +519,6 @@
 
     @Test
     public void testDelete() throws Exception {
-        createEmployeeTable();
-
         {
             final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
             qb.setTables("employee");
@@ -544,12 +536,7 @@
 
     @Test
     public void testStrictQuery() throws Exception {
-        createEmployeeTable();
-
-        final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
-        qb.setTables("employee");
-        qb.setStrict(true);
-        qb.appendWhere("month=2");
+        final SQLiteQueryBuilder qb = mStrictBuilder;
 
         // Should normally only be able to see one row
         try (Cursor c = qb.query(mDatabase, null, null, null, null, null, null)) {
@@ -578,16 +565,10 @@
 
     @Test
     public void testStrictUpdate() throws Exception {
-        createEmployeeTable();
+        final SQLiteQueryBuilder qb = mStrictBuilder;
 
         final ContentValues values = new ContentValues();
         values.put("name", "Anonymous");
-        values.put("salary", 0);
-
-        final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
-        qb.setTables("employee");
-        qb.setStrict(true);
-        qb.appendWhere("month=2");
 
         // Should normally only be able to update one row
         assertEquals(1, qb.update(mDatabase, values, null, null));
@@ -614,10 +595,7 @@
 
     @Test
     public void testStrictDelete() throws Exception {
-        final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
-        qb.setTables("employee");
-        qb.setStrict(true);
-        qb.appendWhere("month=2");
+        final SQLiteQueryBuilder qb = mStrictBuilder;
 
         // Should normally only be able to update one row
         createEmployeeTable();
@@ -647,6 +625,186 @@
         }
     }
 
+    private static final String[] COLUMNS_VALID = new String[] {
+            "_id",
+    };
+
+    private static final String[] COLUMNS_INVALID = new String[] {
+            "salary",
+            "MAX(salary)",
+            "undefined",
+            "(secret_column IN secret_table)",
+            "(SELECT secret_column FROM secret_table)",
+    };
+
+    @Test
+    public void testStrictQueryProjection() throws Exception {
+        for (String column : COLUMNS_VALID) {
+            assertStrictQueryValid(
+                    new String[] { column }, null, null, null, null, null, null);
+        }
+        for (String column : COLUMNS_INVALID) {
+            assertStrictQueryInvalid(
+                    new String[] { column }, null, null, null, null, null, null);
+        }
+    }
+
+    @Test
+    public void testStrictQueryWhere() throws Exception {
+        for (String column : COLUMNS_VALID) {
+            assertStrictQueryValid(
+                    null, column + ">0", null, null, null, null, null);
+            assertStrictQueryValid(
+                    null, "_id>" + column, null, null, null, null, null);
+        }
+        for (String column : COLUMNS_INVALID) {
+            assertStrictQueryInvalid(
+                    null, column + ">0", null, null, null, null, null);
+            assertStrictQueryInvalid(
+                    null, "_id>" + column, null, null, null, null, null);
+        }
+    }
+
+    @Test
+    public void testStrictQueryGroupBy() {
+        for (String column : COLUMNS_VALID) {
+            assertStrictQueryValid(
+                    null, null, null, column, null, null, null);
+            assertStrictQueryValid(
+                    null, null, null, "_id," + column, null, null, null);
+        }
+        for (String column : COLUMNS_INVALID) {
+            assertStrictQueryInvalid(
+                    null, null, null, column, null, null, null);
+            assertStrictQueryInvalid(
+                    null, null, null, "_id," + column, null, null, null);
+        }
+    }
+
+    @Test
+    public void testStrictQueryHaving() {
+        for (String column : COLUMNS_VALID) {
+            assertStrictQueryValid(
+                    null, null, null, "_id", column, null, null);
+        }
+        for (String column : COLUMNS_INVALID) {
+            assertStrictQueryInvalid(
+                    null, null, null, "_id", column, null, null);
+        }
+    }
+
+    @Test
+    public void testStrictQueryOrderBy() {
+        for (String column : COLUMNS_VALID) {
+            assertStrictQueryValid(
+                    null, null, null, null, null, column, null);
+            assertStrictQueryValid(
+                    null, null, null, null, null, column + " ASC", null);
+            assertStrictQueryValid(
+                    null, null, null, null, null, "_id COLLATE NOCASE ASC," + column, null);
+        }
+        for (String column : COLUMNS_INVALID) {
+            assertStrictQueryInvalid(
+                    null, null, null, null, null, column, null);
+            assertStrictQueryInvalid(
+                    null, null, null, null, null, column + " ASC", null);
+            assertStrictQueryInvalid(
+                    null, null, null, null, null, "_id COLLATE NOCASE ASC," + column, null);
+        }
+    }
+
+    @Test
+    public void testStrictQueryLimit() {
+        assertStrictQueryValid(
+                null, null, null, null, null, null, "32");
+        assertStrictQueryValid(
+                null, null, null, null, null, null, "0,32");
+        assertStrictQueryValid(
+                null, null, null, null, null, null, "32 OFFSET 0");
+
+        for (String column : COLUMNS_VALID) {
+            assertStrictQueryInvalid(
+                    null, null, null, null, null, null, column);
+        }
+        for (String column : COLUMNS_INVALID) {
+            assertStrictQueryInvalid(
+                    null, null, null, null, null, null, column);
+        }
+    }
+
+    @Test
+    public void testStrictInsertValues() throws Exception {
+        final ContentValues values = new ContentValues();
+        for (String column : COLUMNS_VALID) {
+            values.clear();
+            values.put(column, 42);
+            assertStrictInsertValid(values);
+        }
+        for (String column : COLUMNS_INVALID) {
+            values.clear();
+            values.put(column, 42);
+            assertStrictInsertInvalid(values);
+        }
+    }
+
+    @Test
+    public void testStrictUpdateValues() throws Exception {
+        final ContentValues values = new ContentValues();
+        for (String column : COLUMNS_VALID) {
+            values.clear();
+            values.put(column, 42);
+            assertStrictUpdateValid(values, null, null);
+        }
+        for (String column : COLUMNS_INVALID) {
+            values.clear();
+            values.put(column, 42);
+            assertStrictUpdateInvalid(values, null, null);
+        }
+    }
+
+    private void assertStrictInsertValid(ContentValues values) {
+        mStrictBuilder.insert(mDatabase, values);
+    }
+
+    private void assertStrictInsertInvalid(ContentValues values) {
+        try {
+            mStrictBuilder.insert(mDatabase, values);
+            fail(Arrays.asList(values).toString());
+        } catch (Exception expected) {
+        }
+    }
+
+    private void assertStrictUpdateValid(ContentValues values, String selection,
+            String[] selectionArgs) {
+        mStrictBuilder.update(mDatabase, values, selection, selectionArgs);
+    }
+
+    private void assertStrictUpdateInvalid(ContentValues values, String selection,
+            String[] selectionArgs) {
+        try {
+            mStrictBuilder.update(mDatabase, values, selection, selectionArgs);
+            fail(Arrays.asList(values, selection, selectionArgs).toString());
+        } catch (Exception expected) {
+        }
+    }
+
+    private void assertStrictQueryValid(String[] projectionIn, String selection,
+            String[] selectionArgs, String groupBy, String having, String sortOrder, String limit) {
+        try (Cursor c = mStrictBuilder.query(mDatabase, projectionIn, selection, selectionArgs,
+                groupBy, having, sortOrder, limit, null)) {
+        }
+    }
+
+    private void assertStrictQueryInvalid(String[] projectionIn, String selection,
+            String[] selectionArgs, String groupBy, String having, String sortOrder, String limit) {
+        try (Cursor c = mStrictBuilder.query(mDatabase, projectionIn, selection, selectionArgs,
+                groupBy, having, sortOrder, limit, null)) {
+            fail(Arrays.asList(projectionIn, selection, selectionArgs,
+                    groupBy, having, sortOrder, limit).toString());
+        } catch (Exception expected) {
+        }
+    }
+
     private void createEmployeeTable() {
         mDatabase.execSQL("DROP TABLE IF EXISTS employee;");
         mDatabase.execSQL("CREATE TABLE employee (_id INTEGER PRIMARY KEY, " +
@@ -664,4 +822,19 @@
         mDatabase.execSQL("INSERT INTO employee (name, month, salary) " +
                 "VALUES ('Jim', '3', '3500');");
     }
+
+    private void createStrictQueryBuilder() {
+        mStrictBuilder = new SQLiteQueryBuilder();
+        mStrictBuilder.setTables("employee");
+        mStrictBuilder.setStrict(true);
+        mStrictBuilder.setStrictColumns(true);
+        mStrictBuilder.setStrictGrammar(true);
+        mStrictBuilder.appendWhere("month=2");
+
+        final Map<String, String> projectionMap = new HashMap<>();
+        projectionMap.put("_id", "_id");
+        projectionMap.put("name", "name");
+        projectionMap.put("month", "month");
+        mStrictBuilder.setProjectionMap(projectionMap);
+    }
 }
diff --git a/tests/tests/dpi/OWNERS b/tests/tests/dpi/OWNERS
new file mode 100644
index 0000000..6d63a63
--- /dev/null
+++ b/tests/tests/dpi/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 25700
+adamp@google.com
diff --git a/tests/tests/externalservice/OWNERS b/tests/tests/externalservice/OWNERS
new file mode 100644
index 0000000..210a568
--- /dev/null
+++ b/tests/tests/externalservice/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 76427
+maco@google.com
+narayan@google.com
diff --git a/tests/tests/gesture/OWNERS b/tests/tests/gesture/OWNERS
new file mode 100644
index 0000000..6d63a63
--- /dev/null
+++ b/tests/tests/gesture/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 25700
+adamp@google.com
diff --git a/tests/tests/graphics/assets/passthrough_fsh.glsl b/tests/tests/graphics/assets/shaders/passthrough_fsh.glsl
similarity index 100%
rename from tests/tests/graphics/assets/passthrough_fsh.glsl
rename to tests/tests/graphics/assets/shaders/passthrough_fsh.glsl
diff --git a/tests/tests/graphics/assets/passthrough_fsh.spv b/tests/tests/graphics/assets/shaders/passthrough_fsh.spv
similarity index 100%
rename from tests/tests/graphics/assets/passthrough_fsh.spv
rename to tests/tests/graphics/assets/shaders/passthrough_fsh.spv
Binary files differ
diff --git a/tests/tests/graphics/assets/passthrough_vsh.glsl b/tests/tests/graphics/assets/shaders/passthrough_vsh.glsl
similarity index 100%
rename from tests/tests/graphics/assets/passthrough_vsh.glsl
rename to tests/tests/graphics/assets/shaders/passthrough_vsh.glsl
diff --git a/tests/tests/graphics/assets/passthrough_vsh.spv b/tests/tests/graphics/assets/shaders/passthrough_vsh.spv
similarity index 100%
rename from tests/tests/graphics/assets/passthrough_vsh.spv
rename to tests/tests/graphics/assets/shaders/passthrough_vsh.spv
Binary files differ
diff --git a/tests/tests/graphics/jni/VulkanTestHelpers.cpp b/tests/tests/graphics/jni/VulkanTestHelpers.cpp
index 17d952f..bfe94ee 100644
--- a/tests/tests/graphics/jni/VulkanTestHelpers.cpp
+++ b/tests/tests/graphics/jni/VulkanTestHelpers.cpp
@@ -647,7 +647,7 @@
   {
     AAsset *vertFile =
         AAssetManager_open(AAssetManager_fromJava(env, assetMgr),
-                           "passthrough_vsh.spv", AASSET_MODE_BUFFER);
+                           "shaders/passthrough_vsh.spv", AASSET_MODE_BUFFER);
     ASSERT(vertFile);
     size_t vertShaderLength = AAsset_getLength(vertFile);
     std::vector<uint8_t> vertShader;
@@ -658,7 +658,7 @@
 
     AAsset *pixelFile =
         AAssetManager_open(AAssetManager_fromJava(env, assetMgr),
-                           "passthrough_fsh.spv", AASSET_MODE_BUFFER);
+                           "shaders/passthrough_fsh.spv", AASSET_MODE_BUFFER);
     ASSERT(pixelFile);
     size_t pixelShaderLength = AAsset_getLength(pixelFile);
     std::vector<uint8_t> pixelShader;
diff --git a/tests/tests/graphics/jni/android_graphics_cts_BitmapTest.cpp b/tests/tests/graphics/jni/android_graphics_cts_BitmapTest.cpp
index d44c14f..ae9ecb2 100644
--- a/tests/tests/graphics/jni/android_graphics_cts_BitmapTest.cpp
+++ b/tests/tests/graphics/jni/android_graphics_cts_BitmapTest.cpp
@@ -37,7 +37,7 @@
     ASSERT_EQ(format, info.format);
 }
 
-static void validateNdkAccessAfterRecycle(JNIEnv* env, jclass, jobject jbitmap) {
+static void validateNdkAccessFails(JNIEnv* env, jclass, jobject jbitmap) {
     void* pixels = nullptr;
     int err = AndroidBitmap_lockPixels(env, jbitmap, &pixels);
     ASSERT_EQ(err, ANDROID_BITMAP_RESULT_JNI_EXCEPTION);
@@ -79,14 +79,29 @@
     return info.format;
 }
 
+static void testNullBitmap(JNIEnv* env, jclass) {
+    ASSERT_NE(nullptr, env);
+    AndroidBitmapInfo info;
+    int err = AndroidBitmap_getInfo(env, nullptr, &info);
+    ASSERT_EQ(err, ANDROID_BITMAP_RESULT_BAD_PARAMETER);
+
+    void* pixels = nullptr;
+    err = AndroidBitmap_lockPixels(env, nullptr, &pixels);
+    ASSERT_EQ(err, ANDROID_BITMAP_RESULT_BAD_PARAMETER);
+
+    err = AndroidBitmap_unlockPixels(env, nullptr);
+    ASSERT_EQ(err, ANDROID_BITMAP_RESULT_BAD_PARAMETER);
+}
+
 static JNINativeMethod gMethods[] = {
     { "nValidateBitmapInfo", "(Landroid/graphics/Bitmap;IIZ)V",
         (void*) validateBitmapInfo },
-    { "nValidateNdkAccessAfterRecycle", "(Landroid/graphics/Bitmap;)V",
-        (void*) validateNdkAccessAfterRecycle },
+    { "nValidateNdkAccessFails", "(Landroid/graphics/Bitmap;)V",
+        (void*) validateNdkAccessFails },
     { "nFillRgbaHwBuffer", "(Landroid/hardware/HardwareBuffer;)V",
         (void*) fillRgbaHardwareBuffer },
     { "nGetFormat", "(Landroid/graphics/Bitmap;)I", (void*) getFormat },
+    { "nTestNullBitmap", "()V", (void*) testNullBitmap },
 };
 
 int register_android_graphics_cts_BitmapTest(JNIEnv* env) {
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png
index e3be1d1..e67fce8 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png
index ce18075..8a48104 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_golden.png
index f991189..2145eec 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_repeat_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_repeat_golden.png
index f2798b4..5428052 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_repeat_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_repeat_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png
index aee71ec..70ee76a 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png
index a879e3c..0a195a8 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png
Binary files differ
diff --git a/tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java b/tests/tests/graphics/res/drawable/gradientdrawable_no_angle.xml
similarity index 67%
copy from tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java
copy to tests/tests/graphics/res/drawable/gradientdrawable_no_angle.xml
index 37276e2..de316cf 100644
--- a/tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java
+++ b/tests/tests/graphics/res/drawable/gradientdrawable_no_angle.xml
@@ -1,4 +1,5 @@
-/*
+<?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");
@@ -12,17 +13,11 @@
  * WITHOUT 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.tests.atomicinstall.testapp;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-public class MainActivity extends Activity {
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-    }
-}
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <gradient
+        android:endColor="#00ff00"
+        android:startColor="#ff0000"
+        android:type="linear" />
+</shape>
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
index a2025bd..232d087 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
@@ -1959,7 +1959,7 @@
         nValidateBitmapInfo(bitmap, 10, 20, true);
         bitmap.recycle();
         nValidateBitmapInfo(bitmap, 10, 20, true);
-        nValidateNdkAccessAfterRecycle(bitmap);
+        nValidateNdkAccessFails(bitmap);
     }
 
     @Test
@@ -2022,15 +2022,15 @@
         Debug.MemoryInfo meminfoStart = new Debug.MemoryInfo();
         Debug.MemoryInfo meminfoEnd = new Debug.MemoryInfo();
         int fdCount = -1;
+        // Do a warmup to reach steady-state memory usage
+        for (int i = 0; i < 50; i++) {
+            test.run();
+        }
+        runGcAndFinalizersSync();
+        Debug.getMemoryInfo(meminfoStart);
+        fdCount = getFdCount();
+        // Now run the test
         for (int i = 0; i < 2000; i++) {
-            if (i == 4) {
-                // Not really the "start" but by having done a couple
-                // we've fully initialized any state that may be required,
-                // so memory usage should be stable now
-                runGcAndFinalizersSync();
-                Debug.getMemoryInfo(meminfoStart);
-                fdCount = getFdCount();
-            }
             if (i % 100 == 5) {
                 assertNotLeaking(i, meminfoStart, meminfoEnd);
                 final int curFdCount = getFdCount();
@@ -2161,6 +2161,48 @@
         }
     }
 
+    @Test
+    public void testNdkFormats() {
+        for (ConfigToFormat pair : CONFIG_TO_FORMAT) {
+            Bitmap bm = Bitmap.createBitmap(10, 10, pair.config);
+            assertNotNull(bm);
+            int nativeFormat = nGetFormat(bm);
+            assertEquals("Config: " + pair.config, pair.format, nativeFormat);
+        }
+    }
+
+    @Test
+    public void testNdkFormatsHardware() {
+        for (ConfigToFormat pair : CONFIG_TO_FORMAT) {
+            Bitmap bm = Bitmap.createBitmap(10, 10, pair.config);
+            bm = bm.copy(Bitmap.Config.HARDWARE, false);
+
+            // ALPHA_8 is not supported in HARDWARE.
+            if (bm == null) {
+                assertEquals(Bitmap.Config.ALPHA_8, pair.config);
+                continue;
+            }
+            assertNotEquals(Bitmap.Config.ALPHA_8, pair.config);
+
+            int nativeFormat = nGetFormat(bm);
+            if (pair.config == Bitmap.Config.RGBA_F16) {
+                // It is possible the system does not support RGBA_F16 in HARDWARE.
+                // In that case, it will fall back to ARGB_8888.
+                assertTrue(nativeFormat == ANDROID_BITMAP_FORMAT_RGBA_8888
+                        || nativeFormat == ANDROID_BITMAP_FORMAT_RGBA_F16);
+            } else {
+                assertEquals("Config: " + pair.config, pair.format, nativeFormat);
+            }
+
+            nValidateNdkAccessFails(bm);
+        }
+    }
+
+    @Test
+    public void testNullBitmapNdk() {
+        nTestNullBitmap();
+    }
+
     private void strictModeTest(Runnable runnable) {
         StrictMode.ThreadPolicy originalPolicy = StrictMode.getThreadPolicy();
         StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
@@ -2177,12 +2219,35 @@
 
     private static native void nValidateBitmapInfo(Bitmap bitmap, int width, int height,
             boolean is565);
-    private static native void nValidateNdkAccessAfterRecycle(Bitmap bitmap);
+    private static native void nValidateNdkAccessFails(Bitmap bitmap);
 
     private static native void nFillRgbaHwBuffer(HardwareBuffer hwBuffer);
+    private static native void nTestNullBitmap();
 
     private static final int ANDROID_BITMAP_FORMAT_RGBA_8888 = 1;
     private static final int ANDROID_BITMAP_FORMAT_RGB_565 = 4;
+    private static final int ANDROID_BITMAP_FORMAT_A_8 = 8;
+    private static final int ANDROID_BITMAP_FORMAT_RGBA_F16 = 9;
+
+    private static class ConfigToFormat {
+        public final Config config;
+        public final int format;
+
+        ConfigToFormat(Config c, int f) {
+            this.config = c;
+            this.format = f;
+        }
+    }
+
+    private static final ConfigToFormat[] CONFIG_TO_FORMAT = new ConfigToFormat[] {
+        new ConfigToFormat(Bitmap.Config.ARGB_8888, ANDROID_BITMAP_FORMAT_RGBA_8888),
+        // ARGB_4444 is deprecated, and createBitmap converts to 8888.
+        new ConfigToFormat(Bitmap.Config.ARGB_4444, ANDROID_BITMAP_FORMAT_RGBA_8888),
+        new ConfigToFormat(Bitmap.Config.RGB_565, ANDROID_BITMAP_FORMAT_RGB_565),
+        new ConfigToFormat(Bitmap.Config.ALPHA_8, ANDROID_BITMAP_FORMAT_A_8),
+        new ConfigToFormat(Bitmap.Config.RGBA_F16, ANDROID_BITMAP_FORMAT_RGBA_F16),
+    };
+
     private static native int nGetFormat(Bitmap bitmap);
 
     private static HardwareBuffer createTestBuffer(int width, int height, boolean cpuAccess) {
diff --git a/tests/tests/graphics/src/android/graphics/cts/CameraTest.java b/tests/tests/graphics/src/android/graphics/cts/CameraTest.java
index ff8f77c..92f6322 100644
--- a/tests/tests/graphics/src/android/graphics/cts/CameraTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/CameraTest.java
@@ -91,8 +91,8 @@
         float[] f = new float[9];
         m2.getValues(f);
         assertArrayEquals(new float[] {
-                1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.0017361111f, 1.0f
-        }, f, 0.0f);
+                1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f
+        }, f, 0.0017361111f);
     }
 
     @Test
@@ -107,8 +107,8 @@
         float[] f = new float[9];
         m2.getValues(f);
         assertArrayEquals(new float[] {
-                0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0017361111f, 0.0f, 1.0f
-        }, f, 0.0f);
+                0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f
+        }, f, 0.0017361111f);
     }
 
     @Test
@@ -124,7 +124,7 @@
         m2.getValues(f);
         assertArrayEquals(new float[] {
                 0.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f
-        }, f, 0.0f);
+        }, f, 0.0017361111f);
     }
 
     @Test
diff --git a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
index 596e939..9ba5006 100644
--- a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
@@ -379,46 +379,44 @@
 
         boolean trueFalse[] = new boolean[] { true, false };
         for (Record record : RECORDS) {
-            for (SourceCreator f : mCreators) {
-                for (int allocator : ALLOCATORS) {
-                    for (boolean doCrop : trueFalse) {
-                        for (boolean doScale : trueFalse) {
-                            l.doCrop = doCrop;
-                            l.doScale = doScale;
-                            l.allocator = allocator;
-                            ImageDecoder.Source src = f.apply(record.resId);
-                            assertNotNull(src);
+            ImageDecoder.Source src = ImageDecoder.createSource(mRes, record.resId);
+            assertNotNull(src);
+            for (int allocator : ALLOCATORS) {
+                for (boolean doCrop : trueFalse) {
+                    for (boolean doScale : trueFalse) {
+                        l.doCrop = doCrop;
+                        l.doScale = doScale;
+                        l.allocator = allocator;
 
-                            Bitmap bm = null;
-                            try {
-                               bm = ImageDecoder.decodeBitmap(src, l);
-                            } catch (IOException e) {
-                                fail("Failed " + getAsResourceUri(record.resId) +
-                                        " with exception " + e);
-                            }
-                            assertNotNull(bm);
+                        Bitmap bm = null;
+                        try {
+                            bm = ImageDecoder.decodeBitmap(src, l);
+                        } catch (IOException e) {
+                            fail("Failed " + getAsResourceUri(record.resId)
+                                    + " with exception " + e);
+                        }
+                        assertNotNull(bm);
 
-                            switch (allocator) {
-                                case ImageDecoder.ALLOCATOR_SOFTWARE:
-                                // TODO: Once Bitmap provides access to its
-                                // SharedMemory, confirm that ALLOCATOR_SHARED_MEMORY
-                                // worked.
-                                case ImageDecoder.ALLOCATOR_SHARED_MEMORY:
-                                    assertNotEquals(Bitmap.Config.HARDWARE, bm.getConfig());
+                        switch (allocator) {
+                            case ImageDecoder.ALLOCATOR_SOFTWARE:
+                            // TODO: Once Bitmap provides access to its
+                            // SharedMemory, confirm that ALLOCATOR_SHARED_MEMORY
+                            // worked.
+                            case ImageDecoder.ALLOCATOR_SHARED_MEMORY:
+                                assertNotEquals(Bitmap.Config.HARDWARE, bm.getConfig());
 
-                                    if (!doScale && !doCrop) {
-                                        Bitmap reference = BitmapFactory.decodeResource(mRes,
-                                                record.resId, null);
-                                        assertNotNull(reference);
-                                        BitmapUtils.compareBitmaps(bm, reference);
-                                    }
-                                    break;
-                                default:
-                                    String name = getAsResourceUri(record.resId).toString();
-                                    assertEquals("image " + name + "; allocator: " + allocator,
-                                                 Bitmap.Config.HARDWARE, bm.getConfig());
-                                    break;
-                            }
+                                if (!doScale && !doCrop) {
+                                    Bitmap reference = BitmapFactory.decodeResource(mRes,
+                                            record.resId, null);
+                                    assertNotNull(reference);
+                                    BitmapUtils.compareBitmaps(bm, reference);
+                                }
+                                break;
+                            default:
+                                String name = getAsResourceUri(record.resId).toString();
+                                assertEquals("image " + name + "; allocator: " + allocator,
+                                             Bitmap.Config.HARDWARE, bm.getConfig());
+                                break;
                         }
                     }
                 }
@@ -519,32 +517,30 @@
         Listener l = new Listener();
         boolean trueFalse[] = new boolean[] { true, false };
         for (Record record : RECORDS) {
-            for (SourceCreator f : mCreators) {
-                for (boolean requireSoftware : trueFalse) {
-                    l.requireSoftware = requireSoftware;
-                    ImageDecoder.Source src = f.apply(record.resId);
-                    assertNotNull(src);
+            ImageDecoder.Source src = ImageDecoder.createSource(mRes, record.resId);
+            assertNotNull(src);
+            for (boolean requireSoftware : trueFalse) {
+                l.requireSoftware = requireSoftware;
 
-                    Bitmap bitmap = null;
-                    try {
-                        bitmap = ImageDecoder.decodeBitmap(src, l);
-                    } catch (IOException e) {
-                        fail("Failed with exception " + e);
-                    }
-                    assertNotNull(bitmap);
-                    assertFalse(bitmap.isMutable());
-                    if (requireSoftware) {
-                        assertNotEquals(Bitmap.Config.HARDWARE, bitmap.getConfig());
-                        for (int x = 0; x < bitmap.getWidth(); ++x) {
-                            for (int y = 0; y < bitmap.getHeight(); ++y) {
-                                int color = bitmap.getPixel(x, y);
-                                assertEquals("pixel at (" + x + ", " + y + ") does not match!",
-                                        color, Color.BLACK);
-                            }
+                Bitmap bitmap = null;
+                try {
+                    bitmap = ImageDecoder.decodeBitmap(src, l);
+                } catch (IOException e) {
+                    fail("Failed with exception " + e);
+                }
+                assertNotNull(bitmap);
+                assertFalse(bitmap.isMutable());
+                if (requireSoftware) {
+                    assertNotEquals(Bitmap.Config.HARDWARE, bitmap.getConfig());
+                    for (int x = 0; x < bitmap.getWidth(); ++x) {
+                        for (int y = 0; y < bitmap.getHeight(); ++y) {
+                            int color = bitmap.getPixel(x, y);
+                            assertEquals("pixel at (" + x + ", " + y + ") does not match!",
+                                    color, Color.BLACK);
                         }
-                    } else {
-                        assertEquals(bitmap.getConfig(), Bitmap.Config.HARDWARE);
                     }
+                } else {
+                    assertEquals(bitmap.getConfig(), Bitmap.Config.HARDWARE);
                 }
             }
         }
@@ -1219,47 +1215,41 @@
 
         float[] scales = new float[] { .0625f, .125f, .25f, .5f, .75f, 1.1f, 2.0f };
         for (Record record : RECORDS) {
-            for (SourceCreator f : mCreators) {
-                for (int j = 0; j < scales.length; ++j) {
-                    l.width  = (int) (scales[j] * record.width);
-                    l.height = (int) (scales[j] * record.height);
-
-                    ImageDecoder.Source src = f.apply(record.resId);
-
-                    try {
-                        Drawable drawable = ImageDecoder.decodeDrawable(src, l);
-                        assertEquals(l.width,  drawable.getIntrinsicWidth());
-                        assertEquals(l.height, drawable.getIntrinsicHeight());
-
-                        src = f.apply(record.resId);
-                        Bitmap bm = ImageDecoder.decodeBitmap(src, l);
-                        assertEquals(l.width,  bm.getWidth());
-                        assertEquals(l.height, bm.getHeight());
-                    } catch (IOException e) {
-                        fail("Failed " + getAsResourceUri(record.resId) + " with exception " + e);
-                    }
-                }
+            ImageDecoder.Source src = mCreators[0].apply(record.resId);
+            for (int j = 0; j < scales.length; ++j) {
+                l.width  = (int) (scales[j] * record.width);
+                l.height = (int) (scales[j] * record.height);
 
                 try {
-                    // Arbitrary square.
-                    l.width  = 50;
-                    l.height = 50;
-                    ImageDecoder.Source src = f.apply(record.resId);
                     Drawable drawable = ImageDecoder.decodeDrawable(src, l);
-                    assertEquals(50,  drawable.getIntrinsicWidth());
-                    assertEquals(50, drawable.getIntrinsicHeight());
+                    assertEquals(l.width,  drawable.getIntrinsicWidth());
+                    assertEquals(l.height, drawable.getIntrinsicHeight());
 
-                    // Swap width and height, for different scales.
-                    l.height = record.width;
-                    l.width  = record.height;
-                    src = f.apply(record.resId);
-                    drawable = ImageDecoder.decodeDrawable(src, l);
-                    assertEquals(record.height, drawable.getIntrinsicWidth());
-                    assertEquals(record.width,  drawable.getIntrinsicHeight());
+                    Bitmap bm = ImageDecoder.decodeBitmap(src, l);
+                    assertEquals(l.width,  bm.getWidth());
+                    assertEquals(l.height, bm.getHeight());
                 } catch (IOException e) {
-                    fail("Failed with exception " + e);
+                    fail("Failed " + getAsResourceUri(record.resId) + " with exception " + e);
                 }
             }
+
+            try {
+                // Arbitrary square.
+                l.width  = 50;
+                l.height = 50;
+                Drawable drawable = ImageDecoder.decodeDrawable(src, l);
+                assertEquals(50,  drawable.getIntrinsicWidth());
+                assertEquals(50, drawable.getIntrinsicHeight());
+
+                // Swap width and height, for different scales.
+                l.height = record.width;
+                l.width  = record.height;
+                drawable = ImageDecoder.decodeDrawable(src, l);
+                assertEquals(record.height, drawable.getIntrinsicWidth());
+                assertEquals(record.width,  drawable.getIntrinsicHeight());
+            } catch (IOException e) {
+                fail("Failed with exception " + e);
+            }
         }
     }
 
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
index 8650f00..5516727 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
@@ -751,6 +751,14 @@
     }
 
     @Test
+    public void testGradientNoAngle() {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        GradientDrawable drawable = (GradientDrawable)
+                context.getDrawable(R.drawable.gradientdrawable_no_angle);
+        assertEquals(Orientation.LEFT_RIGHT, drawable.getOrientation());
+    }
+
+    @Test
     public void testGradientDrawableOrientationConstructor() {
         GradientDrawable drawable = new GradientDrawable(Orientation.TOP_BOTTOM, null);
         assertEquals(Orientation.TOP_BOTTOM, drawable.getOrientation());
diff --git a/tests/tests/jni/OWNERS b/tests/tests/jni/OWNERS
new file mode 100644
index 0000000..29fea99
--- /dev/null
+++ b/tests/tests/jni/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 86431
+include /hostsidetests/classloaders/OWNERS
diff --git a/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
index 25f4b0e..415bef0 100644
--- a/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
+++ b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
@@ -47,7 +47,7 @@
 #endif
 
 static const std::string kSystemLibraryPath = "/system/" LIB_DIR;
-static const std::string kRuntimeApexLibraryPath = "/apex/com.android.runtime/" LIB_DIR;
+static const std::string kArtApexLibraryPath = "/apex/com.android.art/" LIB_DIR;
 static const std::string kVendorLibraryPath = "/vendor/" LIB_DIR;
 static const std::string kProductLibraryPath = "/product/" LIB_DIR;
 
@@ -377,7 +377,7 @@
   // These paths should be tested too - this is because apps may rely on some
   // libraries being available there.
   system_library_search_paths.insert(kSystemLibraryPath);
-  system_library_search_paths.insert(kRuntimeApexLibraryPath);
+  system_library_search_paths.insert(kArtApexLibraryPath);
 
   if (!check_path(env, clazz, kSystemLibraryPath, system_library_search_paths,
                   system_public_libraries,
@@ -391,7 +391,7 @@
   bool check_absence = !android::base::GetBoolProperty("ro.vndk.lite", false);
 
   // Check the runtime libraries.
-  if (!check_path(env, clazz, kRuntimeApexLibraryPath, {kRuntimeApexLibraryPath},
+  if (!check_path(env, clazz, kArtApexLibraryPath, {kArtApexLibraryPath},
                   runtime_public_libraries,
                   // System.loadLibrary("icuuc") would fail since a copy exists in /system.
                   // TODO(b/124218500): Change to true when the bug is resolved.
diff --git a/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java b/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
index 439284d..e8d23fa 100644
--- a/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
+++ b/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
@@ -45,7 +45,6 @@
     private final static Pattern EXTENSION_CONFIG_FILE_PATTERN = Pattern.compile(
             "public\\.libraries-([A-Za-z0-9\\-_.]+)\\.txt");
     private final static String VENDOR_CONFIG_FILE = "/vendor/etc/public.libraries.txt";
-    private final static String RUNTIME_APEX_DIR = "/apex/com.android.runtime";
     private final static String[] PUBLIC_SYSTEM_LIBRARIES = {
         "libaaudio.so",
         "libamidi.so",
@@ -73,8 +72,8 @@
         "libz.so"
     };
 
-    // Libraries listed in public.libraries.android.txt, located in RUNTIME_APEX_DIR path
-    private final static String[] PUBLIC_RUNTIME_LIBRARIES = {
+    // Libraries listed in public.libraries.android.txt, located in ART_APEX_DIR path
+    private final static String[] PUBLIC_ART_LIBRARIES = {
         "libicui18n.so",
         "libicuuc.so",
     };
@@ -154,7 +153,7 @@
 
     public static String runAccessibilityTest() throws IOException {
         List<String> systemLibs = new ArrayList<>();
-        List<String> runtimeApexLibs = new ArrayList<>();
+        List<String> artApexLibs = new ArrayList<>();
 
         Collections.addAll(systemLibs, PUBLIC_SYSTEM_LIBRARIES);
 
@@ -163,7 +162,7 @@
             systemLibs.add(WEBVIEW_PLAT_SUPPORT_LIB);
         }
 
-        Collections.addAll(runtimeApexLibs, PUBLIC_RUNTIME_LIBRARIES);
+        Collections.addAll(artApexLibs, PUBLIC_ART_LIBRARIES);
 
         // Check if public.libraries.txt contains libs other than the
         // public system libs (NDK libs).
@@ -197,7 +196,7 @@
         }
 
         return runAccessibilityTestImpl(systemLibs.toArray(new String[systemLibs.size()]),
-                                        runtimeApexLibs.toArray(new String[runtimeApexLibs.size()]),
+                                        artApexLibs.toArray(new String[artApexLibs.size()]),
                                         vendorLibs.toArray(new String[vendorLibs.size()]),
                                         productLibs.toArray(new String[productLibs.size()]));
     }
@@ -355,7 +354,7 @@
         try {
             List<String> publicLibs = new ArrayList<>();
             Collections.addAll(publicLibs, PUBLIC_SYSTEM_LIBRARIES);
-            Collections.addAll(publicLibs, PUBLIC_RUNTIME_LIBRARIES);
+            Collections.addAll(publicLibs, PUBLIC_ART_LIBRARIES);
             error = readExtensionConfigFiles(PUBLIC_CONFIG_DIR, publicLibs);
             if (error != null) return error;
             error = readExtensionConfigFiles(PRODUCT_CONFIG_DIR, publicLibs);
diff --git a/tests/tests/keystore/OWNERS b/tests/tests/keystore/OWNERS
new file mode 100644
index 0000000..30685c8
--- /dev/null
+++ b/tests/tests/keystore/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 36824
+swillden@google.com
+jdanis@google.com
+jbires@google.com
diff --git a/tests/tests/location/OWNERS b/tests/tests/location/OWNERS
index e875815..99ad4f3 100644
--- a/tests/tests/location/OWNERS
+++ b/tests/tests/location/OWNERS
@@ -1,5 +1,6 @@
 # Bug component: 32850
-wyattriley@google.com
-patrickor@google.com
-aadmal@google.com
+lifu@google.com
 sooniln@google.com
+weiwa@google.com
+wyattriley@google.com
+yuhany@google.com
diff --git a/tests/tests/location/src/android/location/cts/LocationManagerTest.java b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
index 89ee843..a46b19c 100644
--- a/tests/tests/location/src/android/location/cts/LocationManagerTest.java
+++ b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
@@ -856,21 +856,9 @@
         assertTrue(mManager.isProviderEnabled(TEST_MOCK_PROVIDER_NAME));
 
         mManager.clearTestProviderEnabled(TEST_MOCK_PROVIDER_NAME);
-        //onSetEnabled in LMS is handle in thread, it's not synchronized ,
-        //need add delay here or will cause check failed
-        try {
-            Thread.sleep(100);
-        } catch (Exception e) {
-            Log.e(TAG, "fail in testIsProviderEnabled");
-        }
         assertFalse(mManager.isProviderEnabled(TEST_MOCK_PROVIDER_NAME));
 
         mManager.setTestProviderEnabled(TEST_MOCK_PROVIDER_NAME, true);
-        try {
-            Thread.sleep(100);
-        } catch (Exception e) {
-            Log.e(TAG, "fail in testIsProviderEnabled");
-        }
         assertTrue(mManager.isProviderEnabled(TEST_MOCK_PROVIDER_NAME));
 
         try {
diff --git a/tests/tests/media/Android.bp b/tests/tests/media/Android.bp
index a15b1ec..eec4cd8 100644
--- a/tests/tests/media/Android.bp
+++ b/tests/tests/media/Android.bp
@@ -40,7 +40,6 @@
         "truth-prebuilt",
         "mockito-target-minus-junit4",
         "androidx.heifwriter_heifwriter",
-        "androidx.media2_media2",
     ],
     jni_libs: [
         "libaudio_jni",
diff --git a/tests/tests/media/src/android/media/cts/AudioManagerTest.java b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
index 4aa72d0..659bea9 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -178,13 +178,6 @@
 
     @AppModeFull(reason = "Instant apps cannot hold android.permission.MODIFY_AUDIO_SETTINGS")
     public void testMicrophoneMuteIntent() throws Exception {
-        // Skip this test for automotive.
-        // This tests listens for ACTION_MICROPHONE_MUTE_CHANGED which AudioService only broadcasts
-        // to system user. Automotive devices, which runs in secondary user, will fail this test.
-        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
-            return;
-        }
-
         final MyBlockingIntentReceiver receiver = new MyBlockingIntentReceiver(
                 AudioManager.ACTION_MICROPHONE_MUTE_CHANGED);
         final boolean initialMicMute = mAudioManager.isMicrophoneMute();
diff --git a/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java b/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
index 6256175..21f7a1b 100644
--- a/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
@@ -33,6 +33,7 @@
 import android.media.AudioPlaybackConfiguration;
 
 import com.android.compatibility.common.util.CtsAndroidTestCase;
+import com.android.internal.annotations.GuardedBy;
 
 import java.lang.reflect.Method;
 import java.util.ArrayList;
@@ -361,16 +362,16 @@
     }
 
     private static class MyAudioPlaybackCallback extends AudioManager.AudioPlaybackCallback {
-        private int mCalled = 0;
-        private int mNbConfigs = 0;
-        private List<AudioPlaybackConfiguration> mConfigs;
         private final Object mCbLock = new Object();
+        @GuardedBy("mCbLock")
+        private int mCalled;
+        @GuardedBy("mCbLock")
+        private List<AudioPlaybackConfiguration> mConfigs;
 
         void reset() {
             synchronized (mCbLock) {
                 mCalled = 0;
-                mNbConfigs = 0;
-                mConfigs.clear();
+                mConfigs = new ArrayList<AudioPlaybackConfiguration>();
             }
         }
 
@@ -381,9 +382,7 @@
         }
 
         int getNbConfigs() {
-            synchronized (mCbLock) {
-                return mNbConfigs;
-            }
+            return getConfigs().size();
         }
 
         List<AudioPlaybackConfiguration> getConfigs() {
@@ -393,13 +392,13 @@
         }
 
         MyAudioPlaybackCallback() {
+            reset();
         }
 
         @Override
         public void onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configs) {
             synchronized (mCbLock) {
                 mCalled++;
-                mNbConfigs = configs.size();
                 mConfigs = configs;
             }
         }
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordAppOpTest.java b/tests/tests/media/src/android/media/cts/AudioRecordAppOpTest.java
index 6ead39e..70da3d3 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordAppOpTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordAppOpTest.java
@@ -16,7 +16,7 @@
 
 package android.media.cts;
 
-import static android.app.AppOpsManager.OP_RECORD_AUDIO;
+import static android.app.AppOpsManager.OPSTR_RECORD_AUDIO;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -76,11 +76,12 @@
                     .build();
 
             // The app op should be reported as not started
-            assertThat(appOpsManager.isOperationActive(OP_RECORD_AUDIO,
+            assertThat(appOpsManager.isOpActive(OPSTR_RECORD_AUDIO,
                     uid, packageName)).isFalse();
 
             // Start watching for app op active
-            appOpsManager.startWatchingActive(new int[] {OP_RECORD_AUDIO}, listener);
+            appOpsManager.startWatchingActive(new String[] { OPSTR_RECORD_AUDIO },
+                    getContext().getMainExecutor(), listener);
 
             // Start recording
             candidateRecorder.startRecording();
@@ -88,11 +89,11 @@
 
             // The app op should start
             verify(listener, timeout(APP_OP_CHANGE_TIMEOUT_MILLIS)
-                    .only()).onOpActiveChanged(eq(OP_RECORD_AUDIO),
+                    .only()).onOpActiveChanged(eq(OPSTR_RECORD_AUDIO),
                     eq(uid), eq(packageName), eq(true));
 
             // The app op should be reported as started
-            assertThat(appOpsManager.isOperationActive(OP_RECORD_AUDIO,
+            assertThat(appOpsManager.isOpActive(OPSTR_RECORD_AUDIO,
                     uid, packageName)).isTrue();
 
 
@@ -106,11 +107,11 @@
 
             // The app op should stop
             verify(listener, timeout(APP_OP_CHANGE_TIMEOUT_MILLIS)
-                    .only()).onOpActiveChanged(eq(OP_RECORD_AUDIO),
+                    .only()).onOpActiveChanged(eq(OPSTR_RECORD_AUDIO),
                     eq(uid), eq(packageName), eq(false));
 
             // The app op should be reported as not started
-            assertThat(appOpsManager.isOperationActive(OP_RECORD_AUDIO,
+            assertThat(appOpsManager.isOpActive(OPSTR_RECORD_AUDIO,
                     uid, packageName)).isFalse();
         } finally {
             if (recorder != null) {
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordTest.java b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
index 90a5a3f..ddd1be7 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
@@ -35,6 +35,7 @@
 import android.media.AudioTrack;
 import android.media.MediaRecorder;
 import android.media.MediaSyncEvent;
+import android.media.MicrophoneDirection;
 import android.media.MicrophoneInfo;
 import android.media.cts.AudioRecordingConfigurationTest.MyAudioRecordingCallback;
 import android.os.Handler;
@@ -1649,4 +1650,46 @@
     private static Context getContext() {
         return InstrumentationRegistry.getInstrumentation().getTargetContext();
     }
+
+    /*
+     * Microphone Direction API tests
+     */
+    public void testSetPreferredMicrophoneDirection() {
+        if (!hasMicrophone()) {
+            return;
+        }
+
+        try {
+            boolean success =
+                    mAudioRecord.setPreferredMicrophoneDirection(
+                            MicrophoneDirection.MIC_DIRECTION_TOWARDS_USER);
+
+            // Can't actually test this as HAL may not have implemented it
+            // Just verify that it doesn't crash or throw an exception
+            // assertTrue(success);
+        } catch (Exception ex) {
+            Log.e(TAG, "testSetPreferredMicrophoneDirection() exception:" + ex);
+            assertTrue(false);
+        }
+        return;
+    }
+
+    public void testSetPreferredMicrophoneFieldDimension() {
+        if (!hasMicrophone()) {
+            return;
+        }
+
+        try {
+            boolean success = mAudioRecord.setPreferredMicrophoneFieldDimension(1.0f);
+
+            // Can't actually test this as HAL may not have implemented it
+            // Just verify that it doesn't crash or throw an exception
+            // assertTrue(success);
+        } catch (Exception ex) {
+            Log.e(TAG, "testSetPreferredMicrophoneFieldDimension() exception:" + ex);
+            assertTrue(false);
+        }
+        return;
+    }
+
 }
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index fa20770..09e0014 100755
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -2652,6 +2652,46 @@
         }
     }
 
+    @Test
+    public void testMaxAudioTracks() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+
+        // The framework must not give more than MAX_TRACKS tracks per UID.
+        final int MAX_TRACKS = 512; // an arbitrary large number > 40
+        final int FRAMES = 1024;
+
+        final AudioTrack[] tracks = new AudioTrack[MAX_TRACKS];
+        final AudioTrack.Builder builder = new AudioTrack.Builder()
+            .setAudioFormat(new AudioFormat.Builder()
+                .setEncoding(AudioFormat.ENCODING_PCM_8BIT)
+                .setSampleRate(8000)
+                .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
+                .build())
+            .setBufferSizeInBytes(FRAMES)
+            .setTransferMode(AudioTrack.MODE_STATIC);
+
+        int n = 0;
+        try {
+            for (; n < MAX_TRACKS; ++n) {
+                tracks[n] = builder.build();
+            }
+        } catch (UnsupportedOperationException e) {
+            ; // we expect this when we hit the uid track limit.
+        }
+
+        // release all the tracks created.
+        for (int i = 0; i < n; ++i) {
+            tracks[i].release();
+            tracks[i] = null;
+        }
+        Log.d(TAG, "" + n + " tracks were created");
+        assertTrue("should be able to create at least one static track", n > 0);
+        assertTrue("was able to create " + MAX_TRACKS + " tracks - that's too many!",
+            n < MAX_TRACKS);
+    }
+
 /* Do not run in JB-MR1. will be re-opened in the next platform release.
     public void testResourceLeakage() throws Exception {
         final int BUFFER_SIZE = 600 * 1024;
diff --git a/tests/tests/media/src/android/media/cts/ExifInterfaceTest.java b/tests/tests/media/src/android/media/cts/ExifInterfaceTest.java
index d25c76a..660094a 100644
--- a/tests/tests/media/src/android/media/cts/ExifInterfaceTest.java
+++ b/tests/tests/media/src/android/media/cts/ExifInterfaceTest.java
@@ -21,6 +21,7 @@
 import android.media.ExifInterface;
 import android.os.Environment;
 import android.os.FileUtils;
+import android.os.StrictMode;
 import android.platform.test.annotations.AppModeFull;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -481,6 +482,16 @@
         // about writing back in here.
     }
 
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
+                .detectUnbufferedIo()
+                .penaltyDeath()
+                .build());
+    }
+
     public void testReadExifDataFromExifByteOrderIIJpeg() throws Throwable {
         testExifInterfaceForJpeg(EXIF_BYTE_ORDER_II_JPEG, R.array.exifbyteorderii_jpg);
     }
diff --git a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
index 94f334c..f7231fd 100644
--- a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
@@ -40,7 +40,8 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.compatibility.common.util.MediaUtils;
-
+import java.io.FileOutputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
@@ -246,6 +247,10 @@
     }
 
     private void testThumbnail(int resId) {
+        testThumbnail(resId, null /*outPath*/);
+    }
+
+    private void testThumbnail(int resId, String outPath) {
         if (!MediaUtils.hasCodecForResourceAndDomain(getContext(), resId, "video/")) {
             MediaUtils.skipTest("no video codecs for resource");
             return;
@@ -263,7 +268,29 @@
             fail("Unable to open file");
         }
 
-        assertNotNull(retriever.getFrameAtTime(-1 /* timeUs (any) */));
+        Bitmap thumbnail = retriever.getFrameAtTime(-1 /* timeUs (any) */);
+        retriever.release();
+
+        assertNotNull(thumbnail);
+
+        // save output file if needed
+        if (outPath != null) {
+            FileOutputStream out = null;
+            try {
+                out = new FileOutputStream(outPath);
+            } catch (FileNotFoundException e) {
+                fail("Can't open output file");
+            }
+
+            thumbnail.compress(Bitmap.CompressFormat.PNG, 100, out);
+
+            try {
+                out.flush();
+                out.close();
+            } catch (IOException e) {
+                fail("Can't close file");
+            }
+        }
     }
 
     public void testThumbnailH264() {
@@ -290,6 +317,17 @@
         testThumbnail(R.raw.bbb_s1_720x480_mp4_hevc_mp3_1600kbps_30fps_aac_he_6ch_240kbps_48000hz);
     }
 
+    public void testThumbnailVP9Hdr() {
+        testThumbnail(R.raw.video_1280x720_vp9_hdr_static_3mbps);
+    }
+
+    public void testThumbnailAV1Hdr() {
+        testThumbnail(R.raw.video_1280x720_av1_hdr_static_3mbps);
+    }
+
+    public void testThumbnailHDR10() {
+        testThumbnail(R.raw.video_1280x720_hevc_hdr10_static_3mbps);
+    }
 
     /**
      * The following tests verifies MediaMetadataRetriever.getFrameAtTime behavior.
diff --git a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
index 70d5c20..f929e4b 100644
--- a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
@@ -37,6 +37,7 @@
 import android.media.MediaRecorder;
 import android.media.MediaRecorder.OnErrorListener;
 import android.media.MediaRecorder.OnInfoListener;
+import android.media.MicrophoneDirection;
 import android.media.MicrophoneInfo;
 import android.media.cts.AudioRecordingConfigurationTest.MyAudioRecordingCallback;
 import android.opengl.GLES20;
@@ -1703,5 +1704,45 @@
         config.isClientSilenced();
     }
 
+    /*
+     * Microphone Direction API tests
+     */
+    public void testSetPreferredMicrophoneDirection() {
+        if (!hasMicrophone()) {
+            return;
+        }
+
+        try {
+            boolean succecss =
+                    mMediaRecorder.setPreferredMicrophoneDirection(
+                            MicrophoneDirection.MIC_DIRECTION_TOWARDS_USER);
+
+            // Can't actually test this as HAL may not have implemented it
+            // Just verify that it doesn't crash or throw an exception
+            // assertTrue(succecss);
+        }  catch (Exception ex) {
+            Log.e(TAG, "testSetPreferredMicrophoneDirection() exception:" + ex);
+            assertTrue(false);
+        }
+        return;
+    }
+
+    public void testSetPreferredMicrophoneFieldDimension() {
+        if (!hasMicrophone()) {
+            return;
+        }
+
+        try {
+            boolean succecss = mMediaRecorder.setPreferredMicrophoneFieldDimension(1.0f);
+
+            // Can't actually test this as HAL may not have implemented it
+            // Just verify that it doesn't crash or throw an exception
+            // assertTrue(succecss);
+        }  catch (Exception ex) {
+            Log.e(TAG, "testSetPreferredMicrophoneFieldDimension() exception:" + ex);
+            assertTrue(false);
+        }
+        return;
+    }
 
 }
diff --git a/tests/tests/media/src/android/media/cts/MediaScannerConnectionTest.java b/tests/tests/media/src/android/media/cts/MediaScannerConnectionTest.java
index 8ca7df7..f46bd24 100644
--- a/tests/tests/media/src/android/media/cts/MediaScannerConnectionTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaScannerConnectionTest.java
@@ -76,13 +76,10 @@
         mMediaScannerConnection.connect();
         checkConnectionState(true);
 
-        assertTrue(mMediaScannerConnection.mIsOnServiceConnectedCalled);
         mMediaScannerConnection.disconnect();
 
         checkConnectionState(false);
 
-        // FIXME: onServiceDisconnected is not called.
-        assertFalse(mMediaScannerConnection.mIsOnServiceDisconnectedCalled);
         mMediaScannerConnection.connect();
 
         checkConnectionState(true);
@@ -117,25 +114,9 @@
     }
 
     class MockMediaScannerConnection extends MediaScannerConnection {
-
-        public boolean mIsOnServiceConnectedCalled;
-        public boolean mIsOnServiceDisconnectedCalled;
         public MockMediaScannerConnection(Context context, MediaScannerConnectionClient client) {
             super(context, client);
         }
-
-        @Override
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            super.onServiceConnected(className, service);
-            mIsOnServiceConnectedCalled = true;
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName className) {
-            super.onServiceDisconnected(className);
-            mIsOnServiceDisconnectedCalled = true;
-            // this is not called.
-        }
     }
 
     class MockMediaScannerConnectionClient implements MediaScannerConnectionClient {
diff --git a/tests/tests/midi/OWNERS b/tests/tests/midi/OWNERS
new file mode 100644
index 0000000..e78e1d0
--- /dev/null
+++ b/tests/tests/midi/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 1344
+philburk@google.com
diff --git a/tests/tests/multiuser/OWNERS b/tests/tests/multiuser/OWNERS
new file mode 100644
index 0000000..2b344eb
--- /dev/null
+++ b/tests/tests/multiuser/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 71510
+include /tests/app/OWNERS
+
+bookatz@google.com
diff --git a/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp b/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
index 91949db..42d3057 100644
--- a/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
+++ b/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
@@ -1556,10 +1556,7 @@
             }
         }
     } else {
-        // TODO(b/123042748): The fact that glEGLImageTargetTexStorageEXT does not work for YUV
-        //                    textures is a bug. The condition for the target should be removed
-        //                    once the bug is fixed.
-        if (HasGLExtension("GL_EXT_EGL_image_storage") && mTexTarget != GL_TEXTURE_EXTERNAL_OES) {
+        if (HasGLExtension("GL_EXT_EGL_image_storage")) {
             glEGLImageTargetTexStorageEXT(mTexTarget, static_cast<GLeglImageOES>(mEGLImage),
                                           nullptr);
         } else {
diff --git a/tests/tests/nativemedia/sl/OWNERS b/tests/tests/nativemedia/sl/OWNERS
new file mode 100644
index 0000000..38b4a35
--- /dev/null
+++ b/tests/tests/nativemedia/sl/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 1344
+jmtrivi@google.com
diff --git a/tests/tests/nativemedia/xa/OWNERS b/tests/tests/nativemedia/xa/OWNERS
new file mode 100644
index 0000000..90b75e4
--- /dev/null
+++ b/tests/tests/nativemedia/xa/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 1344
+include ../sl/OWNERS
diff --git a/tests/tests/nativemidi/OWNERS b/tests/tests/nativemidi/OWNERS
new file mode 100644
index 0000000..ce5adf1
--- /dev/null
+++ b/tests/tests/nativemidi/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 48436
+pmclean@google.com
\ No newline at end of file
diff --git a/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
index 9a6a7de..dc5e5af 100644
--- a/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
+++ b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
@@ -222,6 +222,10 @@
 
     @After
     public void tearDown() throws Exception {
+        if (!hasMidiSupport()) {
+            Assert.assertTrue("FEATURE_MIDI Not Supported.", false);
+            return; // Not supported so don't test it.
+        }
         tearDownEchoServer();
 
         Log.i(TAG, "++ tearDown()");
@@ -250,6 +254,10 @@
     public void test_B_SendData() throws Exception {
         Log.i(TAG, "++++ test_B_SendData() this:" + System.identityHashCode(this));
 
+        if (!hasMidiSupport()) {
+            return; // Nothing to test
+        }
+
         Assert.assertEquals("Didn't start with 0 sends", 0, getNumSends(mTestContext));
         Assert.assertEquals("Didn't start with 0 bytes sent", 0, getNumBytesSent(mTestContext));
 
diff --git a/tests/tests/netsecpolicy/OWNERS b/tests/tests/netsecpolicy/OWNERS
new file mode 100644
index 0000000..fdd42d8
--- /dev/null
+++ b/tests/tests/netsecpolicy/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 36824
+include ../networksecurityconfig/OWNERS
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-attributes/OWNERS b/tests/tests/networksecurityconfig/networksecurityconfig-attributes/OWNERS
deleted file mode 100644
index 7705bfe..0000000
--- a/tests/tests/networksecurityconfig/networksecurityconfig-attributes/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-# Inherits OWNERS from parent directory.
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/OWNERS b/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/OWNERS
deleted file mode 100644
index 7705bfe..0000000
--- a/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-# Inherits OWNERS from parent directory.
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/OWNERS b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/OWNERS
deleted file mode 100644
index 7705bfe..0000000
--- a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-# Inherits OWNERS from parent directory.
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/OWNERS b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/OWNERS
deleted file mode 100644
index 7705bfe..0000000
--- a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-# Inherits OWNERS from parent directory.
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/OWNERS b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/OWNERS
deleted file mode 100644
index 7705bfe..0000000
--- a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-# Inherits OWNERS from parent directory.
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/OWNERS b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/OWNERS
deleted file mode 100644
index 7705bfe..0000000
--- a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-# Inherits OWNERS from parent directory.
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/OWNERS b/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/OWNERS
deleted file mode 100644
index 7705bfe..0000000
--- a/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-# Inherits OWNERS from parent directory.
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/OWNERS b/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/OWNERS
deleted file mode 100644
index 7705bfe..0000000
--- a/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-# Inherits OWNERS from parent directory.
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/OWNERS b/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/OWNERS
deleted file mode 100644
index 7705bfe..0000000
--- a/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-# Inherits OWNERS from parent directory.
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/OWNERS b/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/OWNERS
deleted file mode 100644
index 7705bfe..0000000
--- a/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-# Inherits OWNERS from parent directory.
diff --git a/tests/tests/notificationlegacy/notificationlegacy20/OWNERS b/tests/tests/notificationlegacy/notificationlegacy20/OWNERS
deleted file mode 100644
index d6078e0..0000000
--- a/tests/tests/notificationlegacy/notificationlegacy20/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# DO NOT DELETE: OWNERS is intended to be empty as module owner and bug component are defined
-# in parent directory. This file is needed as a placeholder for verification purpose as we
-# need to tell the difference between "no owner is specified for this module" vs "explicitly
-# defer to parent OWNERS file"
diff --git a/tests/tests/notificationlegacy/notificationlegacy27/OWNERS b/tests/tests/notificationlegacy/notificationlegacy27/OWNERS
deleted file mode 100644
index d6078e0..0000000
--- a/tests/tests/notificationlegacy/notificationlegacy27/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# DO NOT DELETE: OWNERS is intended to be empty as module owner and bug component are defined
-# in parent directory. This file is needed as a placeholder for verification purpose as we
-# need to tell the difference between "no owner is specified for this module" vs "explicitly
-# defer to parent OWNERS file"
diff --git a/tests/tests/notificationlegacy/notificationlegacy28/OWNERS b/tests/tests/notificationlegacy/notificationlegacy28/OWNERS
deleted file mode 100644
index d6078e0..0000000
--- a/tests/tests/notificationlegacy/notificationlegacy28/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# DO NOT DELETE: OWNERS is intended to be empty as module owner and bug component are defined
-# in parent directory. This file is needed as a placeholder for verification purpose as we
-# need to tell the difference between "no owner is specified for this module" vs "explicitly
-# defer to parent OWNERS file"
diff --git a/tests/tests/notificationlegacy/notificationlegacy29/OWNERS b/tests/tests/notificationlegacy/notificationlegacy29/OWNERS
deleted file mode 100644
index d6078e0..0000000
--- a/tests/tests/notificationlegacy/notificationlegacy29/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# DO NOT DELETE: OWNERS is intended to be empty as module owner and bug component are defined
-# in parent directory. This file is needed as a placeholder for verification purpose as we
-# need to tell the difference between "no owner is specified for this module" vs "explicitly
-# defer to parent OWNERS file"
diff --git a/tests/tests/os/Android.bp b/tests/tests/os/Android.bp
index 6c48c87..1f9ca09 100644
--- a/tests/tests/os/Android.bp
+++ b/tests/tests/os/Android.bp
@@ -47,7 +47,7 @@
         "vts",
         "general-tests",
     ],
-    platform_apis: true,
+    sdk_version: "test_current",
     libs: [
         "android.test.runner.stubs",
         "android.test.base.stubs",
diff --git a/tests/tests/os/src/android/os/cts/EnvironmentTest.java b/tests/tests/os/src/android/os/cts/EnvironmentTest.java
index b00e1f2..0b02291 100644
--- a/tests/tests/os/src/android/os/cts/EnvironmentTest.java
+++ b/tests/tests/os/src/android/os/cts/EnvironmentTest.java
@@ -37,6 +37,7 @@
 
     @AppModeFull(reason = "External directory not accessible by instant apps")
     public void testEnvironmentExternal() {
+        assertTrue(Environment.getStorageDirectory().isDirectory());
         assertTrue(Environment.getExternalStorageDirectory().isDirectory());
     }
 
diff --git a/tests/tests/packageinstaller/atomicinstall/Android.bp b/tests/tests/packageinstaller/atomicinstall/Android.bp
index 1762edc..c772572 100644
--- a/tests/tests/packageinstaller/atomicinstall/Android.bp
+++ b/tests/tests/packageinstaller/atomicinstall/Android.bp
@@ -18,14 +18,12 @@
     srcs:  ["src/**/*.java"],
 
     java_resources:  [
-        ":AtomicInstallTestAppAv1",
-        ":AtomicInstallTestAppAv2",
-        ":AtomicInstallTestAppBv1",
         ":AtomicInstallCorrupt"
     ],
     static_libs: [
         "androidx.test.runner",
         "truth-prebuilt",
+	"cts-install-lib",
     ],
     sdk_version: "test_current",
     test_suites: [
@@ -40,28 +38,3 @@
     srcs: ["testdata/apk/prebuilt/corrupt.apk"],
     path: "testdata/apk/prebuilt"
 }
-
-android_test_helper_app {
-    name: "AtomicInstallTestAppAv1",
-
-    srcs:   ["testdata/apk/src/**/*java"],
-
-    manifest:   "testdata/apk/Av1.xml",
-}
-
-android_test_helper_app {
-    name: "AtomicInstallTestAppAv2",
-
-    srcs:   ["testdata/apk/src/**/*java"],
-
-    manifest:   "testdata/apk/Av2.xml",
-}
-
-android_test_helper_app {
-    name: "AtomicInstallTestAppBv1",
-
-    srcs:   ["testdata/apk/src/**/*java"],
-
-    manifest:   "testdata/apk/Bv1.xml",
-}
-
diff --git a/tests/tests/packageinstaller/atomicinstall/AndroidManifest.xml b/tests/tests/packageinstaller/atomicinstall/AndroidManifest.xml
index e0edcba..1f8f283 100644
--- a/tests/tests/packageinstaller/atomicinstall/AndroidManifest.xml
+++ b/tests/tests/packageinstaller/atomicinstall/AndroidManifest.xml
@@ -18,7 +18,7 @@
           package="com.android.tests.atomicinstall" >
 
     <application>
-        <receiver android:name="com.android.tests.atomicinstall.LocalIntentSender"
+        <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
                   android:exported="true" />
         <uses-library android:name="android.test.runner" />
     </application>
diff --git a/tests/tests/packageinstaller/atomicinstall/OWNERS b/tests/tests/packageinstaller/atomicinstall/OWNERS
new file mode 100644
index 0000000..25775b8
--- /dev/null
+++ b/tests/tests/packageinstaller/atomicinstall/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 36137
+include /hostsidetests/stagedinstall/OWNERS
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 f99ac2f..67051cc 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
@@ -16,41 +16,40 @@
 
 package com.android.tests.atomicinstall;
 
+import static com.android.cts.install.lib.InstallUtils.assertStatusSuccess;
+import static com.android.cts.install.lib.InstallUtils.getInstalledVersion;
+import static com.android.cts.install.lib.InstallUtils.openPackageInstallerSession;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.fail;
 
 import android.Manifest;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
-import android.content.pm.PackageManager;
 
 import androidx.test.InstrumentationRegistry;
 
+import com.android.cts.install.lib.Install;
+import com.android.cts.install.lib.InstallUtils;
+import com.android.cts.install.lib.LocalIntentSender;
+import com.android.cts.install.lib.TestApp;
+import com.android.cts.install.lib.Uninstall;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-
 /**
  * Tests for multi-package (a.k.a. atomic) installs.
  */
 @RunWith(JUnit4.class)
 public class AtomicInstallTest {
 
-    private static final String TEST_APP_A = "com.android.tests.atomicinstall.testapp.A";
-    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_A_V2_FILENAME = "AtomicInstallTestAppAv2.apk";
-    public static final String TEST_APP_B_FILENAME = "AtomicInstallTestAppBv1.apk";
     public static final String TEST_APP_CORRUPT_FILENAME = "corrupt.apk";
+    private static final TestApp CORRUPT_TESTAPP = new TestApp(
+            "corrupt", "com.corrupt", 1, false, TEST_APP_CORRUPT_FILENAME);
 
     private void adoptShellPermissions() {
         InstrumentationRegistry
@@ -64,8 +63,7 @@
     public void setup() throws Exception {
         adoptShellPermissions();
 
-        uninstall(TEST_APP_A);
-        uninstall(TEST_APP_B);
+        Uninstall.packages(TestApp.A, TestApp.B);
     }
 
     @After
@@ -78,149 +76,75 @@
 
     @Test
     public void testInstallTwoApks() throws Exception {
-        installMultiPackage(TEST_APP_A_FILENAME, TEST_APP_B_FILENAME);
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(1);
-        assertThat(getInstalledVersion(TEST_APP_B)).isEqualTo(1);
+        Install.multi(TestApp.A1, TestApp.B1).commit();
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(1);
+        assertThat(getInstalledVersion(TestApp.B)).isEqualTo(1);
     }
 
     @Test
     public void testInstallTwoApksDowngradeFail() throws Exception {
-        installMultiPackage(TEST_APP_A_V2_FILENAME, TEST_APP_B_FILENAME);
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(2);
-        assertThat(getInstalledVersion(TEST_APP_B)).isEqualTo(1);
+        Install.multi(TestApp.A2, TestApp.B1).commit();
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2);
+        assertThat(getInstalledVersion(TestApp.B)).isEqualTo(1);
 
-        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}) {
-            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, "INSTALL_FAILED_VERSION_DOWNGRADE");
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(2);
-        assertThat(getInstalledVersion(TEST_APP_B)).isEqualTo(1);
+        InstallUtils.commitExpectingFailure(AssertionError.class,
+                "INSTALL_FAILED_VERSION_DOWNGRADE", Install.multi(TestApp.A1, TestApp.B1));
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2);
+        assertThat(getInstalledVersion(TestApp.B)).isEqualTo(1);
     }
 
     @Test
     public void testFailInconsistentMultiPackageCommit() throws Exception {
         // Test inconsistency in staged settings
-        for (boolean staged : new boolean[]{false, true}) {
-            final PackageInstaller.SessionParams parentSessionParams =
-                    createSessionParams(/*staged*/staged, /*multipackage*/true,
-                            /*enableRollback*/false, /*inherit*/false);
-            final int parentSessionId =
-                    createSessionId(/*apkFileName*/null, parentSessionParams);
-            // Create a child session that differs in the staged parameter
-            final PackageInstaller.SessionParams childSessionParams =
-                    createSessionParams(/*staged*/!staged, /*multipackage*/false,
-                            /*enableRollback*/false, /*inherit*/false);
-            final int childSessionId =
-                    createSessionId("AtomicInstallTestAppAv1.apk", childSessionParams);
+        Install parentStaged = Install.multi(Install.single(TestApp.A1)).setStaged();
+        Install childStaged = Install.multi(Install.single(TestApp.A1).setStaged());
 
-            final PackageInstaller.Session parentSession = getSessionOrFail(parentSessionId);
-            parentSession.addChildSessionId(childSessionId);
-            parentSession.commit(LocalIntentSender.getIntentSender());
-            assertStatusFailure(LocalIntentSender.getIntentSenderResult(),
-                    "inconsistent staged settings");
-            assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
-        }
+        assertInconsistentStagedSettings(parentStaged);
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+        assertInconsistentStagedSettings(childStaged);
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
 
         // Test inconsistency in rollback settings
-        for (boolean enableRollback : new boolean[]{false, true}) {
-            final PackageInstaller.SessionParams parentSessionParams =
-                    createSessionParams(/*staged*/false, /*multipackage*/true,
-                            /*enableRollback*/enableRollback, /*inherit*/false);
-            final int parentSessionId =
-                    createSessionId(/*apkFileName*/null, parentSessionParams);
-            // Create a child session that differs in the staged parameter
-            final PackageInstaller.SessionParams childSessionParams =
-                    createSessionParams(/*staged*/false, /*multipackage*/false,
-                            /*enableRollback*/!enableRollback, /*inherit*/false);
-            final int childSessionId =
-                    createSessionId("AtomicInstallTestAppAv1.apk", childSessionParams);
+        Install parentEnabledRollback = Install.multi(Install.single(TestApp.A1))
+                .setEnableRollback();
+        Install childEnabledRollback = Install.multi(
+                Install.single(TestApp.A1).setEnableRollback());
 
-            final PackageInstaller.Session parentSession = getSessionOrFail(parentSessionId);
-            parentSession.addChildSessionId(childSessionId);
-            parentSession.commit(LocalIntentSender.getIntentSender());
-            assertStatusFailure(LocalIntentSender.getIntentSenderResult(),
-                    "inconsistent rollback settings");
-            assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
-        }
+        assertInconsistentRollbackSettings(parentEnabledRollback);
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+        assertInconsistentRollbackSettings(childEnabledRollback);
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
     }
 
     @Test
     public void testChildFailurePropagated() 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);
-
         // Create a child session that "inherits" from a non-existent package. This
         // causes the session commit to fail with a PackageManagerException.
-        final PackageInstaller.SessionParams childSessionParams =
-                createSessionParams(/*staged*/false, /*multipackage*/false,
-                        /*enableRollback*/ false, /*inherit*/true);
-        final int childSessionId =
-                createSessionId("AtomicInstallTestAppAv1.apk", childSessionParams);
-        parentSession.addChildSessionId(childSessionId);
-        parentSession.commit(LocalIntentSender.getIntentSender());
+        Install childInstall = Install.single(TestApp.A1).setSessionMode(
+                PackageInstaller.SessionParams.MODE_INHERIT_EXISTING);
+        Install parentInstall = Install.multi(childInstall);
 
-        final Intent intent = LocalIntentSender.getIntentSenderResult();
-        assertStatusFailure(intent, "Missing existing base package");
-        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
+        InstallUtils.commitExpectingFailure(AssertionError.class, "Missing existing base package",
+                parentInstall);
+
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
     }
 
     @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);
+        InstallUtils.commitExpectingFailure(AssertionError.class, "Failed to parse",
+                Install.multi(TestApp.A1, TestApp.B1, CORRUPT_TESTAPP));
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+        assertThat(getInstalledVersion(TestApp.B)).isEqualTo(-1);
     }
 
     @Test
     public void testInvalidStateScenarios() 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);
+        int parentSessionId = Install.multi(TestApp.A1, TestApp.B1).createSession();
+        PackageInstaller.Session parentSession = openPackageInstallerSession(parentSessionId);
 
-        final PackageInstaller.SessionParams childSessionParams =
-                createSessionParams(/*staged*/false, /*multipackage*/false,
-                        /*enableRollback*/false, /*inherit*/ false);
-        for (String apkFileName : new String[]{TEST_APP_A_FILENAME, TEST_APP_B_FILENAME}) {
-            final int childSessionId = createSessionId(apkFileName, childSessionParams);
-            parentSession.addChildSessionId(childSessionId);
-            PackageInstaller.Session childSession = getSessionOrFail(childSessionId);
+        for (int childSessionId : parentSession.getChildSessionIds()) {
+            PackageInstaller.Session childSession = openPackageInstallerSession(childSessionId);
             try {
                 childSession.commit(LocalIntentSender.getIntentSender());
                 fail("Should not be able to commit a child session!");
@@ -234,8 +158,8 @@
                 // ignore
             }
         }
-        int toAbandonSessionId = createSessionId(TEST_APP_A_FILENAME, childSessionParams);
-        PackageInstaller.Session toAbandonSession = getSessionOrFail(toAbandonSessionId);
+        int toAbandonSessionId = Install.single(TestApp.A1).createSession();
+        PackageInstaller.Session toAbandonSession = openPackageInstallerSession(toAbandonSessionId);
         toAbandonSession.abandon();
         try {
             parentSession.addChildSessionId(toAbandonSessionId);
@@ -244,140 +168,19 @@
             // ignore
         }
 
-        // Commit the session (this will start the installation workflow).
         parentSession.commit(LocalIntentSender.getIntentSender());
         assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
     }
 
-    private static long getInstalledVersion(String packageName) {
-        Context context = InstrumentationRegistry.getContext();
-        PackageManager pm = context.getPackageManager();
-        try {
-            PackageInfo info = pm.getPackageInfo(packageName, 0);
-            return info.getLongVersionCode();
-        } catch (PackageManager.NameNotFoundException e) {
-            return -1;
-        }
+    private static void assertInconsistentStagedSettings(Install install) {
+        assertInconsistentSettings("inconsistent staged settings", install);
     }
 
-    private static void installMultiPackage(String... resources) 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);
-
-        ArrayList<Integer> childSessionIds = new ArrayList<>(resources.length);
-        for (String apkFileName : resources) {
-            final PackageInstaller.SessionParams childSessionParams =
-                    createSessionParams(/*staged*/false, /*multipackage*/false,
-                            /*enableRollback*/false, /*inherit*/ false);
-            final int childSessionId = createSessionId(apkFileName, childSessionParams);
-            childSessionIds.add(childSessionId);
-            parentSession.addChildSessionId(childSessionId);
-
-            PackageInstaller.Session childSession = getSessionOrFail(childSessionId);
-            assertThat(childSession.getParentSessionId()).isEqualTo(parentSessionId);
-            assertThat(getSessionInfoOrFail(childSessionId).getParentSessionId())
-                    .isEqualTo(parentSessionId);
-        }
-        assertThat(parentSession.getChildSessionIds()).asList().containsAllIn(childSessionIds);
-        assertThat(getSessionInfoOrFail(parentSessionId).getChildSessionIds()).asList()
-                .containsAllIn(childSessionIds);
-
-        // Commit the session (this will start the installation workflow).
-        parentSession.commit(LocalIntentSender.getIntentSender());
-        assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
+    private static void assertInconsistentRollbackSettings(Install install) {
+        assertInconsistentSettings("inconsistent rollback settings", install);
     }
 
-    private static PackageInstaller.SessionParams createSessionParams(
-            boolean staged, boolean multiPackage, boolean enableRollback, boolean inherit) {
-        final int sessionMode = inherit
-                ? PackageInstaller.SessionParams.MODE_INHERIT_EXISTING
-                : PackageInstaller.SessionParams.MODE_FULL_INSTALL;
-        final PackageInstaller.SessionParams params =
-                new PackageInstaller.SessionParams(sessionMode);
-        if (staged) {
-            params.setStaged();
-        }
-        if (multiPackage) {
-            params.setMultiPackage();
-        }
-        params.setEnableRollback(enableRollback);
-        return params;
-    }
-
-    private static int createSessionId(String apkFileName, PackageInstaller.SessionParams params)
-            throws Exception {
-        final PackageInstaller packageInstaller = InstrumentationRegistry.getContext()
-                .getPackageManager().getPackageInstaller();
-        final int sessionId = packageInstaller.createSession(params);
-        if (apkFileName == null) {
-            return sessionId;
-        }
-        final PackageInstaller.Session session = packageInstaller.openSession(sessionId);
-        try (OutputStream packageInSession = session.openWrite(apkFileName, 0, -1);
-            final InputStream is =
-                AtomicInstallTest.class.getClassLoader().getResourceAsStream(
-                    apkFileName)) {
-            final byte[] buffer = new byte[4096];
-            int n;
-            while ((n = is.read(buffer)) >= 0) {
-                packageInSession.write(buffer, 0, n);
-            }
-        }
-        return sessionId;
-    }
-
-    private static PackageInstaller.Session getSessionOrFail(int sessionId) throws Exception {
-        final PackageInstaller packageInstaller = InstrumentationRegistry.getContext()
-                .getPackageManager().getPackageInstaller();
-        return packageInstaller.openSession(sessionId);
-    }
-
-    private static PackageInstaller.SessionInfo getSessionInfoOrFail(int sessionId)
-            throws Exception {
-        final PackageInstaller packageInstaller = InstrumentationRegistry.getContext()
-                .getPackageManager().getPackageInstaller();
-        return packageInstaller.getSessionInfo(sessionId);
-    }
-
-    private static void assertStatusSuccess(Intent result) {
-        final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
-                PackageInstaller.STATUS_FAILURE);
-        if (status == -1) {
-            throw new AssertionError("PENDING USER ACTION");
-        } else if (status > 0) {
-            String message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
-            throw new AssertionError(message == null ? "UNKNOWN FAILURE" : message);
-        }
-    }
-
-    private static void assertStatusFailure(Intent result, String errorMessage) {
-        final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
-            PackageInstaller.STATUS_FAILURE);
-        if (status == -1) {
-            throw new AssertionError("PENDING USER ACTION");
-        } else if (status == 0) {
-            throw new AssertionError("Installation unexpectedly succeeded!");
-        }
-        final String message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
-        if (message == null || !message.contains(errorMessage)) {
-            throw new AssertionError("Unexpected failure:" +
-                    (message == null ? "UNKNOWN" : message));
-        }
-    }
-
-    private static void uninstall(String packageName) throws Exception {
-        // No need to uninstall if the package isn't installed.
-        if (getInstalledVersion(packageName) == -1) {
-            return;
-        }
-
-        final PackageInstaller packageInstaller = InstrumentationRegistry.getContext()
-                .getPackageManager().getPackageInstaller();
-        packageInstaller.uninstall(packageName, LocalIntentSender.getIntentSender());
-        // Don't care about status; this is just cleanup
-        LocalIntentSender.getIntentSenderResult();
+    private static void assertInconsistentSettings(String failMessage, Install install) {
+        InstallUtils.commitExpectingFailure(AssertionError.class, failMessage, install);
     }
 }
diff --git a/tests/tests/packageinstaller/atomicinstall/src/com/android/tests/atomicinstall/LocalIntentSender.java b/tests/tests/packageinstaller/atomicinstall/src/com/android/tests/atomicinstall/LocalIntentSender.java
deleted file mode 100644
index e015cd8..0000000
--- a/tests/tests/packageinstaller/atomicinstall/src/com/android/tests/atomicinstall/LocalIntentSender.java
+++ /dev/null
@@ -1,55 +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.
- */
-
-package com.android.tests.atomicinstall;
-
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
-
-import androidx.test.InstrumentationRegistry;
-
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-
-public class LocalIntentSender extends BroadcastReceiver {
-    private static final BlockingQueue<Intent> sIntentSenderResults = new LinkedBlockingQueue<>();
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        sIntentSenderResults.add(intent);
-    }
-
-    /**
-     * Get a LocalIntentSender.
-     */
-    static IntentSender getIntentSender() {
-        Context context = InstrumentationRegistry.getContext();
-        Intent intent = new Intent(context, LocalIntentSender.class);
-        PendingIntent pending = PendingIntent.getBroadcast(context, 0, intent, 0);
-        return pending.getIntentSender();
-    }
-
-    /**
-     * Returns the most recent Intent sent by a LocalIntentSender.
-     */
-    static Intent getIntentSenderResult() throws InterruptedException {
-        return sIntentSenderResults.poll(5, TimeUnit.SECONDS);
-    }
-}
diff --git a/tests/tests/packageinstaller/atomicinstall/testdata/apk/Av1.xml b/tests/tests/packageinstaller/atomicinstall/testdata/apk/Av1.xml
deleted file mode 100644
index e3c8c28..0000000
--- a/tests/tests/packageinstaller/atomicinstall/testdata/apk/Av1.xml
+++ /dev/null
@@ -1,31 +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
-  -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.tests.atomicinstall.testapp.A"
-          android:versionCode="1"
-          android:versionName="1.0" >
-
-    <uses-sdk android:minSdkVersion="19" />
-
-    <application android:label="AtomicInstall Test App A v1">
-        <activity android:name="com.android.tests.atomicinstall.testapp.MainActivity">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/tests/tests/packageinstaller/atomicinstall/testdata/apk/Bv1.xml b/tests/tests/packageinstaller/atomicinstall/testdata/apk/Bv1.xml
deleted file mode 100644
index dd5db42..0000000
--- a/tests/tests/packageinstaller/atomicinstall/testdata/apk/Bv1.xml
+++ /dev/null
@@ -1,31 +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
-  -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.tests.atomicinstall.testapp.B"
-          android:versionCode="1"
-          android:versionName="1.0" >
-
-    <uses-sdk android:minSdkVersion="19" />
-
-    <application android:label="AtomicInstall Test App B v1">
-        <activity android:name="com.android.tests.atomicinstall.testapp.MainActivity">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/tests/tests/packageinstaller/install/AndroidManifest.xml b/tests/tests/packageinstaller/install/AndroidManifest.xml
index 7d2e984..eeef252 100644
--- a/tests/tests/packageinstaller/install/AndroidManifest.xml
+++ b/tests/tests/packageinstaller/install/AndroidManifest.xml
@@ -22,7 +22,7 @@
     <application android:label="Cts Package Installer Tests">
         <uses-library android:name="android.test.runner" />
 
-        <activity android:name=".InstallConfirmDialogStarter" />
+        <activity android:name="com.android.compatibility.common.util.FutureResultActivity" />
 
         <provider android:authorities="android.packageinstaller.install.cts.fileprovider"
                   android:name="androidx.core.content.FileProvider"
diff --git a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/InstallConfirmDialogStarter.kt b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/InstallConfirmDialogStarter.kt
deleted file mode 100644
index b026943..0000000
--- a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/InstallConfirmDialogStarter.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.packageinstaller.install.cts
-
-import android.app.Activity
-import android.content.Intent
-import android.os.Bundle
-import java.util.concurrent.LinkedBlockingQueue
-
-val installDialogResults = LinkedBlockingQueue<Int>()
-
-class InstallConfirmDialogStarter : Activity() {
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-
-        savedInstanceState ?: installDialogResults.clear()
-    }
-
-    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
-        installDialogResults.offer(resultCode)
-    }
-}
diff --git a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/IntentTest.kt b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/IntentTest.kt
index e163a32..a8a8917 100644
--- a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/IntentTest.kt
+++ b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/IntentTest.kt
@@ -28,6 +28,7 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import java.util.concurrent.TimeUnit
 
 private const val INSTALL_BUTTON_ID = "button1"
 private const val CANCEL_BUTTON_ID = "button2"
@@ -47,11 +48,11 @@
      */
     @Test
     fun confirmInstallation() {
-        startInstallationViaIntent()
+        val installation = startInstallationViaIntent()
         clickInstallerUIButton(INSTALL_BUTTON_ID)
 
         // Install should have succeeded
-        assertEquals(RESULT_OK, getInstallDialogResult())
+        assertEquals(RESULT_OK, installation.get(TIMEOUT, TimeUnit.MILLISECONDS))
         assertInstalled()
     }
 
@@ -61,14 +62,12 @@
      */
     @Test
     fun cancelInstallation() {
-        startInstallationViaIntent()
+        val installation = startInstallationViaIntent()
         clickInstallerUIButton(CANCEL_BUTTON_ID)
 
         // Install should have been aborted
-        assertEquals(RESULT_CANCELED, getInstallDialogResult())
+        assertEquals(RESULT_CANCELED, installation.get(TIMEOUT, TimeUnit.MILLISECONDS))
         assertNotInstalled()
-
-        assertNoMoreInstallResults()
     }
 
     /**
@@ -85,12 +84,12 @@
         intent.putExtra(Intent.EXTRA_RETURN_RESULT, true)
         intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
 
-        installDialogStarter.activity.startActivityForResult(intent, 0)
+        val reinstall = installDialogStarter.activity.startActivityForResult(intent)
 
         clickInstallerUIButton(INSTALL_BUTTON_ID)
 
         // Install should have succeeded
-        assertEquals(RESULT_OK, getInstallDialogResult())
+        assertEquals(RESULT_OK, reinstall.get(TIMEOUT, TimeUnit.MILLISECONDS))
         assertInstalled()
     }
 }
diff --git a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PackageInstallerTestBase.kt b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PackageInstallerTestBase.kt
index f6bbe06..d84ab1b 100644
--- a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PackageInstallerTestBase.kt
+++ b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PackageInstallerTestBase.kt
@@ -38,12 +38,14 @@
 import android.support.test.uiautomator.Until
 import androidx.core.content.FileProvider
 import com.android.compatibility.common.util.AppOpsUtils
+import com.android.compatibility.common.util.FutureResultActivity
 import org.junit.After
 import org.junit.Assert
 import org.junit.Before
 import org.junit.Rule
 import java.io.File
 import java.lang.IllegalArgumentException
+import java.util.concurrent.CompletableFuture
 import java.util.concurrent.LinkedBlockingQueue
 import java.util.concurrent.TimeUnit
 
@@ -62,7 +64,7 @@
 
 open class PackageInstallerTestBase {
     @get:Rule
-    val installDialogStarter = ActivityTestRule(InstallConfirmDialogStarter::class.java)
+    val installDialogStarter = ActivityTestRule(FutureResultActivity::class.java)
 
     private val context = InstrumentationRegistry.getTargetContext()
     private val pm = context.packageManager
@@ -79,7 +81,7 @@
             if (status == STATUS_PENDING_USER_ACTION) {
                 val activityIntent = intent.getParcelableExtra<Intent>(EXTRA_INTENT)
                 activityIntent!!.addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK)
-                installDialogStarter.activity.startActivityForResult(activityIntent, 0)
+                installDialogStarter.activity.startActivityForResult(activityIntent)
             }
 
             installSessionResult.offer(status)
@@ -128,7 +130,7 @@
     /**
      * Start an installation via a session
      */
-    protected fun startInstallationViaSession(): PackageInstaller.Session {
+    protected fun startInstallationViaSession(): CompletableFuture<Int> {
         val pi = pm.packageInstaller
 
         // Create session
@@ -143,33 +145,28 @@
         }
 
         // Commit session
-        val pendingIntent = PendingIntent.getBroadcast(context, 0, Intent(INSTALL_ACTION_CB),
-                FLAG_UPDATE_CURRENT)
-        session.commit(pendingIntent.intentSender)
+        val dialog = FutureResultActivity.doAndAwaitStart {
+            val pendingIntent = PendingIntent.getBroadcast(context, 0, Intent(INSTALL_ACTION_CB),
+                    FLAG_UPDATE_CURRENT)
+            session.commit(pendingIntent.intentSender)
+        }
 
         // The system should have asked us to launch the installer
         Assert.assertEquals(STATUS_PENDING_USER_ACTION, getInstallSessionResult())
 
-        return session
+        return dialog
     }
 
     /**
      * Start an installation via a session
      */
-    protected fun startInstallationViaIntent() {
+    protected fun startInstallationViaIntent(): CompletableFuture<Int> {
         val intent = Intent(Intent.ACTION_INSTALL_PACKAGE)
         intent.data = FileProvider.getUriForFile(context, CONTENT_AUTHORITY, apkFile)
         intent.putExtra(Intent.EXTRA_RETURN_RESULT, true)
         intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
 
-        installDialogStarter.activity.startActivityForResult(intent, 0)
-    }
-
-    /**
-     * Wait for result of install dialog and return it
-     */
-    fun getInstallDialogResult(timeout: Long = TIMEOUT): Int? {
-        return installDialogResults.poll(timeout, TimeUnit.MILLISECONDS)
+        return installDialogStarter.activity.startActivityForResult(intent)
     }
 
     fun assertInstalled() {
@@ -195,14 +192,6 @@
                 .click()
     }
 
-    /**
-     * Assert that there are no more callbacks from the install session or install dialog
-     */
-    fun assertNoMoreInstallResults() {
-        Assert.assertNull(getInstallSessionResult(0))
-        Assert.assertEquals(0, installDialogResults.size)
-    }
-
     @After
     fun unregisterInstallResultReceiver() {
         try {
diff --git a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/SessionTest.kt b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/SessionTest.kt
index 34b78c3..111e6a8 100644
--- a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/SessionTest.kt
+++ b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/SessionTest.kt
@@ -30,6 +30,7 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import java.util.concurrent.TimeUnit
 
 private const val INSTALL_BUTTON_ID = "button1"
 private const val CANCEL_BUTTON_ID = "button2"
@@ -50,7 +51,7 @@
      */
     @Test
     fun confirmInstallation() {
-        startInstallationViaSession()
+        val installation = startInstallationViaSession()
         clickInstallerUIButton(INSTALL_BUTTON_ID)
 
         // Install should have succeeded
@@ -58,9 +59,7 @@
         assertInstalled()
 
         // Even when the install succeeds the install confirm dialog returns 'canceled'
-        assertEquals(RESULT_CANCELED, getInstallDialogResult())
-
-        assertNoMoreInstallResults()
+        assertEquals(RESULT_CANCELED, installation.get(TIMEOUT, TimeUnit.MILLISECONDS))
 
         assertTrue(AppOpsUtils.allowedOperationLogged(context.packageName, APP_OP_STR))
     }
@@ -70,7 +69,7 @@
      */
     @Test
     fun setAppCategory() {
-        startInstallationViaSession()
+        val installation = startInstallationViaSession()
         clickInstallerUIButton(INSTALL_BUTTON_ID)
 
         // Wait for installation to finish
@@ -90,14 +89,12 @@
      */
     @Test
     fun cancelInstallation() {
-        startInstallationViaSession()
+        val installation = startInstallationViaSession()
         clickInstallerUIButton(CANCEL_BUTTON_ID)
 
         // Install should have been aborted
         assertEquals(STATUS_FAILURE_ABORTED, getInstallSessionResult())
-        assertEquals(RESULT_CANCELED, getInstallDialogResult())
+        assertEquals(RESULT_CANCELED, installation.get(TIMEOUT, TimeUnit.MILLISECONDS))
         assertNotInstalled()
-
-        assertNoMoreInstallResults()
     }
 }
diff --git a/tests/tests/packageinstaller/tapjacking/OWNERS b/tests/tests/packageinstaller/tapjacking/OWNERS
new file mode 100644
index 0000000..0088192
--- /dev/null
+++ b/tests/tests/packageinstaller/tapjacking/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 36137
+moltmann@google.com
\ No newline at end of file
diff --git a/tests/tests/packageinstaller/uninstall/OWNERS b/tests/tests/packageinstaller/uninstall/OWNERS
new file mode 100644
index 0000000..0088192
--- /dev/null
+++ b/tests/tests/packageinstaller/uninstall/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 36137
+moltmann@google.com
\ No newline at end of file
diff --git a/tests/tests/permission/AndroidTest.xml b/tests/tests/permission/AndroidTest.xml
index c4ee088..8fcdc9c 100644
--- a/tests/tests/permission/AndroidTest.xml
+++ b/tests/tests/permission/AndroidTest.xml
@@ -50,6 +50,7 @@
         <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" />
+        <option name="push" value="CtsAppThatRunsRationaleTests.apk->/data/local/tmp/cts/permissions/CtsAppThatRunsRationaleTests.apk" />
     </target_preparer>
 
     <!-- Remove additional apps if installed -->
diff --git a/common/device-side/util/tests/Android.bp b/tests/tests/permission/AppThatRunsRationaleTests/Android.bp
similarity index 62%
copy from common/device-side/util/tests/Android.bp
copy to tests/tests/permission/AppThatRunsRationaleTests/Android.bp
index 2abba37..6bd320b 100644
--- a/common/device-side/util/tests/Android.bp
+++ b/tests/tests/permission/AppThatRunsRationaleTests/Android.bp
@@ -1,28 +1,32 @@
-// Copyright (C) 2015 The Android Open Source Project
+//
+// 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
+//      http://www.apache.org/licenses/LICENSE-2.0
 //
 // Unless required by applicable law or agreed to in writing, software
 // distributed under the License is distributed on an "AS IS" BASIS,
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
+//
 
-java_test {
-    name: "compatibility-device-util-tests",
-
-    srcs: ["src/**/*.java"],
-
-    static_libs: [
-        "compatibility-device-util",
-        "junit",
-        "testng", // TODO: remove once Android migrates to JUnit 4.12, which provide assertThrows
-        "truth-prebuilt"
-    ],
+android_test {
+    name: "CtsAppThatRunsRationaleTests",
+    defaults: ["cts_defaults"],
 
     sdk_version: "test_current",
+
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+        "cts_instant",
+    ],
+
+    srcs: ["src/**/*.java"],
 }
diff --git a/tests/tests/packageinstaller/atomicinstall/testdata/apk/Av2.xml b/tests/tests/permission/AppThatRunsRationaleTests/AndroidManifest.xml
similarity index 67%
rename from tests/tests/packageinstaller/atomicinstall/testdata/apk/Av2.xml
rename to tests/tests/permission/AppThatRunsRationaleTests/AndroidManifest.xml
index 34472a0..cede468 100644
--- a/tests/tests/packageinstaller/atomicinstall/testdata/apk/Av2.xml
+++ b/tests/tests/permission/AppThatRunsRationaleTests/AndroidManifest.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
 <!--
   ~ Copyright (C) 2019 The Android Open Source Project
   ~
@@ -13,19 +14,19 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
   -->
+
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.tests.atomicinstall.testapp.A"
-          android:versionCode="2"
-          android:versionName="2.0" >
+    package="android.permission.cts.appthatrunsrationaletests">
 
-    <uses-sdk android:minSdkVersion="19" />
+    <uses-permission android:name="android.permission.READ_CONTACTS" />
 
-    <application android:label="AtomicInstall Test App A v2">
-        <activity android:name="com.android.tests.atomicinstall.testapp.MainActivity">
+    <application android:label="CtsRationaleTests">
+        <activity android:name=".TestActivity">
             <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
+                <action android:name="CtsRationalTests.intent.action.Launch" />
+                <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
     </application>
 </manifest>
+
diff --git a/tests/tests/permission/AppThatRunsRationaleTests/src/android/permission/cts/appthatrunsrationaletests/TestActivity.java b/tests/tests/permission/AppThatRunsRationaleTests/src/android/permission/cts/appthatrunsrationaletests/TestActivity.java
new file mode 100644
index 0000000..7544890
--- /dev/null
+++ b/tests/tests/permission/AppThatRunsRationaleTests/src/android/permission/cts/appthatrunsrationaletests/TestActivity.java
@@ -0,0 +1,42 @@
+/*
+ * 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.permission.cts.appthatrunsrationaletests;
+
+import android.Manifest;
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+
+public class TestActivity extends Activity {
+    private static final String CALLBACK_KEY = "testactivitycallback";
+    private static final String RESULT_KEY = "testactivityresult";
+    private static final String PERMISSION_NAME = Manifest.permission.READ_CONTACTS;
+
+    @Override
+    public void onCreate(Bundle bundle) {
+        super.onCreate(bundle);
+
+        RemoteCallback cb = (RemoteCallback) getIntent().getExtras().get(CALLBACK_KEY);
+
+        boolean result = shouldShowRequestPermissionRationale(PERMISSION_NAME);
+        Bundle res = new Bundle();
+        res.putBoolean(RESULT_KEY, result);
+
+        finish();
+        cb.sendResult(res);
+    }
+}
diff --git a/tests/tests/permission/OWNERS b/tests/tests/permission/OWNERS
index e37d064..9c17e51 100644
--- a/tests/tests/permission/OWNERS
+++ b/tests/tests/permission/OWNERS
@@ -3,3 +3,4 @@
 per-file PowerManagerServicePermissionTest.java = dehboxturtle@google.com
 per-file RequestLocation.java = hallliu@google.com
 per-file MainlineNetworkStackPermissionTest.java = file: platform/frameworks/base:/services/net/OWNERS
+per-file Camera2PermissionTest.java = file: platform/frameworks/av:/camera/OWNERS
diff --git a/tests/tests/permission/sdk28/OWNERS b/tests/tests/permission/sdk28/OWNERS
new file mode 100644
index 0000000..c126a70
--- /dev/null
+++ b/tests/tests/permission/sdk28/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 137825
+moltmann@google.com
\ No newline at end of file
diff --git a/tests/tests/permission/src/android/permission/cts/ActivityPermissionRationaleTest.java b/tests/tests/permission/src/android/permission/cts/ActivityPermissionRationaleTest.java
new file mode 100644
index 0000000..947e59a
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/ActivityPermissionRationaleTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.permission.cts;
+
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.Manifest;
+import android.app.UiAutomation;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+@AppModeFull(reason = "Tests properties of other app. Instant apps cannot interact with other apps")
+@RunWith(AndroidJUnit4ClassRunner.class)
+public class ActivityPermissionRationaleTest {
+    private static final String APK =
+            "/data/local/tmp/cts/permissions/CtsAppThatRunsRationaleTests.apk";
+    private static final String PACKAGE_NAME = "android.permission.cts.appthatrunsrationaletests";
+    private static final String PERMISSION_NAME = Manifest.permission.READ_CONTACTS;
+    private static final String CALLBACK_KEY = "testactivitycallback";
+    private static final String RESULT_KEY = "testactivityresult";
+    private static final int TIMEOUT = 5000;
+
+    private static Context sContext = InstrumentationRegistry.getInstrumentation().getContext();
+    private static UiAutomation sUiAuto =
+            InstrumentationRegistry.getInstrumentation().getUiAutomation();
+
+    @BeforeClass
+    public static void setUp() {
+        runShellCommand("pm install -r " + APK);
+        int flag = PackageManager.FLAG_PERMISSION_USER_SET;
+        PermissionUtils.setPermissionFlags(PACKAGE_NAME, PERMISSION_NAME, flag, flag);
+    }
+
+    @AfterClass
+    public static void unInstallApp() {
+        runShellCommand("pm uninstall " + PACKAGE_NAME);
+    }
+
+    private void assertAppShowRationaleIs(boolean expected) throws Exception {
+        CompletableFuture<Boolean> callbackReturn = new CompletableFuture<>();
+        RemoteCallback cb = new RemoteCallback((Bundle result) ->
+                callbackReturn.complete(result.getBoolean(RESULT_KEY)));
+        Intent intent = new Intent();
+        intent.setComponent(new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".TestActivity"));
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.putExtra(CALLBACK_KEY, cb);
+
+        sContext.startActivity(intent);
+        assertThat(callbackReturn.get(TIMEOUT, TimeUnit.MILLISECONDS)).isEqualTo(expected);
+    }
+
+    @Before
+    public void clearData() {
+        runShellCommand("pm clear android.permission.cts.appthatrunsrationaletests");
+        PermissionUtils.setPermissionFlags(PACKAGE_NAME, PERMISSION_NAME,
+                PackageManager.FLAG_PERMISSION_POLICY_FIXED, 0);
+    }
+
+    @Test
+    public void permissionGrantedNoRationale() throws Exception {
+        sUiAuto.grantRuntimePermission(PACKAGE_NAME, PERMISSION_NAME);
+
+        assertAppShowRationaleIs(false);
+    }
+
+    @Test
+    public void policyFixedNoRationale() throws Exception {
+        int flags = PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+        PermissionUtils.setPermissionFlags(PACKAGE_NAME, PERMISSION_NAME, flags, flags);
+
+        assertAppShowRationaleIs(false);
+    }
+
+    @Test
+    public void userFixedNoRationale() throws Exception {
+        int flags = PackageManager.FLAG_PERMISSION_USER_FIXED;
+        PermissionUtils.setPermissionFlags(PACKAGE_NAME, PERMISSION_NAME, flags, flags);
+
+        assertAppShowRationaleIs(false);
+    }
+
+    @Test
+    public void notUserSetNoRationale() throws Exception {
+        PermissionUtils.setPermissionFlags(PACKAGE_NAME, PERMISSION_NAME,
+                PackageManager.FLAG_PERMISSION_USER_SET, 0);
+
+        assertAppShowRationaleIs(false);
+    }
+
+    @Test
+    public void userSetNeedRationale() throws Exception {
+        int flags = PackageManager.FLAG_PERMISSION_USER_SET;
+        PermissionUtils.setPermissionFlags(PACKAGE_NAME, PERMISSION_NAME, flags, flags);
+
+        assertAppShowRationaleIs(true);
+    }
+}
diff --git a/tests/tests/permission/src/android/permission/cts/Camera2PermissionTest.java b/tests/tests/permission/src/android/permission/cts/Camera2PermissionTest.java
index 4076ace..379f478 100644
--- a/tests/tests/permission/src/android/permission/cts/Camera2PermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/Camera2PermissionTest.java
@@ -19,27 +19,28 @@
 import static com.android.ex.camera2.blocking.BlockingStateCallback.*;
 
 import android.content.Context;
-import android.hardware.camera2.CameraDevice;
-import android.hardware.camera2.CameraManager;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraCharacteristics.Key;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.platform.test.annotations.Presubmit;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
-import java.util.List;
-
 import com.android.ex.camera2.blocking.BlockingCameraManager;
 import com.android.ex.camera2.blocking.BlockingStateCallback;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Tests for Camera2 API related Permissions. Currently, this means
  * android.permission.CAMERA.
  */
 public class Camera2PermissionTest extends AndroidTestCase {
-    private static final String TAG = "CameraDeviceTest";
+    private static final String TAG = "Camera2PermissionTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
     private static final int CAMERA_CLOSE_TIMEOUT_MS = 2000;
 
@@ -99,6 +100,25 @@
     }
 
     /**
+     * Check that no system cameras can be discovered without
+     * {@link android.Manifest.permission#CAMERA} and android.permission.SYSTEM_CAMERA
+     */
+    public void testSystemCameraDiscovery() throws Exception {
+        for (String id : mCameraIds) {
+            Log.i(TAG, "testSystemCameraDiscovery for camera id " +  id);
+            CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(id);
+            assertNotNull("Camera characteristics shouldn't be null", characteristics);
+            int[] availableCapabilities =
+                    characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+            assertTrue("Camera capabilities shouldn't be null", availableCapabilities != null);
+            List<Integer> capList = toList(availableCapabilities);
+            assertFalse("System camera device " + id + " should not be public",
+                    capList.contains(
+                            CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA));
+        }
+    }
+
+    /**
      * Check the absence of camera characteristics keys that require Permission:
      * {@link android.Manifest.permission#CAMERA}.
      */
@@ -153,6 +173,14 @@
                 cameraId, mCameraListener, mHandler);
     }
 
+    private static List<Integer> toList(int[] array) {
+        List<Integer> list = new ArrayList<Integer>();
+        for (int i  : array) {
+            list.add(i);
+        }
+        return list;
+    }
+
     private void closeCamera() {
         if (mCamera != null) {
             mCamera.close();
diff --git a/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java b/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
index db30b83..49af2bf 100644
--- a/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
+++ b/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
@@ -54,7 +54,6 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Looper;
-import android.os.ParcelFileDescriptor;
 import android.platform.test.annotations.AppModeFull;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
@@ -67,6 +66,7 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.compatibility.common.util.ProtoUtils;
 import com.android.server.job.nano.JobSchedulerServiceDumpProto;
 import com.android.server.job.nano.JobSchedulerServiceDumpProto.RegisteredJob;
 
@@ -77,8 +77,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.ByteArrayOutputStream;
-import java.io.FileInputStream;
 import java.util.Arrays;
 import java.util.concurrent.CountDownLatch;
 
@@ -213,26 +211,8 @@
      * Get the state of the job scheduler
      */
     public static JobSchedulerServiceDumpProto getJobSchedulerDump() throws Exception {
-        ParcelFileDescriptor pfd = sUiAutomation.executeShellCommand("dumpsys jobscheduler --proto");
-
-        try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
-            // Copy data from 'is' into 'os'
-            try (FileInputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
-                byte[] buffer = new byte[16384];
-
-                while (true) {
-                    int numRead = is.read(buffer);
-
-                    if (numRead == -1) {
-                        break;
-                    } else {
-                        os.write(buffer, 0, numRead);
-                    }
-                }
-            }
-
-            return JobSchedulerServiceDumpProto.parseFrom(os.toByteArray());
-        }
+        return ProtoUtils.getProto(sUiAutomation, JobSchedulerServiceDumpProto.class,
+                ProtoUtils.DUMPSYS_JOB_SCHEDULER);
     }
 
     /**
diff --git a/tests/tests/permission/src/android/permission/cts/PermissionUpdateListenerTest.java b/tests/tests/permission/src/android/permission/cts/PermissionUpdateListenerTest.java
new file mode 100644
index 0000000..a03b61d
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/PermissionUpdateListenerTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.permission.cts;
+
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.OnPermissionsChangedListener;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.annotation.NonNull;
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@AppModeFull(reason = "Instant apps cannot access properties of other apps")
+@RunWith(AndroidJUnit4ClassRunner.class)
+public class PermissionUpdateListenerTest {
+    private static final String APK =
+            "/data/local/tmp/cts/permissions/"
+                    + "CtsAppThatRequestsCalendarContactsBodySensorCustomPermission.apk";
+    private static final String PACKAGE_NAME =
+            "android.permission.cts.appthatrequestcustompermission";
+    private static final String PERMISSION_NAME = "android.permission.READ_CONTACTS";
+    private static final int TIMEOUT = 30000;
+
+    private static final Context sContext =
+            InstrumentationRegistry.getInstrumentation().getContext();
+    private static final PackageManager sPm = sContext.getPackageManager();
+    private static int sUid;
+
+    @BeforeClass
+    public static void installApp() throws PackageManager.NameNotFoundException {
+        runShellCommand("pm install -r " + APK);
+        sUid = sPm.getPackageUid(PACKAGE_NAME, 0);
+    }
+
+    @AfterClass
+    public static void unInstallApp() {
+        runShellCommand("pm uninstall " + PACKAGE_NAME);
+    }
+
+    private class LatchWithPermissionsChangedListener extends CountDownLatch
+            implements OnPermissionsChangedListener {
+
+        LatchWithPermissionsChangedListener() {
+            super(1);
+        }
+
+        public void onPermissionsChanged(int uid) {
+            if (uid == sUid) {
+                countDown();
+            }
+        }
+    }
+
+    private void waitForLatchAndRemoveListener(@NonNull LatchWithPermissionsChangedListener latch)
+            throws Exception {
+        latch.await(TIMEOUT, TimeUnit.MILLISECONDS);
+        runWithShellPermissionIdentity(() -> sPm.removeOnPermissionsChangeListener(latch));
+        assertThat(latch.getCount()).isEqualTo((long) 0);
+    }
+
+    @Test
+    public void grantNotifiesListener() throws Exception {
+        LatchWithPermissionsChangedListener listenerCalled =
+                new LatchWithPermissionsChangedListener();
+
+        runWithShellPermissionIdentity(() -> {
+            sPm.addOnPermissionsChangeListener(listenerCalled);
+            sPm.grantRuntimePermission(PACKAGE_NAME, PERMISSION_NAME, sContext.getUser());
+        });
+        waitForLatchAndRemoveListener(listenerCalled);
+    }
+
+    @Test
+    public void revokeNotifiesListener() throws Exception {
+        LatchWithPermissionsChangedListener listenerCalled =
+                new LatchWithPermissionsChangedListener();
+
+        runWithShellPermissionIdentity(() -> {
+            sPm.addOnPermissionsChangeListener(listenerCalled);
+            sPm.revokeRuntimePermission(PACKAGE_NAME, PERMISSION_NAME, sContext.getUser());
+        });
+        waitForLatchAndRemoveListener(listenerCalled);
+    }
+
+    @Test
+    public void updateFlagsNotifiesListener() throws Exception {
+        LatchWithPermissionsChangedListener listenerCalled =
+                new LatchWithPermissionsChangedListener();
+
+        runWithShellPermissionIdentity(() -> {
+            sPm.addOnPermissionsChangeListener(listenerCalled);
+            int flag = PackageManager.FLAG_PERMISSION_USER_SET;
+            sPm.updatePermissionFlags(PERMISSION_NAME, PACKAGE_NAME, flag, flag,
+                    sContext.getUser());
+        });
+        waitForLatchAndRemoveListener(listenerCalled);
+    }
+}
diff --git a/tests/tests/permission2/Android.bp b/tests/tests/permission2/Android.bp
index f4f25b0..a816a55 100644
--- a/tests/tests/permission2/Android.bp
+++ b/tests/tests/permission2/Android.bp
@@ -26,6 +26,7 @@
     ],
     libs: ["android.test.base.stubs"],
     static_libs: [
+        "androidx.test.core",
         "compatibility-device-util-axt",
         "ctstestrunner-axt",
         "guava",
@@ -35,4 +36,22 @@
     ],
     srcs: ["src/**/*.java"],
     sdk_version: "test_current",
+    data: [
+        ":CtsLocationPermissionsUserSdk22",
+        ":CtsLocationPermissionsUserSdk29",
+        ":CtsSMSCallLogPermissionsUserSdk22",
+        ":CtsSMSCallLogPermissionsUserSdk29",
+        ":CtsStoragePermissionsUserDefaultSdk22",
+        ":CtsStoragePermissionsUserDefaultSdk28",
+        ":CtsStoragePermissionsUserDefaultSdk29",
+        ":CtsStoragePermissionsUserOptInSdk22",
+        ":CtsStoragePermissionsUserOptInSdk28",
+        ":CtsStoragePermissionsUserOptOutSdk29",
+        ":CtsLegacyStorageNotIsolatedWithSharedUid",
+        ":CtsLegacyStorageIsolatedWithSharedUid",
+        ":CtsLegacyStorageRestrictedWithSharedUid",
+        ":CtsLegacyStorageRestrictedSdk28WithSharedUid",
+        ":CtsSMSRestrictedWithSharedUid",
+        ":CtsSMSNotRestrictedWithSharedUid",
+    ],
 }
diff --git a/tests/tests/permission2/CtsLegacyStorageIsolatedWithSharedUid/Android.bp b/tests/tests/permission2/CtsLegacyStorageIsolatedWithSharedUid/Android.bp
index 6ee6120..ea9e86e 100644
--- a/tests/tests/permission2/CtsLegacyStorageIsolatedWithSharedUid/Android.bp
+++ b/tests/tests/permission2/CtsLegacyStorageIsolatedWithSharedUid/Android.bp
@@ -19,11 +19,4 @@
     defaults: ["cts_defaults"],
 
     sdk_version: "current",
-
-    test_suites: [
-        "cts",
-        "vts",
-        "general-tests",
-        "cts_instant",
-    ]
 }
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsLegacyStorageNotIsolatedWithSharedUid/Android.bp b/tests/tests/permission2/CtsLegacyStorageNotIsolatedWithSharedUid/Android.bp
index 63c2a14..c44004b 100644
--- a/tests/tests/permission2/CtsLegacyStorageNotIsolatedWithSharedUid/Android.bp
+++ b/tests/tests/permission2/CtsLegacyStorageNotIsolatedWithSharedUid/Android.bp
@@ -19,11 +19,4 @@
     defaults: ["cts_defaults"],
 
     sdk_version: "current",
-
-    test_suites: [
-        "cts",
-        "vts",
-        "general-tests",
-        "cts_instant",
-    ]
 }
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsLegacyStorageRestrictedSdk28WithSharedUid/Android.bp b/tests/tests/permission2/CtsLegacyStorageRestrictedSdk28WithSharedUid/Android.bp
index 50ac715..f3b9994 100644
--- a/tests/tests/permission2/CtsLegacyStorageRestrictedSdk28WithSharedUid/Android.bp
+++ b/tests/tests/permission2/CtsLegacyStorageRestrictedSdk28WithSharedUid/Android.bp
@@ -19,11 +19,4 @@
     defaults: ["cts_defaults"],
 
     sdk_version: "current",
-
-    test_suites: [
-        "cts",
-        "vts",
-        "general-tests",
-        "cts_instant",
-    ]
 }
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsLegacyStorageRestrictedWithSharedUid/Android.bp b/tests/tests/permission2/CtsLegacyStorageRestrictedWithSharedUid/Android.bp
index 78068f9..b0b486d 100644
--- a/tests/tests/permission2/CtsLegacyStorageRestrictedWithSharedUid/Android.bp
+++ b/tests/tests/permission2/CtsLegacyStorageRestrictedWithSharedUid/Android.bp
@@ -19,11 +19,4 @@
     defaults: ["cts_defaults"],
 
     sdk_version: "current",
-
-    test_suites: [
-        "cts",
-        "vts",
-        "general-tests",
-        "cts_instant",
-    ]
 }
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsLocationPermissionsUserSdk22/Android.bp b/tests/tests/permission2/CtsLocationPermissionsUserSdk22/Android.bp
index 35ab9e4..ef1e160 100644
--- a/tests/tests/permission2/CtsLocationPermissionsUserSdk22/Android.bp
+++ b/tests/tests/permission2/CtsLocationPermissionsUserSdk22/Android.bp
@@ -20,13 +20,6 @@
 
     sdk_version: "current",
 
-    test_suites: [
-        "cts",
-        "vts",
-        "general-tests",
-        "cts_instant",
-    ],
-
     // TODO: Uncomment when uncommenting the test
     // srcs: ["src/**/*.java"]
 
diff --git a/tests/tests/permission2/CtsLocationPermissionsUserSdk29/Android.bp b/tests/tests/permission2/CtsLocationPermissionsUserSdk29/Android.bp
index 3804b92..44e9af0 100644
--- a/tests/tests/permission2/CtsLocationPermissionsUserSdk29/Android.bp
+++ b/tests/tests/permission2/CtsLocationPermissionsUserSdk29/Android.bp
@@ -20,13 +20,6 @@
 
     sdk_version: "current",
 
-    test_suites: [
-        "cts",
-        "vts",
-        "general-tests",
-        "cts_instant",
-    ],
-
     // TODO: Uncomment when uncommenting the test
     // srcs: ["src/**/*.java"]
 
diff --git a/tests/tests/permission2/CtsSMSCallLogPermissionsUserSdk22/Android.bp b/tests/tests/permission2/CtsSMSCallLogPermissionsUserSdk22/Android.bp
index 0fe3599..d1fc86a 100644
--- a/tests/tests/permission2/CtsSMSCallLogPermissionsUserSdk22/Android.bp
+++ b/tests/tests/permission2/CtsSMSCallLogPermissionsUserSdk22/Android.bp
@@ -20,13 +20,6 @@
 
     sdk_version: "current",
 
-    test_suites: [
-        "cts",
-        "vts",
-        "general-tests",
-        "cts_instant",
-    ],
-
     // TODO: Uncomment when uncommenting the test
     // srcs: ["src/**/*.java"]
 
diff --git a/tests/tests/permission2/CtsSMSCallLogPermissionsUserSdk29/Android.bp b/tests/tests/permission2/CtsSMSCallLogPermissionsUserSdk29/Android.bp
index a0189ad..3246d76 100644
--- a/tests/tests/permission2/CtsSMSCallLogPermissionsUserSdk29/Android.bp
+++ b/tests/tests/permission2/CtsSMSCallLogPermissionsUserSdk29/Android.bp
@@ -20,13 +20,6 @@
 
     sdk_version: "current",
 
-    test_suites: [
-        "cts",
-        "vts",
-        "general-tests",
-        "cts_instant",
-    ],
-
     // TODO: Uncomment when uncommenting the test
     // srcs: ["src/**/*.java"]
 
diff --git a/tests/tests/permission2/CtsSMSNotRestrictedWithSharedUid/Android.bp b/tests/tests/permission2/CtsSMSNotRestrictedWithSharedUid/Android.bp
index 9806571..34be9bf 100644
--- a/tests/tests/permission2/CtsSMSNotRestrictedWithSharedUid/Android.bp
+++ b/tests/tests/permission2/CtsSMSNotRestrictedWithSharedUid/Android.bp
@@ -19,11 +19,4 @@
     defaults: ["cts_defaults"],
 
     sdk_version: "current",
-
-    test_suites: [
-        "cts",
-        "vts",
-        "general-tests",
-        "cts_instant",
-    ]
 }
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsSMSRestrictedWithSharedUid/Android.bp b/tests/tests/permission2/CtsSMSRestrictedWithSharedUid/Android.bp
index ec6d128..305125a 100644
--- a/tests/tests/permission2/CtsSMSRestrictedWithSharedUid/Android.bp
+++ b/tests/tests/permission2/CtsSMSRestrictedWithSharedUid/Android.bp
@@ -19,11 +19,4 @@
     defaults: ["cts_defaults"],
 
     sdk_version: "current",
-
-    test_suites: [
-        "cts",
-        "vts",
-        "general-tests",
-        "cts_instant",
-    ]
 }
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk22/Android.bp b/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk22/Android.bp
index f63f14a..8183468 100644
--- a/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk22/Android.bp
+++ b/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk22/Android.bp
@@ -17,11 +17,4 @@
 android_test_helper_app {
     name: "CtsStoragePermissionsUserDefaultSdk22",
     defaults: ["cts_defaults"],
-
-    test_suites: [
-        "cts",
-        "vts",
-        "general-tests",
-        "cts_instant",
-    ]
 }
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk28/Android.bp b/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk28/Android.bp
index be2761a..738f20c 100644
--- a/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk28/Android.bp
+++ b/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk28/Android.bp
@@ -17,11 +17,4 @@
 android_test_helper_app {
     name: "CtsStoragePermissionsUserDefaultSdk28",
     defaults: ["cts_defaults"],
-
-    test_suites: [
-        "cts",
-        "vts",
-        "general-tests",
-        "cts_instant",
-    ]
 }
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk29/Android.bp b/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk29/Android.bp
index eb77c12..53086d3 100644
--- a/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk29/Android.bp
+++ b/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk29/Android.bp
@@ -19,11 +19,4 @@
     defaults: ["cts_defaults"],
 
     sdk_version: "current",
-
-    test_suites: [
-        "cts",
-        "vts",
-        "general-tests",
-        "cts_instant",
-    ]
 }
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsStoragePermissionsUserOptInSdk22/Android.bp b/tests/tests/permission2/CtsStoragePermissionsUserOptInSdk22/Android.bp
index b7f40bf..843176f 100644
--- a/tests/tests/permission2/CtsStoragePermissionsUserOptInSdk22/Android.bp
+++ b/tests/tests/permission2/CtsStoragePermissionsUserOptInSdk22/Android.bp
@@ -17,11 +17,4 @@
 android_test_helper_app {
     name: "CtsStoragePermissionsUserOptInSdk22",
     defaults: ["cts_defaults"],
-
-    test_suites: [
-        "cts",
-        "vts",
-        "general-tests",
-        "cts_instant",
-    ]
 }
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsStoragePermissionsUserOptInSdk28/Android.bp b/tests/tests/permission2/CtsStoragePermissionsUserOptInSdk28/Android.bp
index 63062c1..c0b5fd6 100644
--- a/tests/tests/permission2/CtsStoragePermissionsUserOptInSdk28/Android.bp
+++ b/tests/tests/permission2/CtsStoragePermissionsUserOptInSdk28/Android.bp
@@ -17,11 +17,4 @@
 android_test_helper_app {
     name: "CtsStoragePermissionsUserOptInSdk28",
     defaults: ["cts_defaults"],
-
-    test_suites: [
-        "cts",
-        "vts",
-        "general-tests",
-        "cts_instant",
-    ]
 }
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsStoragePermissionsUserOptOutSdk29/Android.bp b/tests/tests/permission2/CtsStoragePermissionsUserOptOutSdk29/Android.bp
index 9d6a481..e1a43da 100644
--- a/tests/tests/permission2/CtsStoragePermissionsUserOptOutSdk29/Android.bp
+++ b/tests/tests/permission2/CtsStoragePermissionsUserOptOutSdk29/Android.bp
@@ -19,11 +19,4 @@
     defaults: ["cts_defaults"],
 
     sdk_version: "current",
-
-    test_suites: [
-        "cts",
-        "vts",
-        "general-tests",
-        "cts_instant",
-    ]
 }
\ No newline at end of file
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index d43b866..d345d4f 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -102,9 +102,6 @@
     <protected-broadcast android:name="android.os.action.POWER_SAVE_TEMP_WHITELIST_CHANGED" />
     <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED_INTERNAL" />
 
-    <!-- @deprecated This is rarely used and will be phased out soon. -->
-    <protected-broadcast android:name="android.os.action.SCREEN_BRIGHTNESS_BOOST_CHANGED" />
-
     <protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" />
     <protected-broadcast android:name="android.app.action.EXIT_CAR_MODE" />
     <protected-broadcast android:name="android.app.action.ENTER_DESK_MODE" />
@@ -1206,6 +1203,15 @@
         android:description="@string/permdesc_camera"
         android:protectionLevel="dangerous|instant" />
 
+    <!-- @SystemApi Required in addition to android.permission.CAMERA to be able to access
+     system only camera devices.
+     <p>Protection level: system|signature
+     @hide -->
+    <permission android:name="android.permission.SYSTEM_CAMERA"
+                android:permissionGroup="android.permission-group.UNDEFINED"
+                android:label="@string/permlab_systemCamera"
+                android:description="@string/permdesc_systemCamera"
+                android:protectionLevel="system|signature" />
 
     <!-- ====================================================================== -->
     <!-- Permissions for accessing the device sensors                           -->
@@ -1522,18 +1528,6 @@
     <permission android:name="android.permission.OVERRIDE_WIFI_CONFIG"
         android:protectionLevel="signature|privileged" />
 
-    <!-- @hide -->
-    <permission android:name="android.permission.ACCESS_WIMAX_STATE"
-        android:description="@string/permdesc_accessWimaxState"
-        android:label="@string/permlab_accessWimaxState"
-        android:protectionLevel="normal" />
-
-    <!-- @hide -->
-    <permission android:name="android.permission.CHANGE_WIMAX_STATE"
-        android:description="@string/permdesc_changeWimaxState"
-        android:label="@string/permlab_changeWimaxState"
-        android:protectionLevel="normal" />
-
     <!-- Allows applications to act as network scorers. @hide @SystemApi-->
     <permission android:name="android.permission.SCORE_NETWORKS"
         android:protectionLevel="signature|privileged" />
@@ -1542,7 +1536,7 @@
          recommendations and scores from the NetworkScoreService.
          <p>Not for use by third-party applications. @hide -->
     <permission android:name="android.permission.REQUEST_NETWORK_SCORES"
-        android:protectionLevel="signature|setup" />
+        android:protectionLevel="signature|setup|privileged" />
 
     <!-- Allows network stack services (Connectivity and Wifi) to coordinate
          <p>Not for use by third-party or privileged applications.
@@ -1603,12 +1597,6 @@
     <permission android:name="android.permission.MANAGE_LOWPAN_INTERFACES"
         android:protectionLevel="signature|privileged" />
 
-    <!-- @hide Allows internal management of Wi-Fi connectivity state when on
-         wireless consent mode.
-         <p>Not for use by third-party applications. -->
-    <permission android:name="android.permission.MANAGE_WIFI_WHEN_WIRELESS_CONSENT_REQUIRED"
-        android:protectionLevel="signature" />
-
     <!-- #SystemApi @hide Allows an app to bypass Private DNS.
          <p>Not for use by third-party applications.
          TODO: publish as system API in next API release. -->
@@ -2829,6 +2817,11 @@
     <permission android:name="android.permission.STATUS_BAR_SERVICE"
         android:protectionLevel="signature" />
 
+    <!-- Allows an application to trigger bugreports via the shell app (which uses bugreport API)
+    @hide -->
+    <permission android:name="android.permission.TRIGGER_SHELL_BUGREPORT"
+        android:protectionLevel="signature" />
+
     <!-- Allows an application to bind to third party quick settings tiles.
          <p>Should only be requested by the System, should be required by
          TileService declarations.-->
@@ -3457,6 +3450,11 @@
     <permission android:name="android.permission.OBSERVE_ROLE_HOLDERS"
         android:protectionLevel="signature|installer" />
 
+    <!-- Allows an application to manage the companion devices.
+         @hide -->
+    <permission android:name="android.permission.MANAGE_COMPANION_DEVICES"
+                android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows an application to use SurfaceFlinger's low level features.
          <p>Not for use by third-party applications.
          @hide
@@ -4398,12 +4396,12 @@
     <!-- @SystemApi Allows to access all app shortcuts.
          @hide -->
     <permission android:name="android.permission.ACCESS_SHORTCUTS"
-        android:protectionLevel="signature|textClassifier" />
+        android:protectionLevel="signature|appPredictor" />
 
     <!-- @SystemApi Allows unlimited calls to shortcut mutation APIs.
          @hide -->
     <permission android:name="android.permission.UNLIMITED_SHORTCUTS_API_CALLS"
-        android:protectionLevel="signature|textClassifier" />
+        android:protectionLevel="signature|appPredictor" />
 
     <!-- @SystemApi Allows an application to read the runtime profiles of other apps.
          @hide <p>Not for use by third-party applications. -->
@@ -4463,6 +4461,13 @@
     <permission android:name="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Must be required by an {@link android.service.storage.ExternalStorageService} to
+     ensure that only the system can bind to it.
+     @hide This is not a third-party API (intended for OEMs and system apps).
+    -->
+    <permission android:name="android.permission.BIND_EXTERNAL_STORAGE_SERVICE"
+                android:protectionLevel="signature" />
+
     <!-- @hide Permission that allows configuring appops.
      <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.MANAGE_APPOPS"
@@ -4537,6 +4542,11 @@
     <permission android:name="android.permission.MONITOR_INPUT"
                 android:protectionLevel="signature" />
 
+    <!-- Allows query of any normal app on the device, regardless of manifest declarations. -->
+    <permission android:name="android.permission.QUERY_ALL_PACKAGES"
+                android:protectionLevel="normal" />
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+
     <application android:process="system"
         android:persistent="true"
         android:hasCode="false"
diff --git a/tests/tests/preference/OWNERS b/tests/tests/preference/OWNERS
index 827134e..0ea8182 100644
--- a/tests/tests/preference/OWNERS
+++ b/tests/tests/preference/OWNERS
@@ -1,3 +1,4 @@
+# Bug component: 25700
 lpf@google.com
 pavlis@google.com
 clarabayarri@google.com
diff --git a/tests/tests/print/Android.mk b/tests/tests/print/Android.mk
new file mode 100644
index 0000000..f45aed1
--- /dev/null
+++ b/tests/tests/print/Android.mk
@@ -0,0 +1,15 @@
+# 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 $(call all-subdir-makefiles)
diff --git a/tests/tests/provider/OWNERS b/tests/tests/provider/OWNERS
new file mode 100644
index 0000000..fea932d
--- /dev/null
+++ b/tests/tests/provider/OWNERS
@@ -0,0 +1,7 @@
+# Bug component: 1344
+jsharkey@android.com
+omakoto@google.com
+yamasani@google.com
+tgunn@google.com
+nicksauer@google.com
+nona@google.com
diff --git a/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java b/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java
index 3e47d15..e8eb0b2 100644
--- a/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java
+++ b/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java
@@ -16,8 +16,6 @@
 
 package android.provider.cts;
 
-import static android.provider.cts.MediaStoreTest.TAG;
-
 import static com.google.common.truth.Truth.assertWithMessage;
 import static org.junit.Assert.fail;
 
@@ -33,8 +31,9 @@
 import android.os.UserManager;
 import android.provider.MediaStore;
 import android.provider.MediaStore.MediaColumns;
-import android.provider.cts.MediaStoreUtils.PendingParams;
-import android.provider.cts.MediaStoreUtils.PendingSession;
+import android.provider.cts.media.MediaStoreUtils;
+import android.provider.cts.media.MediaStoreUtils.PendingParams;
+import android.provider.cts.media.MediaStoreUtils.PendingSession;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
@@ -65,6 +64,7 @@
  * Utility methods for provider cts tests.
  */
 public class ProviderTestUtils {
+    static final String TAG = "ProviderTestUtils";
 
     private static final int BACKUP_TIMEOUT_MILLIS = 4000;
     private static final Pattern BMGR_ENABLED_PATTERN = Pattern.compile(
@@ -75,7 +75,7 @@
 
     private static final Timeout IO_TIMEOUT = new Timeout("IO_TIMEOUT", 2_000, 2, 2_000);
 
-    static Iterable<String> getSharedVolumeNames() {
+    public static Iterable<String> getSharedVolumeNames() {
         // We test both new and legacy volume names
         final HashSet<String> testVolumes = new HashSet<>();
         testVolumes.addAll(
@@ -84,7 +84,7 @@
         return testVolumes;
     }
 
-    static String resolveVolumeName(String volumeName) {
+    public static String resolveVolumeName(String volumeName) {
         if (MediaStore.VOLUME_EXTERNAL.equals(volumeName)) {
             return MediaStore.VOLUME_EXTERNAL_PRIMARY;
         } else {
@@ -100,12 +100,12 @@
         executeShellCommand(String.format(cmd, packageName, "READ_SMS", mode), uiAutomation);
     }
 
-    static String executeShellCommand(String command) throws IOException {
+    public static String executeShellCommand(String command) throws IOException {
         return executeShellCommand(command,
                 InstrumentationRegistry.getInstrumentation().getUiAutomation());
     }
 
-    static String executeShellCommand(String command, UiAutomation uiAutomation)
+    public static String executeShellCommand(String command, UiAutomation uiAutomation)
             throws IOException {
         Log.v(TAG, "$ " + command);
         ParcelFileDescriptor pfd = uiAutomation.executeShellCommand(command.toString());
@@ -197,7 +197,7 @@
         }
     }
 
-    static File stageDir(String volumeName) throws IOException {
+    public static File stageDir(String volumeName) throws IOException {
         if (MediaStore.VOLUME_EXTERNAL.equals(volumeName)) {
             volumeName = MediaStore.VOLUME_EXTERNAL_PRIMARY;
         }
@@ -207,7 +207,7 @@
         return dir;
     }
 
-    static File stageDownloadDir(String volumeName) throws IOException {
+    public static File stageDownloadDir(String volumeName) throws IOException {
         if (MediaStore.VOLUME_EXTERNAL.equals(volumeName)) {
             volumeName = MediaStore.VOLUME_EXTERNAL_PRIMARY;
         }
@@ -215,7 +215,7 @@
                 Environment.DIRECTORY_DOWNLOADS, "android.provider.cts");
     }
 
-    static File stageFile(int resId, File file) throws IOException {
+    public 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
         final Context context = InstrumentationRegistry.getTargetContext();
@@ -248,11 +248,11 @@
         return waitUntilExists(file);
     }
 
-    static Uri stageMedia(int resId, Uri collectionUri) throws IOException {
+    public static Uri stageMedia(int resId, Uri collectionUri) throws IOException {
         return stageMedia(resId, collectionUri, "image/png");
     }
 
-    static Uri stageMedia(int resId, Uri collectionUri, String mimeType) throws IOException {
+    public static Uri stageMedia(int resId, Uri collectionUri, String mimeType) throws IOException {
         final Context context = InstrumentationRegistry.getTargetContext();
         final String displayName = "cts" + System.nanoTime();
         final PendingParams params = new PendingParams(collectionUri, displayName, mimeType);
@@ -266,19 +266,19 @@
         }
     }
 
-    static Uri scanFile(File file) throws Exception {
+    public static Uri scanFile(File file) throws Exception {
         Uri uri = MediaStore.scanFile(InstrumentationRegistry.getTargetContext(), file);
         assertWithMessage("no URI for '%s'", file).that(uri).isNotNull();
         return uri;
     }
 
-    static Uri scanFileFromShell(File file) throws Exception {
+    public static Uri scanFileFromShell(File file) throws Exception {
         Uri uri = MediaStore.scanFileFromShell(InstrumentationRegistry.getTargetContext(), file);
         assertWithMessage("no URI for '%s'", file).that(uri).isNotNull();
         return uri;
     }
 
-    static void scanVolume(File file) throws Exception {
+    public static void scanVolume(File file) throws Exception {
         MediaStore.scanVolume(InstrumentationRegistry.getTargetContext(), file);
     }
 
diff --git a/tests/tests/provider/src/android/provider/cts/CalendarTest.java b/tests/tests/provider/src/android/provider/cts/calendar/CalendarTest.java
similarity index 99%
rename from tests/tests/provider/src/android/provider/cts/CalendarTest.java
rename to tests/tests/provider/src/android/provider/cts/calendar/CalendarTest.java
index fcd873a..c97d4d9 100644
--- a/tests/tests/provider/src/android/provider/cts/CalendarTest.java
+++ b/tests/tests/provider/src/android/provider/cts/calendar/CalendarTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.calendar;
 
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
diff --git a/tests/tests/provider/src/android/provider/cts/CallLogTest.java b/tests/tests/provider/src/android/provider/cts/contacts/CallLogTest.java
similarity index 98%
rename from tests/tests/provider/src/android/provider/cts/CallLogTest.java
rename to tests/tests/provider/src/android/provider/cts/contacts/CallLogTest.java
index 77985c6..be71f6a 100644
--- a/tests/tests/provider/src/android/provider/cts/CallLogTest.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/CallLogTest.java
@@ -14,7 +14,7 @@
  * limitations under the License
  */
 
-package android.provider.cts;
+package android.provider.cts.contacts;
 
 import android.content.ContentResolver;
 import android.content.ContentValues;
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_ContactsTest.java b/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_ContactsTest.java
index c9d1371..cb3a2a6 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_ContactsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_ContactsTest.java
@@ -156,6 +156,93 @@
         RawContactUtil.delete(mResolver, ids.mRawContactId, true);
     }
 
+    public void testContactDelete_localContactDeletedImmediately() {
+        // Create a raw contact in the local (null) account
+        DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(
+                mResolver, null);
+
+        ContactUtil.delete(mResolver, ids.mContactId);
+
+        // Assert that the local raw contact is removed from the database and
+        // not merely marked DELETED=1.
+        assertNull(RawContactUtil.queryByRawContactId(mResolver, ids.mRawContactId, null));
+
+        // Nothing to clean up
+    }
+
+    public void testContactDelete_allLocalContactsDeletedImmediately() {
+        // Create two raw contacts in the local (null) account
+        DatabaseAsserts.ContactIdPair ids1 = DatabaseAsserts.assertAndCreateContactWithName(
+                mResolver, null, "John Smith");
+        DatabaseAsserts.ContactIdPair ids2 = DatabaseAsserts.assertAndCreateContactWithName(
+                mResolver, null, "John Smith");
+
+        // Aggregate the two raw contacts together
+        ContactUtil.setAggregationException(mResolver,
+                ContactsContract.AggregationExceptions.TYPE_KEEP_TOGETHER, ids1.mRawContactId,
+                ids2.mRawContactId);
+
+        // Assert that the contacts were aggregated together
+        long contactId1 = RawContactUtil.queryContactIdByRawContactId(mResolver,
+                ids1.mRawContactId);
+        long contactId2 = RawContactUtil.queryContactIdByRawContactId(mResolver,
+                ids2.mRawContactId);
+        assertEquals(contactId1, contactId2);
+
+        // Delete the contact
+        ContactUtil.delete(mResolver, contactId1);
+
+        // Assert that both of the local raw contacts were removed from the database and
+        // not merely marked DELETED=1.
+        assertNull(RawContactUtil.queryByRawContactId(mResolver, ids1.mRawContactId, null));
+        assertNull(RawContactUtil.queryByRawContactId(mResolver, ids2.mRawContactId, null));
+
+        // Nothing to clean up
+    }
+
+    public void testContactDelete_localContactDeletedImmediatelyWhenAggregatedWithNonLocal() {
+        // Create a raw contact in the local (null) account
+        DatabaseAsserts.ContactIdPair ids1 = DatabaseAsserts.assertAndCreateContactWithName(
+                mResolver, null, "John Smith");
+
+        // Create a raw contact in a non-local account with the same name
+        DatabaseAsserts.ContactIdPair ids2 = DatabaseAsserts.assertAndCreateContactWithName(
+                mResolver, StaticAccountAuthenticator.ACCOUNT_1, "John Smith");
+
+        // Aggregate the two raw contacts together
+        ContactUtil.setAggregationException(mResolver,
+                ContactsContract.AggregationExceptions.TYPE_KEEP_TOGETHER, ids1.mRawContactId,
+                ids2.mRawContactId);
+
+        // Assert that the contacts were aggregated together
+        long contactId1 = RawContactUtil.queryContactIdByRawContactId(mResolver,
+                ids1.mRawContactId);
+        long contactId2 = RawContactUtil.queryContactIdByRawContactId(mResolver,
+                ids2.mRawContactId);
+        assertEquals(contactId1, contactId2);
+
+        // Delete the contact
+        ContactUtil.delete(mResolver, contactId1);
+
+        // Assert that the local raw contact was removed from the database
+        assertNull(RawContactUtil.queryByRawContactId(mResolver, ids1.mRawContactId, null));
+
+        // Assert that the non-local raw contact was marked DELETED=1
+        String[] projection = new String[]{
+                ContactsContract.RawContacts.DIRTY,
+                ContactsContract.RawContacts.DELETED
+        };
+        List<String[]> records = RawContactUtil.queryByContactId(mResolver, ids2.mContactId,
+                projection);
+        for (String[] arr : records) {
+            assertEquals("1", arr[0]);
+            assertEquals("1", arr[1]);
+        }
+
+        // Clean up
+        RawContactUtil.delete(mResolver, ids2.mRawContactId, true);
+    }
+
     public void testContactUpdate_updatesContactUpdatedTimestamp() {
         DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver);
 
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_RawContactsTest.java b/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_RawContactsTest.java
index 89889f7..dc49a2e 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_RawContactsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_RawContactsTest.java
@@ -141,6 +141,18 @@
         assertEquals("1", result[1]);
     }
 
+    public void testRawContactDelete_localDeleteRemovesRecord() {
+        long rawContactid = RawContactUtil.insertRawContact(mResolver, null);
+        assertTrue(RawContactUtil.rawContactExistsById(mResolver, rawContactid));
+
+        // Local raw contacts should be deleted immediately even if isSyncAdapter=false
+        RawContactUtil.delete(mResolver, rawContactid, false);
+
+        assertFalse(RawContactUtil.rawContactExistsById(mResolver, rawContactid));
+
+        // Nothing to clean up
+    }
+
     public void testRawContactDelete_removesRecord() {
         long rawContactid = RawContactUtil.insertRawContact(mResolver,
                 StaticAccountAuthenticator.ACCOUNT_1);
@@ -153,7 +165,6 @@
         // already clean
     }
 
-
     // This implicitly tests the Contact create case.
     public void testRawContactCreate_updatesContactUpdatedTimestamp() {
         long startTime = System.currentTimeMillis();
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/RawContactUtil.java b/tests/tests/provider/src/android/provider/cts/contacts/RawContactUtil.java
index b8e1576..e21190b 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/RawContactUtil.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/RawContactUtil.java
@@ -55,8 +55,10 @@
 
     public static long insertRawContact(ContentResolver resolver, Account account) {
         ContentValues values = new ContentValues();
-        values.put(ContactsContract.RawContacts.ACCOUNT_NAME, account.name);
-        values.put(ContactsContract.RawContacts.ACCOUNT_TYPE, account.type);
+        if (account != null) {
+            values.put(ContactsContract.RawContacts.ACCOUNT_NAME, account.name);
+            values.put(ContactsContract.RawContacts.ACCOUNT_TYPE, account.type);
+        }
         Uri uri = resolver.insert(URI, values);
         return ContentUris.parseId(uri);
     }
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStoreAudioTestHelper.java b/tests/tests/provider/src/android/provider/cts/media/MediaStoreAudioTestHelper.java
similarity index 98%
rename from tests/tests/provider/src/android/provider/cts/MediaStoreAudioTestHelper.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStoreAudioTestHelper.java
index a3ef607..2ae1d6f 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStoreAudioTestHelper.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStoreAudioTestHelper.java
@@ -14,13 +14,14 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.media;
 
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.net.Uri;
 import android.provider.MediaStore;
 import android.provider.MediaStore.Audio.Media;
+import android.provider.cts.ProviderTestUtils;
 
 import androidx.test.runner.AndroidJUnit4;
 
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStoreIntentsTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStoreIntentsTest.java
similarity index 97%
rename from tests/tests/provider/src/android/provider/cts/MediaStoreIntentsTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStoreIntentsTest.java
index 5590a6d..d5442e0 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStoreIntentsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStoreIntentsTest.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.media;
 
-import static android.provider.cts.MediaStoreTest.TAG;
+import static android.provider.cts.media.MediaStoreTest.TAG;
 
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
@@ -25,6 +25,7 @@
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.provider.MediaStore;
+import android.provider.cts.ProviderTestUtils;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
diff --git a/tests/tests/provider/src/android/provider/cts/media/MediaStoreNotificationTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStoreNotificationTest.java
new file mode 100644
index 0000000..e227759
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStoreNotificationTest.java
@@ -0,0 +1,193 @@
+/*
+ * 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.provider.cts.media;
+
+import static android.provider.cts.media.MediaStoreTest.TAG;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.provider.MediaStore;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Ignore;
+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.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(Parameterized.class)
+public class MediaStoreNotificationTest {
+    private Context mContext;
+    private ContentResolver mResolver;
+
+    private Uri mSpecificImages;
+    private Uri mSpecificFiles;
+    private Uri mGenericImages;
+    private Uri mGenericFiles;
+
+    @Parameter(0)
+    public String mVolumeName;
+
+    @Parameters
+    public static Iterable<? extends Object> data() {
+        return MediaStore.getExternalVolumeNames(InstrumentationRegistry.getTargetContext());
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mResolver = mContext.getContentResolver();
+
+        Log.d(TAG, "Using volume " + mVolumeName);
+        mSpecificImages = MediaStore.Images.Media.getContentUri(mVolumeName);
+        mSpecificFiles = MediaStore.Files.getContentUri(mVolumeName);
+        mGenericImages = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
+        mGenericFiles = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL);
+    }
+
+    @Test
+    public void testSimple() throws Exception {
+        Uri specificImage;
+        Uri specificFile;
+        Uri genericImage;
+        Uri genericFile;
+
+        try (BlockingObserver si = BlockingObserver.createAndRegister(mSpecificImages);
+                BlockingObserver sf = BlockingObserver.createAndRegister(mSpecificFiles);
+                BlockingObserver gi = BlockingObserver.createAndRegister(mGenericImages);
+                BlockingObserver gf = BlockingObserver.createAndRegister(mGenericFiles)) {
+            final ContentValues values = new ContentValues();
+            values.put(MediaStore.Images.Media.IS_PENDING, 1);
+            values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
+            values.put(MediaStore.Images.Media.DISPLAY_NAME, "cts" + System.nanoTime());
+            specificImage = mResolver.insert(mSpecificImages, values);
+
+            final long id = ContentUris.parseId(specificImage);
+            specificFile = ContentUris.withAppendedId(mSpecificFiles, id);
+            genericImage = ContentUris.withAppendedId(mGenericImages, id);
+            genericFile = ContentUris.withAppendedId(mGenericFiles, id);
+        }
+
+        try (BlockingObserver si = BlockingObserver.createAndRegister(specificImage);
+                BlockingObserver sf = BlockingObserver.createAndRegister(specificFile);
+                BlockingObserver gi = BlockingObserver.createAndRegister(genericImage);
+                BlockingObserver gf = BlockingObserver.createAndRegister(genericFile)) {
+            final ContentValues values = new ContentValues();
+            values.put(MediaStore.Images.Media.SIZE, 32);
+            mResolver.update(specificImage, values, null, null);
+        }
+
+        try (BlockingObserver si = BlockingObserver.createAndRegister(specificImage);
+                BlockingObserver sf = BlockingObserver.createAndRegister(specificFile);
+                BlockingObserver gi = BlockingObserver.createAndRegister(genericImage);
+                BlockingObserver gf = BlockingObserver.createAndRegister(genericFile)) {
+            mResolver.delete(specificImage, null, null);
+        }
+    }
+
+    @Test
+    @Ignore("b/139110347")
+    public void testCursor() throws Exception {
+        try (Cursor si = mResolver.query(mSpecificImages, null, null, null);
+                Cursor sf = mResolver.query(mSpecificFiles, null, null, null);
+                Cursor gi = mResolver.query(mGenericImages, null, null, null);
+                Cursor gf = mResolver.query(mGenericFiles, null, null, null)) {
+            try (BlockingObserver sio = BlockingObserver.create();
+                    BlockingObserver sfo = BlockingObserver.create();
+                    BlockingObserver gio = BlockingObserver.create();
+                    BlockingObserver gfo = BlockingObserver.create()) {
+                si.registerContentObserver(sio);
+                sf.registerContentObserver(sfo);
+                gi.registerContentObserver(gio);
+                gf.registerContentObserver(gfo);
+
+                // Insert a simple item that will trigger notifications
+                final ContentValues values = new ContentValues();
+                values.put(MediaStore.Images.Media.IS_PENDING, 1);
+                values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
+                values.put(MediaStore.Images.Media.DISPLAY_NAME, "cts" + System.nanoTime());
+                final Uri uri = mResolver.insert(mSpecificImages, values);
+                mResolver.delete(uri, null, null);
+            }
+        }
+    }
+
+    private static class BlockingObserver extends ContentObserver implements AutoCloseable {
+        private final Uri mUri;
+        private final CountDownLatch mLatch = new CountDownLatch(1);
+
+        private static HandlerThread sHandlerThread;
+        private static Handler sHandler;
+
+        static {
+            sHandlerThread = new HandlerThread(TAG);
+            sHandlerThread.start();
+            sHandler = new Handler(sHandlerThread.getLooper());
+        }
+
+        private BlockingObserver(Uri uri) {
+            super(sHandler);
+            mUri = uri;
+        }
+
+        public static BlockingObserver create() {
+            return new BlockingObserver(null);
+        }
+
+        public static BlockingObserver createAndRegister(Uri uri) {
+            final BlockingObserver obs = new BlockingObserver(uri);
+            InstrumentationRegistry.getTargetContext().getContentResolver()
+                    .registerContentObserver(uri, true, obs);
+            return obs;
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            super.onChange(selfChange, uri);
+            Log.v(TAG, "Notified about " + uri);
+            mLatch.countDown();
+        }
+
+        @Override
+        public void close() {
+            try {
+                if (!mLatch.await(5, TimeUnit.SECONDS)) {
+                    throw new InterruptedException();
+                }
+            } catch (InterruptedException e) {
+                throw new IllegalStateException("Failed to get notification for " + mUri);
+            } finally {
+                InstrumentationRegistry.getTargetContext().getContentResolver()
+                        .unregisterContentObserver(this);
+            }
+        }
+    }
+}
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStorePendingTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStorePendingTest.java
similarity index 98%
rename from tests/tests/provider/src/android/provider/cts/MediaStorePendingTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStorePendingTest.java
index 85d4109..ae5674b 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStorePendingTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStorePendingTest.java
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.media;
 
-import static android.provider.cts.MediaStoreTest.TAG;
 import static android.provider.cts.ProviderTestUtils.containsId;
 import static android.provider.cts.ProviderTestUtils.getRawFile;
 import static android.provider.cts.ProviderTestUtils.getRawFileHash;
 import static android.provider.cts.ProviderTestUtils.hash;
+import static android.provider.cts.media.MediaStoreTest.TAG;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -36,11 +36,12 @@
 import android.net.Uri;
 import android.os.Environment;
 import android.os.FileUtils;
-import android.platform.test.annotations.Presubmit;
 import android.provider.MediaStore;
 import android.provider.MediaStore.MediaColumns;
-import android.provider.cts.MediaStoreUtils.PendingParams;
-import android.provider.cts.MediaStoreUtils.PendingSession;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
+import android.provider.cts.media.MediaStoreUtils.PendingParams;
+import android.provider.cts.media.MediaStoreUtils.PendingSession;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -62,7 +63,6 @@
 import java.util.HashSet;
 import java.util.Set;
 
-@Presubmit
 @RunWith(Parameterized.class)
 public class MediaStorePendingTest {
     private Context mContext;
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStorePlacementTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStorePlacementTest.java
similarity index 98%
rename from tests/tests/provider/src/android/provider/cts/MediaStorePlacementTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStorePlacementTest.java
index c05379c..405a70a 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStorePlacementTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStorePlacementTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.media;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -26,9 +26,10 @@
 import android.content.Context;
 import android.net.Uri;
 import android.os.Environment;
-import android.platform.test.annotations.Presubmit;
 import android.provider.MediaStore;
 import android.provider.MediaStore.MediaColumns;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -44,7 +45,6 @@
 import java.io.File;
 import java.util.Optional;
 
-@Presubmit
 @RunWith(Parameterized.class)
 public class MediaStorePlacementTest {
     static final String TAG = "MediaStorePlacementTest";
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStoreTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStoreTest.java
similarity index 94%
rename from tests/tests/provider/src/android/provider/cts/MediaStoreTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStoreTest.java
index 937bddf..5f9d6eb 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStoreTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStoreTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.media;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -32,9 +32,10 @@
 import android.os.SystemClock;
 import android.os.storage.StorageManager;
 import android.os.storage.StorageVolume;
-import android.platform.test.annotations.Presubmit;
 import android.provider.MediaStore;
 import android.provider.MediaStore.MediaColumns;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -53,7 +54,6 @@
 import java.util.Set;
 import java.util.UUID;
 
-@Presubmit
 @RunWith(Parameterized.class)
 public class MediaStoreTest {
     static final String TAG = "MediaStoreTest";
@@ -93,6 +93,18 @@
     }
 
     @Test
+    public void testIncludePending() {
+        assertFalse(MediaStore.getIncludePending(mExternalImages));
+        assertTrue(MediaStore.getIncludePending(MediaStore.setIncludePending(mExternalImages)));
+    }
+
+    @Test
+    public void testRequireOriginal() {
+        assertFalse(MediaStore.getRequireOriginal(mExternalImages));
+        assertTrue(MediaStore.getRequireOriginal(MediaStore.setRequireOriginal(mExternalImages)));
+    }
+
+    @Test
     public void testGetMediaScannerUri() {
         // query
         Cursor c = mContentResolver.query(MediaStore.getMediaScannerUri(), null,
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStoreUtils.java b/tests/tests/provider/src/android/provider/cts/media/MediaStoreUtils.java
similarity index 99%
rename from tests/tests/provider/src/android/provider/cts/MediaStoreUtils.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStoreUtils.java
index 70c6988..0120afb 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStoreUtils.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStoreUtils.java
@@ -14,7 +14,7 @@
  * limitations under the License
  */
 
-package android.provider.cts;
+package android.provider.cts.media;
 
 import android.content.ContentValues;
 import android.content.Context;
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_AudioTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_AudioTest.java
similarity index 96%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_AudioTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_AudioTest.java
index a29b93d..2d99160 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_AudioTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_AudioTest.java
@@ -14,12 +14,11 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.media;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import android.platform.test.annotations.Presubmit;
 import android.provider.MediaStore.Audio;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -28,7 +27,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class MediaStore_AudioTest {
     private String mKeyForBeatles;
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_AlbumsTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_AlbumsTest.java
similarity index 96%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Audio_AlbumsTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_AlbumsTest.java
index 9d17b40..ca0413c 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_AlbumsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_AlbumsTest.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.media;
 
-import static android.provider.cts.MediaStoreTest.TAG;
+import static android.provider.cts.media.MediaStoreTest.TAG;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -32,12 +32,13 @@
 import android.database.Cursor;
 import android.graphics.BitmapFactory;
 import android.net.Uri;
-import android.platform.test.annotations.Presubmit;
 import android.provider.MediaStore;
 import android.provider.MediaStore.Audio.Albums;
 import android.provider.MediaStore.Audio.Media;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio1;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio2;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio1;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio2;
 import android.util.Log;
 import android.util.Size;
 
@@ -53,7 +54,6 @@
 import java.io.File;
 import java.io.IOException;
 
-@Presubmit
 @RunWith(Parameterized.class)
 public class MediaStore_Audio_AlbumsTest {
     private Context mContext;
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_ArtistsTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_ArtistsTest.java
similarity index 94%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Audio_ArtistsTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_ArtistsTest.java
index 5caefc5..6ab7c28 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_ArtistsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_ArtistsTest.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.media;
 
-import static android.provider.cts.MediaStoreTest.TAG;
+import static android.provider.cts.media.MediaStoreTest.TAG;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -28,10 +28,10 @@
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
-import android.platform.test.annotations.Presubmit;
 import android.provider.MediaStore.Audio.Artists;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio1;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio2;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio1;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio2;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -43,7 +43,6 @@
 import org.junit.runners.Parameterized.Parameter;
 import org.junit.runners.Parameterized.Parameters;
 
-@Presubmit
 @RunWith(Parameterized.class)
 public class MediaStore_Audio_ArtistsTest {
     private Context mContext;
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Artists_AlbumsTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_Artists_AlbumsTest.java
similarity index 94%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Artists_AlbumsTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_Artists_AlbumsTest.java
index 5a28865..a3bd099 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Artists_AlbumsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_Artists_AlbumsTest.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.media;
 
-import static android.provider.cts.MediaStoreTest.TAG;
+import static android.provider.cts.media.MediaStoreTest.TAG;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -29,12 +29,12 @@
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
-import android.platform.test.annotations.Presubmit;
 import android.provider.MediaStore;
 import android.provider.MediaStore.Audio.Artists.Albums;
 import android.provider.MediaStore.Audio.Media;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio1;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio2;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio1;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio2;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -46,7 +46,6 @@
 import org.junit.runners.Parameterized.Parameter;
 import org.junit.runners.Parameterized.Parameters;
 
-@Presubmit
 @RunWith(Parameterized.class)
 public class MediaStore_Audio_Artists_AlbumsTest {
     private Context mContext;
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_GenresTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_GenresTest.java
similarity index 93%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Audio_GenresTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_GenresTest.java
index 64ed537..7c183e6 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_GenresTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_GenresTest.java
@@ -14,28 +14,25 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.media;
 
-import static android.provider.cts.MediaStoreTest.TAG;
+import static android.provider.cts.media.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;
-import static org.junit.Assert.fail;
 
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
-import android.database.SQLException;
 import android.net.Uri;
-import android.platform.test.annotations.Presubmit;
 import android.provider.MediaStore.Audio.Genres;
 import android.provider.MediaStore.Audio.Genres.Members;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio1;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio1;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -47,7 +44,6 @@
 import org.junit.runners.Parameterized.Parameter;
 import org.junit.runners.Parameterized.Parameters;
 
-@Presubmit
 @RunWith(Parameterized.class)
 public class MediaStore_Audio_GenresTest {
     private Context mContext;
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Genres_MembersTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_Genres_MembersTest.java
similarity index 97%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Genres_MembersTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_Genres_MembersTest.java
index 710ebbc..6fd5b9b 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Genres_MembersTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_Genres_MembersTest.java
@@ -14,13 +14,12 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.media;
 
-import static android.provider.cts.MediaStoreTest.TAG;
+import static android.provider.cts.media.MediaStoreTest.TAG;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -29,15 +28,14 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
-import android.database.SQLException;
 import android.net.Uri;
-import android.platform.test.annotations.Presubmit;
 import android.provider.MediaStore;
 import android.provider.MediaStore.Audio.Genres;
 import android.provider.MediaStore.Audio.Genres.Members;
 import android.provider.MediaStore.Audio.Media;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio1;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio2;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio1;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio2;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -50,7 +48,6 @@
 import org.junit.runners.Parameterized.Parameter;
 import org.junit.runners.Parameterized.Parameters;
 
-@Presubmit
 @RunWith(Parameterized.class)
 public class MediaStore_Audio_Genres_MembersTest {
     private Context mContext;
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_MediaTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_MediaTest.java
similarity index 95%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Audio_MediaTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_MediaTest.java
index 4a8afe7..ae57425 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_MediaTest.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.media;
 
-import static android.provider.cts.MediaStoreTest.TAG;
+import static android.provider.cts.media.MediaStoreTest.TAG;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -24,15 +24,17 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.ContentResolver;
+import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Environment;
-import android.platform.test.annotations.Presubmit;
 import android.provider.MediaStore;
 import android.provider.MediaStore.Audio.Media;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio1;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio1;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -46,7 +48,6 @@
 
 import java.io.File;
 
-@Presubmit
 @RunWith(Parameterized.class)
 public class MediaStore_Audio_MediaTest {
     private Context mContext;
@@ -78,6 +79,9 @@
                 Media.getContentUri(mVolumeName), null, null,
                     null, null));
         c.close();
+
+        assertEquals(ContentUris.withAppendedId(Media.getContentUri(mVolumeName), 42),
+                Media.getContentUri(mVolumeName, 42));
     }
 
     @Test
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_PlaylistsTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_PlaylistsTest.java
similarity index 94%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Audio_PlaylistsTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_PlaylistsTest.java
index 78cbef0..289d3ac 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_PlaylistsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_PlaylistsTest.java
@@ -14,23 +14,21 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.media;
 
-import static android.provider.cts.MediaStoreTest.TAG;
+import static android.provider.cts.media.MediaStoreTest.TAG;
 
 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.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
-import android.database.SQLException;
 import android.net.Uri;
-import android.platform.test.annotations.Presubmit;
 import android.provider.MediaStore.Audio.Playlists;
+import android.provider.cts.ProviderTestUtils;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -44,7 +42,6 @@
 
 import java.io.File;
 
-@Presubmit
 @RunWith(Parameterized.class)
 public class MediaStore_Audio_PlaylistsTest {
     private Context mContext;
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Playlists_MembersTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_Playlists_MembersTest.java
similarity index 97%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Playlists_MembersTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_Playlists_MembersTest.java
index 0db80d9..9135a4b 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Playlists_MembersTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_Playlists_MembersTest.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.media;
 
-import static android.provider.cts.MediaStoreTest.TAG;
+import static android.provider.cts.media.MediaStoreTest.TAG;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -29,17 +29,17 @@
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
-import android.platform.test.annotations.Presubmit;
 import android.provider.MediaStore;
 import android.provider.MediaStore.Audio.Media;
 import android.provider.MediaStore.Audio.Playlists;
 import android.provider.MediaStore.Audio.Playlists.Members;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio1;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio2;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio3;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio4;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio5;
-import android.provider.cts.MediaStoreAudioTestHelper.MockAudioMediaInfo;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio1;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio2;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio3;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio4;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio5;
+import android.provider.cts.media.MediaStoreAudioTestHelper.MockAudioMediaInfo;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -52,7 +52,6 @@
 import org.junit.runners.Parameterized.Parameter;
 import org.junit.runners.Parameterized.Parameters;
 
-@Presubmit
 @RunWith(Parameterized.class)
 public class MediaStore_Audio_Playlists_MembersTest {
     private String[] mAudioProjection = {
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_DownloadsTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_DownloadsTest.java
similarity index 97%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_DownloadsTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_DownloadsTest.java
index 1c45687..0b6af08 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_DownloadsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_DownloadsTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.media;
 
 import static android.provider.cts.ProviderTestUtils.hash;
 import static android.provider.cts.ProviderTestUtils.resolveVolumeName;
@@ -32,13 +32,14 @@
 import android.net.Uri;
 import android.os.Environment;
 import android.os.FileUtils;
-import android.platform.test.annotations.Presubmit;
 import android.provider.MediaStore;
 import android.provider.MediaStore.Downloads;
 import android.provider.MediaStore.Files;
 import android.provider.MediaStore.Images;
-import android.provider.cts.MediaStoreUtils.PendingParams;
-import android.provider.cts.MediaStoreUtils.PendingSession;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
+import android.provider.cts.media.MediaStoreUtils.PendingParams;
+import android.provider.cts.media.MediaStoreUtils.PendingSession;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -62,7 +63,6 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-@Presubmit
 @RunWith(Parameterized.class)
 public class MediaStore_DownloadsTest {
     private static final String TAG = MediaStore_DownloadsTest.class.getSimpleName();
@@ -138,6 +138,9 @@
         assertNotNull(c = mContentResolver.query(mExternalDownloads,
                 null, null, null, null));
         c.close();
+
+        assertEquals(ContentUris.withAppendedId(Downloads.getContentUri(mVolumeName), 42),
+                Downloads.getContentUri(mVolumeName, 42));
     }
 
     @Test
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_FilesTest.java
similarity index 97%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_FilesTest.java
index 9e71c69..757766e 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_FilesTest.java
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.media;
 
-import static android.provider.cts.MediaStoreTest.TAG;
 import static android.provider.cts.ProviderTestUtils.containsId;
 import static android.provider.cts.ProviderTestUtils.resolveVolumeName;
+import static android.provider.cts.media.MediaStoreTest.TAG;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -35,10 +35,11 @@
 import android.net.Uri;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
-import android.platform.test.annotations.Presubmit;
 import android.provider.MediaStore;
 import android.provider.MediaStore.Files.FileColumns;
 import android.provider.MediaStore.MediaColumns;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -53,7 +54,6 @@
 import java.io.File;
 import java.io.IOException;
 
-@Presubmit
 @RunWith(Parameterized.class)
 public class MediaStore_FilesTest {
     private Context mContext;
@@ -158,6 +158,9 @@
         } finally {
             cursor.close();
         }
+
+        assertEquals(ContentUris.withAppendedId(MediaStore.Files.getContentUri(mVolumeName), 42),
+                MediaStore.Files.getContentUri(mVolumeName, 42));
     }
 
     @Test
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_MediaTest.java
similarity index 97%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_MediaTest.java
index ec78a94..67df33a 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_MediaTest.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.media;
 
-import static android.provider.cts.MediaStoreTest.TAG;
+import static android.provider.cts.media.MediaStoreTest.TAG;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -26,6 +26,7 @@
 import static org.junit.Assert.fail;
 
 import android.content.ContentResolver;
+import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
@@ -35,19 +36,18 @@
 import android.net.Uri;
 import android.os.ParcelFileDescriptor;
 import android.os.storage.StorageManager;
-import android.platform.test.annotations.Presubmit;
 import android.provider.MediaStore;
 import android.provider.MediaStore.Images.ImageColumns;
 import android.provider.MediaStore.Images.Media;
-import android.provider.cts.MediaStoreUtils.PendingParams;
-import android.provider.cts.MediaStoreUtils.PendingSession;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
+import android.provider.cts.media.MediaStoreUtils.PendingParams;
+import android.provider.cts.media.MediaStoreUtils.PendingSession;
 import android.util.Log;
 import android.util.Size;
 
 import androidx.test.InstrumentationRegistry;
 
-import com.android.compatibility.common.util.FileUtils;
-
 import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
@@ -62,7 +62,6 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 
-@Presubmit
 @RunWith(Parameterized.class)
 public class MediaStore_Images_MediaTest {
     private static final String MIME_TYPE_JPEG = "image/jpeg";
@@ -200,7 +199,6 @@
         assertEquals(src.getHeight(), result.getHeight());
     }
 
-    @Presubmit
     @Test
     public void testGetContentUri() {
         Cursor c = null;
@@ -210,6 +208,9 @@
         assertNotNull(c = mContentResolver.query(Media.getContentUri(mVolumeName), null, null, null,
                 null));
         c.close();
+
+        assertEquals(ContentUris.withAppendedId(Media.getContentUri(mVolumeName), 42),
+                Media.getContentUri(mVolumeName, 42));
     }
 
     private void cleanExternalMediaFile(String path) {
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_ThumbnailsTest.java
similarity index 98%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_ThumbnailsTest.java
index 2d28405..5d0199b 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_ThumbnailsTest.java
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.media;
 
-import static android.provider.cts.MediaStoreTest.TAG;
 import static android.provider.cts.ProviderTestUtils.assertExists;
 import static android.provider.cts.ProviderTestUtils.assertNotExists;
+import static android.provider.cts.media.MediaStoreTest.TAG;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -41,13 +41,14 @@
 import android.graphics.ImageDecoder;
 import android.net.Uri;
 import android.os.Environment;
-import android.platform.test.annotations.Presubmit;
 import android.provider.MediaStore;
 import android.provider.MediaStore.Images.Media;
 import android.provider.MediaStore.Images.Thumbnails;
 import android.provider.MediaStore.MediaColumns;
-import android.provider.cts.MediaStoreUtils.PendingParams;
-import android.provider.cts.MediaStoreUtils.PendingSession;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
+import android.provider.cts.media.MediaStoreUtils.PendingParams;
+import android.provider.cts.media.MediaStoreUtils.PendingSession;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Size;
@@ -69,7 +70,6 @@
 import java.io.OutputStream;
 import java.util.ArrayList;
 
-@Presubmit
 @RunWith(Parameterized.class)
 public class MediaStore_Images_ThumbnailsTest {
     private ArrayList<Uri> mRowsAdded;
@@ -125,6 +125,7 @@
         mBlue = ProviderTestUtils.stageMedia(R.raw.scenery, mExternalImages);
         mRowsAdded.add(mRed);
         mRowsAdded.add(mBlue);
+        MediaStore.waitForIdle(mContext);
     }
 
     public static void assertMostlyEquals(long expected, long actual, long delta) {
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_VideoTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_VideoTest.java
similarity index 93%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_VideoTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_VideoTest.java
index 98242b8..202c3ea 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_VideoTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_VideoTest.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.media;
 
-import static android.provider.cts.MediaStoreTest.TAG;
+import static android.provider.cts.media.MediaStoreTest.TAG;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -26,10 +26,11 @@
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
-import android.platform.test.annotations.Presubmit;
 import android.provider.MediaStore;
 import android.provider.MediaStore.Video;
 import android.provider.MediaStore.Video.VideoColumns;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -43,7 +44,6 @@
 
 import java.io.File;
 
-@Presubmit
 @RunWith(Parameterized.class)
 public class MediaStore_VideoTest {
     private Context mContext;
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_MediaTest.java
similarity index 92%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_MediaTest.java
index 6a7a1f9..c53536e 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_MediaTest.java
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.media;
 
-import static android.provider.cts.MediaStoreTest.TAG;
 import static android.provider.cts.ProviderTestUtils.assertExists;
 import static android.provider.cts.ProviderTestUtils.assertNotExists;
+import static android.provider.cts.media.MediaStoreTest.TAG;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -28,26 +28,26 @@
 import static org.junit.Assert.fail;
 
 import android.content.ContentResolver;
+import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
-import android.media.MediaExtractor;
 import android.media.MediaMetadataRetriever;
 import android.net.Uri;
+import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
 import android.os.storage.StorageManager;
-import android.platform.test.annotations.Presubmit;
 import android.provider.MediaStore;
 import android.provider.MediaStore.Video.Media;
 import android.provider.MediaStore.Video.VideoColumns;
-import android.provider.cts.MediaStoreUtils.PendingParams;
-import android.provider.cts.MediaStoreUtils.PendingSession;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
+import android.provider.cts.media.MediaStoreUtils.PendingParams;
+import android.provider.cts.media.MediaStoreUtils.PendingSession;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
 
-import com.android.compatibility.common.util.FileUtils;
-
 import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
@@ -63,7 +63,6 @@
 import java.io.OutputStream;
 import java.util.Arrays;
 
-@Presubmit
 @RunWith(Parameterized.class)
 public class MediaStore_Video_MediaTest {
     private Context mContext;
@@ -97,6 +96,9 @@
         assertNotNull(c = mContentResolver.query(Media.getContentUri(mVolumeName), null, null, null,
                 null));
         c.close();
+
+        assertEquals(ContentUris.withAppendedId(Media.getContentUri(mVolumeName), 42),
+                Media.getContentUri(mVolumeName, 42));
     }
 
     private void cleanExternalMediaFile(String path) {
@@ -106,18 +108,14 @@
 
     @Test
     public void testStoreVideoMediaExternal() throws Exception {
-        final String externalVideoPath = new File(ProviderTestUtils.stageDir(mVolumeName),
-                "testvideo.3gp").getAbsolutePath();
-        final String externalVideoPath2 = new File(ProviderTestUtils.stageDir(mVolumeName),
-                "testvideo1.3gp").getAbsolutePath();
+        final File dir = ProviderTestUtils.stageDir(mVolumeName);
+        final File videoFile = ProviderTestUtils.stageFile(R.raw.testvideo,
+                new File(dir, "cts" + System.nanoTime() + ".mp4"));
 
-        // clean up any potential left over entries from a previous aborted run
-        cleanExternalMediaFile(externalVideoPath);
-        cleanExternalMediaFile(externalVideoPath2);
+        final String externalVideoPath = videoFile.getAbsolutePath();
+        final long numBytes = videoFile.length();
 
-        int numBytes = 1337;
-        File videoFile = new File(externalVideoPath);
-        FileUtils.createFile(videoFile, numBytes);
+        ProviderTestUtils.waitUntilExists(videoFile);
 
         ContentValues values = new ContentValues();
         values.put(Media.ALBUM, "cts");
@@ -166,7 +164,7 @@
             assertEquals("176x144", c.getString(c.getColumnIndex(Media.RESOLUTION)));
             assertEquals("cts, test", c.getString(c.getColumnIndex(Media.TAGS)));
             assertEquals(externalVideoPath, c.getString(c.getColumnIndex(Media.DATA)));
-            assertEquals("testvideo.3gp", c.getString(c.getColumnIndex(Media.DISPLAY_NAME)));
+            assertEquals(videoFile.getName(), c.getString(c.getColumnIndex(Media.DISPLAY_NAME)));
             assertEquals("video/3gpp", c.getString(c.getColumnIndex(Media.MIME_TYPE)));
             assertEquals("testvideo", c.getString(c.getColumnIndex(Media.TITLE)));
             assertEquals(numBytes, c.getInt(c.getColumnIndex(Media.SIZE)));
@@ -221,7 +219,7 @@
         try (PendingSession session = MediaStoreUtils.openPending(mContext, pendingUri)) {
             try (InputStream in = mContext.getResources().openRawResource(R.raw.testvideo_meta);
                  OutputStream out = session.openOutputStream()) {
-                android.os.FileUtils.copy(in, out);
+                FileUtils.copy(in, out);
             }
             publishUri = session.publish();
         }
@@ -237,8 +235,10 @@
                     mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION));
             assertEquals("2", mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS));
         }
-        try (InputStream in = mContentResolver.openInputStream(publishUri)) {
-            byte[] bytes = FileUtils.readInputStreamFully(in);
+        try (InputStream in = mContentResolver.openInputStream(publishUri);
+                ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+            FileUtils.copy(in, out);
+            byte[] bytes = out.toByteArray();
             byte[] xmpBytes = Arrays.copyOfRange(bytes, 3269, 3269 + 13197);
             String xmp = new String(xmpBytes);
             assertTrue("Failed to read XMP longitude", xmp.contains("10,41.751000E"));
@@ -261,8 +261,10 @@
                     mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION));
             assertEquals("2", mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS));
         }
-        try (InputStream in = mContentResolver.openInputStream(publishUri)) {
-            byte[] bytes = FileUtils.readInputStreamFully(in);
+        try (InputStream in = mContentResolver.openInputStream(publishUri);
+                ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+            FileUtils.copy(in, out);
+            byte[] bytes = out.toByteArray();
             byte[] xmpBytes = Arrays.copyOfRange(bytes, 3269, 3269 + 13197);
             String xmp = new String(xmpBytes);
             assertFalse("Failed to redact XMP longitude", xmp.contains("10,41.751000E"));
@@ -287,7 +289,7 @@
         try (PendingSession session = MediaStoreUtils.openPending(mContext, pendingUri)) {
             try (InputStream in = mContext.getResources().openRawResource(R.raw.testvideo_meta);
                     OutputStream out = session.openOutputStream()) {
-                android.os.FileUtils.copy(in, out);
+                FileUtils.copy(in, out);
             }
             publishUri = session.publish();
         }
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_ThumbnailsTest.java
similarity index 98%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_ThumbnailsTest.java
index 9d39c1c..09c45a0 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_ThumbnailsTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.media;
 
 import static android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT;
 import static android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH;
@@ -36,12 +36,13 @@
 import android.media.MediaMetadataRetriever;
 import android.net.Uri;
 import android.os.FileUtils;
-import android.platform.test.annotations.Presubmit;
 import android.provider.MediaStore;
 import android.provider.MediaStore.Files;
 import android.provider.MediaStore.Video.Media;
 import android.provider.MediaStore.Video.Thumbnails;
 import android.provider.MediaStore.Video.VideoColumns;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
 import android.util.Log;
 import android.util.Size;
 
@@ -62,7 +63,6 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 
-@Presubmit
 @RunWith(Parameterized.class)
 public class MediaStore_Video_ThumbnailsTest {
     private static final String TAG = "MediaStore_Video_ThumbnailsTest";
diff --git a/tests/tests/provider/src/android/provider/cts/SettingsTest.java b/tests/tests/provider/src/android/provider/cts/settings/SettingsTest.java
similarity index 99%
rename from tests/tests/provider/src/android/provider/cts/SettingsTest.java
rename to tests/tests/provider/src/android/provider/cts/settings/SettingsTest.java
index 6d808e0..41281b7 100644
--- a/tests/tests/provider/src/android/provider/cts/SettingsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/settings/SettingsTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.settings;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
diff --git a/tests/tests/provider/src/android/provider/cts/Settings_NameValueTableTest.java b/tests/tests/provider/src/android/provider/cts/settings/Settings_NameValueTableTest.java
similarity index 98%
rename from tests/tests/provider/src/android/provider/cts/Settings_NameValueTableTest.java
rename to tests/tests/provider/src/android/provider/cts/settings/Settings_NameValueTableTest.java
index c39b927..d2dbc52 100644
--- a/tests/tests/provider/src/android/provider/cts/Settings_NameValueTableTest.java
+++ b/tests/tests/provider/src/android/provider/cts/settings/Settings_NameValueTableTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.settings;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
diff --git a/tests/tests/provider/src/android/provider/cts/Settings_SecureTest.java b/tests/tests/provider/src/android/provider/cts/settings/Settings_SecureTest.java
similarity index 99%
rename from tests/tests/provider/src/android/provider/cts/Settings_SecureTest.java
rename to tests/tests/provider/src/android/provider/cts/settings/Settings_SecureTest.java
index 6ba1126..fd84677 100644
--- a/tests/tests/provider/src/android/provider/cts/Settings_SecureTest.java
+++ b/tests/tests/provider/src/android/provider/cts/settings/Settings_SecureTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.settings;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
diff --git a/tests/tests/provider/src/android/provider/cts/Settings_SettingNotFoundExceptionTest.java b/tests/tests/provider/src/android/provider/cts/settings/Settings_SettingNotFoundExceptionTest.java
similarity index 96%
rename from tests/tests/provider/src/android/provider/cts/Settings_SettingNotFoundExceptionTest.java
rename to tests/tests/provider/src/android/provider/cts/settings/Settings_SettingNotFoundExceptionTest.java
index fd8a8b6..3fdf062 100644
--- a/tests/tests/provider/src/android/provider/cts/Settings_SettingNotFoundExceptionTest.java
+++ b/tests/tests/provider/src/android/provider/cts/settings/Settings_SettingNotFoundExceptionTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.settings;
 
 import android.provider.Settings.SettingNotFoundException;
 
diff --git a/tests/tests/provider/src/android/provider/cts/Settings_SystemTest.java b/tests/tests/provider/src/android/provider/cts/settings/Settings_SystemTest.java
similarity index 98%
rename from tests/tests/provider/src/android/provider/cts/Settings_SystemTest.java
rename to tests/tests/provider/src/android/provider/cts/settings/Settings_SystemTest.java
index 583f337..338d9f8 100644
--- a/tests/tests/provider/src/android/provider/cts/Settings_SystemTest.java
+++ b/tests/tests/provider/src/android/provider/cts/settings/Settings_SystemTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.provider.cts;
+package android.provider.cts.settings;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
diff --git a/tests/tests/renderscript/OWNERS b/tests/tests/renderscript/OWNERS
new file mode 100644
index 0000000..d61905a
--- /dev/null
+++ b/tests/tests/renderscript/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 43047
+include platform/frameworks/rs:/OWNERS
diff --git a/tests/tests/role/Android.bp b/tests/tests/role/Android.bp
index ac8d967..dfb22f8 100644
--- a/tests/tests/role/Android.bp
+++ b/tests/tests/role/Android.bp
@@ -24,12 +24,17 @@
         "androidx.test.rules",
         "compatibility-device-util-axt",
         "ctstestrunner-axt",
-        "truth-prebuilt"
+        "truth-prebuilt",
     ],
 
     test_suites: [
         "cts",
         "vts",
         "general-tests",
-    ]
+    ],
+
+    data: [
+        ":CtsRoleTestApp",
+        ":CtsRoleTestApp28",
+    ],
 }
diff --git a/tests/tests/role/CtsRoleTestApp/Android.bp b/tests/tests/role/CtsRoleTestApp/Android.bp
index 74c1b76..ac9a6c1 100644
--- a/tests/tests/role/CtsRoleTestApp/Android.bp
+++ b/tests/tests/role/CtsRoleTestApp/Android.bp
@@ -19,10 +19,4 @@
     srcs: [
         "src/**/*.java"
     ],
-
-    test_suites: [
-        "cts",
-        "vts",
-        "general-tests",
-    ]
 }
diff --git a/tests/tests/role/CtsRoleTestApp28/Android.bp b/tests/tests/role/CtsRoleTestApp28/Android.bp
index 0d89f4e..0e82deb 100644
--- a/tests/tests/role/CtsRoleTestApp28/Android.bp
+++ b/tests/tests/role/CtsRoleTestApp28/Android.bp
@@ -19,10 +19,4 @@
     srcs: [
         "src/**/*.java"
     ],
-
-    test_suites: [
-        "cts",
-        "vts",
-        "general-tests",
-    ]
 }
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 0da6988..604261e 100644
--- a/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
+++ b/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
@@ -19,6 +19,9 @@
 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.android.compatibility.common.util.UiAutomatorUtils.waitFindObject;
+import static com.android.compatibility.common.util.UiAutomatorUtils.waitFindObjectOrNull;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.fail;
@@ -37,11 +40,10 @@
 import android.os.UserHandle;
 import android.provider.Telephony;
 import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
 import android.telecom.TelecomManager;
-import android.util.Log;
 import android.util.Pair;
 
 import androidx.annotation.NonNull;
@@ -61,9 +63,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.nio.charset.StandardCharsets;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -115,7 +115,6 @@
     private static final Context sContext = InstrumentationRegistry.getTargetContext();
     private static final PackageManager sPackageManager = sContext.getPackageManager();
     private static final RoleManager sRoleManager = sContext.getSystemService(RoleManager.class);
-    private static final UiDevice sUiDevice = UiDevice.getInstance(sInstrumentation);
 
     @Rule
     public ActivityTestRule<WaitForResultActivity> mActivityRule =
@@ -162,6 +161,11 @@
         runShellCommand(sInstrumentation, "input keyevent KEYCODE_WAKEUP");
     }
 
+    @Before
+    public void closeNotificationShade() {
+        sContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+    }
+
     @Test
     public void requestRoleIntentHasPermissionControllerPackage() throws Exception {
         Intent intent = sRoleManager.createRequestRoleIntent(ROLE_NAME);
@@ -313,13 +317,7 @@
 
     private void respondToRoleRequest(boolean allow) throws InterruptedException, IOException {
         if (allow) {
-            UiObject2 item = sUiDevice.wait(Until.findObject(By.text(APP_PACKAGE_NAME)),
-                    TIMEOUT_MILLIS);
-            if (item == null) {
-                dumpWindowHierarchy();
-                fail("Cannot find item to click");
-            }
-            item.click();
+            waitFindObject(By.text(APP_PACKAGE_NAME)).click();
         }
         Pair<Integer, Intent> result = clickButtonAndWaitForResult(allow);
         int expectedResult = allow ? Activity.RESULT_OK : Activity.RESULT_CANCELED;
@@ -329,8 +327,10 @@
 
     @Nullable
     private UiObject2 findDontAskAgainCheck(boolean expected) {
-        return sUiDevice.wait(Until.findObject(By.text("Don\u2019t ask again")), expected
-                ? TIMEOUT_MILLIS : UNEXPECTED_TIMEOUT_MILLIS);
+        BySelector selector = By.text("Don\u2019t ask again");
+        return expected
+                ? waitFindObject(selector, TIMEOUT_MILLIS)
+                : waitFindObjectOrNull(selector, UNEXPECTED_TIMEOUT_MILLIS);
     }
 
     @Nullable
@@ -341,28 +341,10 @@
     @NonNull
     private Pair<Integer, Intent> clickButtonAndWaitForResult(boolean positive) throws IOException,
             InterruptedException {
-        String buttonId = positive ? "android:id/button1" : "android:id/button2";
-        UiObject2 button = sUiDevice.wait(Until.findObject(By.res(buttonId)), TIMEOUT_MILLIS);
-        if (button == null) {
-            dumpWindowHierarchy();
-            fail("Cannot find button to click");
-        }
-        button.click();
+        waitFindObject(By.res(positive ? "android:id/button1" : "android:id/button2")).click();
         return waitForResult();
     }
 
-    private void dumpWindowHierarchy() throws InterruptedException, IOException {
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        sUiDevice.dumpWindowHierarchy(outputStream);
-        String windowHierarchy = outputStream.toString(StandardCharsets.UTF_8.name());
-
-        Log.w(LOG_TAG, "Window hierarchy:");
-        for (String line : windowHierarchy.split("\n")) {
-            Thread.sleep(10);
-            Log.w(LOG_TAG, line);
-        }
-    }
-
     @NonNull
     private Pair<Integer, Intent> waitForResult() throws InterruptedException {
         return mActivityRule.getActivity().waitForActivityResult(TIMEOUT_MILLIS);
@@ -405,7 +387,6 @@
         assertThat(result.first).isEqualTo(Activity.RESULT_CANCELED);
     }
 
-    @FlakyTest
     @Test
     public void targetSdk28AndChangeDefaultDialerAndAllowThenIsDefaultDialer() throws Exception {
         sContext.startActivity(new Intent()
@@ -413,13 +394,13 @@
                         APP_28_CHANGE_DEFAULT_DIALER_ACTIVITY_NAME))
                 .putExtra(Intent.EXTRA_PACKAGE_NAME, APP_28_PACKAGE_NAME)
                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
-        allowRoleRequestForApp28();
+        waitFindObject(By.text(APP_28_LABEL)).click();
+        waitFindObject(By.res("android:id/button1")).click();
         TelecomManager telecomManager = sContext.getSystemService(TelecomManager.class);
         TestUtils.waitUntil("App is not set as default dialer app", () -> Objects.equals(
                 telecomManager.getDefaultDialerPackage(), APP_28_PACKAGE_NAME));
     }
 
-    @FlakyTest
     @Test
     public void targetSdk28AndChangeDefaultSmsAndAllowThenIsDefaultSms() throws Exception {
         sContext.startActivity(new Intent()
@@ -427,27 +408,12 @@
                         APP_28_CHANGE_DEFAULT_SMS_ACTIVITY_NAME))
                 .putExtra(Intent.EXTRA_PACKAGE_NAME, APP_28_PACKAGE_NAME)
                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
-        allowRoleRequestForApp28();
+        waitFindObject(By.text(APP_28_LABEL)).click();
+        waitFindObject(By.res("android:id/button1")).click();
         TestUtils.waitUntil("App is not set as default sms app", () -> Objects.equals(
                 Telephony.Sms.getDefaultSmsPackage(sContext), APP_28_PACKAGE_NAME));
     }
 
-    private void allowRoleRequestForApp28() throws InterruptedException, IOException {
-        UiObject2 item = sUiDevice.wait(Until.findObject(By.text(APP_28_LABEL)), TIMEOUT_MILLIS);
-        if (item == null) {
-            dumpWindowHierarchy();
-            fail("Cannot find item to click");
-        }
-        item.click();
-        UiObject2 button = sUiDevice.wait(Until.findObject(By.res("android:id/button1")),
-                TIMEOUT_MILLIS);
-        if (button == null) {
-            dumpWindowHierarchy();
-            fail("Cannot find button to click");
-        }
-        button.click();
-    }
-
     @Test
     public void roleIsAvailable() {
         assertThat(sRoleManager.isRoleAvailable(ROLE_NAME)).isTrue();
diff --git a/tests/tests/secure_element/access_control/AccessControlApp1/Android.mk b/tests/tests/secure_element/access_control/AccessControlApp1/Android.mk
index 8a78d0c..ef992e6 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 := 29
+LOCAL_SDK_VERSION := current
 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/src/android/omapi/accesscontrol1/cts/AccessControlTest.java b/tests/tests/secure_element/access_control/AccessControlApp1/src/android/omapi/accesscontrol1/cts/AccessControlTest.java
index 4eb7cb9..ad85ac2 100755
--- a/tests/tests/secure_element/access_control/AccessControlApp1/src/android/omapi/accesscontrol1/cts/AccessControlTest.java
+++ b/tests/tests/secure_element/access_control/AccessControlApp1/src/android/omapi/accesscontrol1/cts/AccessControlTest.java
@@ -162,10 +162,18 @@
         return !lowRamDevice || (lowRamDevice && pm.hasSystemFeature("android.hardware.type.watch"));
     }
 
+    private boolean supportOMAPIReaders() {
+        final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+        return (pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_UICC)
+            || pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_ESE)
+            || pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_SD));
+    }
+
     @Before
     public void setUp() throws Exception {
         assumeTrue(PropertyUtil.getFirstApiLevel() > Build.VERSION_CODES.O_MR1);
         assumeTrue(supportsHardware());
+        assumeTrue(supportOMAPIReaders());
         seService = new SEService(InstrumentationRegistry.getContext(), new SynchronousExecutor(), mListener);
         connectionTimer = new Timer();
         connectionTimer.schedule(mTimerTask, SERVICE_CONNECTION_TIME_OUT);
diff --git a/tests/tests/secure_element/access_control/AccessControlApp2/Android.mk b/tests/tests/secure_element/access_control/AccessControlApp2/Android.mk
index 494125f..38f12ee 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 := 29
+LOCAL_SDK_VERSION := current
 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/src/android/omapi/accesscontrol2/cts/AccessControlTest.java b/tests/tests/secure_element/access_control/AccessControlApp2/src/android/omapi/accesscontrol2/cts/AccessControlTest.java
index e3eb557..8bce22b 100755
--- a/tests/tests/secure_element/access_control/AccessControlApp2/src/android/omapi/accesscontrol2/cts/AccessControlTest.java
+++ b/tests/tests/secure_element/access_control/AccessControlApp2/src/android/omapi/accesscontrol2/cts/AccessControlTest.java
@@ -161,10 +161,18 @@
         return !lowRamDevice || (lowRamDevice && pm.hasSystemFeature("android.hardware.type.watch"));
     }
 
+    private boolean supportOMAPIReaders() {
+        final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+        return (pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_UICC)
+            || pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_ESE)
+            || pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_SD));
+    }
+
     @Before
     public void setUp() throws Exception {
         assumeTrue(PropertyUtil.getFirstApiLevel() > Build.VERSION_CODES.O_MR1);
         assumeTrue(supportsHardware());
+        assumeTrue(supportOMAPIReaders());
         seService = new SEService(InstrumentationRegistry.getContext(), new SynchronousExecutor(), mListener);
         connectionTimer = new Timer();
         connectionTimer.schedule(mTimerTask, SERVICE_CONNECTION_TIME_OUT);
diff --git a/tests/tests/secure_element/access_control/AccessControlApp3/Android.mk b/tests/tests/secure_element/access_control/AccessControlApp3/Android.mk
index 11f0200..335c08a 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 := 29
+LOCAL_SDK_VERSION := current
 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/src/android/omapi/accesscontrol3/cts/AccessControlTest.java b/tests/tests/secure_element/access_control/AccessControlApp3/src/android/omapi/accesscontrol3/cts/AccessControlTest.java
index 7260c4f..e6dc5f0 100755
--- a/tests/tests/secure_element/access_control/AccessControlApp3/src/android/omapi/accesscontrol3/cts/AccessControlTest.java
+++ b/tests/tests/secure_element/access_control/AccessControlApp3/src/android/omapi/accesscontrol3/cts/AccessControlTest.java
@@ -171,10 +171,18 @@
         return !lowRamDevice || (lowRamDevice && pm.hasSystemFeature("android.hardware.type.watch"));
     }
 
+    private boolean supportOMAPIReaders() {
+        final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+        return (pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_UICC)
+            || pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_ESE)
+            || pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_SD));
+    }
+
     @Before
     public void setUp() throws Exception {
         assumeTrue(PropertyUtil.getFirstApiLevel() > Build.VERSION_CODES.O_MR1);
         assumeTrue(supportsHardware());
+        assumeTrue(supportOMAPIReaders());
         seService = new SEService(InstrumentationRegistry.getContext(), new SynchronousExecutor(), mListener);
         connectionTimer = new Timer();
         connectionTimer.schedule(mTimerTask, SERVICE_CONNECTION_TIME_OUT);
diff --git a/tests/tests/secure_element/omapi/OWNERS b/tests/tests/secure_element/omapi/OWNERS
index fb599b2..853b7c3 100644
--- a/tests/tests/secure_element/omapi/OWNERS
+++ b/tests/tests/secure_element/omapi/OWNERS
@@ -1,3 +1,6 @@
 # Bug component: 456592
-rmojumder@google.com
 zachoverflow@google.com
+jackcwyu@google.com
+tokuda@google.com
+georgekgchang@google.com
+jimmychchang@google.com
diff --git a/tests/tests/secure_element/omapi/src/android/omapi/cts/OmapiTest.java b/tests/tests/secure_element/omapi/src/android/omapi/cts/OmapiTest.java
index d8c7b65..0734a9b 100644
--- a/tests/tests/secure_element/omapi/src/android/omapi/cts/OmapiTest.java
+++ b/tests/tests/secure_element/omapi/src/android/omapi/cts/OmapiTest.java
@@ -158,6 +158,29 @@
         return !lowRamDevice || (lowRamDevice && pm.hasSystemFeature("android.hardware.type.watch"));
     }
 
+    private boolean supportUICCReaders() {
+        final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+        return pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_UICC);
+    }
+
+    private boolean supportESEReaders() {
+        final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+        return pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_ESE);
+    }
+
+    private boolean supportSDReaders() {
+        final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+        return pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_SD);
+    }
+
+    private boolean supportOMAPIReaders() {
+        final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+        return (pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_UICC)
+            || pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_ESE)
+            || pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_SD));
+    }
+
+
     private void assertGreaterOrEqual(long greater, long lesser) {
         assertTrue("" + greater + " expected to be greater than or equal to " + lesser,
                 greater >= lesser);
@@ -167,6 +190,7 @@
     public void setUp() throws Exception {
         assumeTrue(PropertyUtil.getFirstApiLevel() > Build.VERSION_CODES.O_MR1);
         assumeTrue(supportsHardware());
+        assumeTrue(supportOMAPIReaders());
         seService = new SEService(InstrumentationRegistry.getContext(), new SynchronousExecutor(), mListener);
         connectionTimer = new Timer();
         connectionTimer.schedule(mTimerTask, SERVICE_CONNECTION_TIME_OUT);
@@ -206,6 +230,9 @@
         try {
             waitForConnection();
             Reader[] readers = seService.getReaders();
+            ArrayList<Reader> uiccReaders = new ArrayList<Reader>();
+            ArrayList<Reader> eseReaders = new ArrayList<Reader>();
+            ArrayList<Reader> sdReaders = new ArrayList<Reader>();
 
             for (Reader reader : readers) {
                 assertTrue(reader.isSecureElementPresent());
@@ -215,6 +242,34 @@
                     fail("Incorrect Reader name");
                 }
                 assertNotNull("getseService returned null", reader.getSEService());
+
+                if (reader.getName().startsWith(UICC_READER_PREFIX)) {
+                    uiccReaders.add(reader);
+                }
+                if (reader.getName().startsWith(ESE_READER_PREFIX)) {
+                    eseReaders.add(reader);
+                }
+                if (reader.getName().startsWith(SD_READER_PREFIX)) {
+                    sdReaders.add(reader);
+                }
+            }
+
+            if (supportUICCReaders()) {
+                assertGreaterOrEqual(uiccReaders.size(), 1);
+            } else {
+                assertTrue(uiccReaders.size() == 0);
+            }
+
+            if (supportESEReaders()) {
+                assertGreaterOrEqual(eseReaders.size(), 1);
+            } else {
+                assertTrue(eseReaders.size() == 0);
+            }
+
+            if (supportSDReaders()) {
+                assertGreaterOrEqual(eseReaders.size(), 1);
+            } else {
+                assertTrue(sdReaders.size() == 0);
             }
         } catch (Exception e) {
             fail("Unexpected Exception " + e);
diff --git a/tests/tests/security/Android.bp b/tests/tests/security/Android.bp
index b41f33d..eb35abb 100644
--- a/tests/tests/security/Android.bp
+++ b/tests/tests/security/Android.bp
@@ -22,6 +22,7 @@
         "android-common",
         "ctstestserver",
         "ctstestrunner-axt",
+        "cts-install-lib",
         "compatibility-device-util-axt",
         "compatibility-common-util-devicesidelib",
         "guava",
@@ -32,6 +33,9 @@
         "org.apache.http.legacy",
         "android.test.base.stubs",
     ],
+    java_resources: [
+        ":PackageInstallerTestApp",
+    ],
     jni_libs: [
         "libctssecurity_jni",
         "libcts_jni",
@@ -58,4 +62,16 @@
         "general-tests",
         "sts",
     ],
+    certificate: ":security_cts_test_certificate",
 }
+
+android_test_helper_app {
+    name: "PackageInstallerTestApp",
+    srcs: ["testdata/src/**/*.java"],
+    manifest: "testdata/packageinstallertestapp.xml",
+}
+
+android_app_certificate {
+    name: "security_cts_test_certificate",
+    certificate: "security_cts_test_cert",
+}
\ No newline at end of file
diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml
index 54df055..0226ab7 100644
--- a/tests/tests/security/AndroidManifest.xml
+++ b/tests/tests/security/AndroidManifest.xml
@@ -58,6 +58,16 @@
                 <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
             </intent-filter>
         </activity>
+
+        <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
+                  android:exported="true" />
+        <receiver android:name="android.security.cts.PackageVerificationsBroadcastReceiver"
+                  android:permission="android.permission.BIND_PACKAGE_VERIFIER" >
+            <intent-filter>
+                <action android:name="android.intent.action.PACKAGE_NEEDS_VERIFICATION"/>
+                <data android:mimeType="application/vnd.android.package-archive" />
+            </intent-filter>
+        </receiver>
     </application>
 
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/security/OWNERS b/tests/tests/security/OWNERS
new file mode 100644
index 0000000..c5cfd1e
--- /dev/null
+++ b/tests/tests/security/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 36824
+cbrubaker@google.com
+jeffv@google.com
+nnk@google.com
diff --git a/tests/tests/security/security_cts_test_cert.pk8 b/tests/tests/security/security_cts_test_cert.pk8
new file mode 100644
index 0000000..320e18d
--- /dev/null
+++ b/tests/tests/security/security_cts_test_cert.pk8
Binary files differ
diff --git a/tests/tests/security/security_cts_test_cert.x509.pem b/tests/tests/security/security_cts_test_cert.x509.pem
new file mode 100644
index 0000000..fd10e48
--- /dev/null
+++ b/tests/tests/security/security_cts_test_cert.x509.pem
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIIECzCCAvOgAwIBAgIUBCvKStYix70zHFiAKnzigdwl2z4wDQYJKoZIhvcNAQEL
+BQAwgZQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
+DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
+b2lkMRAwDgYDVQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFu
+ZHJvaWQuY29tMB4XDTE5MDgxOTIxMzcwM1oXDTQ3MDEwNDIxMzcwM1owgZQxCzAJ
+BgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFp
+biBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRyb2lkMRAwDgYD
+VQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29t
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3rB8dYLa9mhYe9GICodU
+FVdjzh00SsfzpdMZ4UGIGF6VY/7D/TCdT5vjdXOdOQtsQnM/nZSgUPgBVX8RObm4
+/PRix68rdl2J58/LstcqdG6EaExb5hPUzHUuvOfd+p+IP+0SFEuRrWeGsmkzvdnx
+C2ZZjzEpE8UNDS8EtC2qULkF0cAGcHdHsjlktXRvn4FO+RN1GW6yxs8mOyCabNHA
+Se3AynYFa894Iamu99+RK51+3iyw+u4cVUeVPH3CzJ2Pu1PyqT+9l4gKUbw0gfC6
+D0/PNEfxe4RPrtn3Z8+ES8+jXPjBLLaMTpT9dFcP25kBwNLiV0MJdTOdZ3f30urt
+JQIDAQABo1MwUTAdBgNVHQ4EFgQUHUCJ6l5sn0M96xgIXBkY7dvm86MwHwYDVR0j
+BBgwFoAUHUCJ6l5sn0M96xgIXBkY7dvm86MwDwYDVR0TAQH/BAUwAwEB/zANBgkq
+hkiG9w0BAQsFAAOCAQEAAXpJFK3Lp6HuEeSkV60YUH7KKFf9FCfIlszxAcnPb7Pu
+8LX8pI59jYXUxp6ig2IRP/3jXWyf5mFyXcfPTES9Xi1yruV/hQ3KvvhrC2FSqF99
+AXPB31NiXxyw554iPGGLqRsxLb1aeRgofiGLG6CE+16RIPX54pDS6Y+MDJ7iaRsG
+L5qPP3JyQ5b3KBHFXE9GHJFEha2mrLThv1V6740ueErt2jkP95BnELmFwo+RH4ha
+sUOe79aEbq4ERKrmKf5vZ4GGS3vHQ6MSk53qeDFrla/05pZRfzUvwu88cLs0EjSI
+o36G2JpHHjd58pH7m4xeqcBX5eUKay/EfoYef4AopA==
+-----END CERTIFICATE-----
diff --git a/tests/tests/security/src/android/security/cts/PackageInstallerTest.java b/tests/tests/security/src/android/security/cts/PackageInstallerTest.java
new file mode 100644
index 0000000..719e062
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/PackageInstallerTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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 android.Manifest;
+import android.platform.test.annotations.SecurityTest;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.cts.install.lib.Install;
+import com.android.cts.install.lib.TestApp;
+import com.android.cts.install.lib.Uninstall;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.concurrent.TimeUnit;
+
+@RunWith(JUnit4.class)
+@SecurityTest
+public class PackageInstallerTest {
+
+    private static final String TEST_APP_NAME = "android.security.cts.packageinstallertestapp";
+
+    private static final TestApp TEST_APP = new TestApp(
+            "PackageInstallerTestApp", TEST_APP_NAME, 1, /*isApex*/ false,
+            "PackageInstallerTestApp.apk");
+
+    @Before
+    public void setUp() {
+        InstrumentationRegistry
+                .getInstrumentation()
+                .getUiAutomation()
+                .adoptShellPermissionIdentity(Manifest.permission.INSTALL_PACKAGES,
+                        Manifest.permission.DELETE_PACKAGES,
+                        Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+                        Manifest.permission.BIND_PACKAGE_VERIFIER);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        Uninstall.packages(TestApp.A);
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                .dropShellPermissionIdentity();
+    }
+
+    @Test
+    public void verificationCanNotBeDisabledByInstaller() throws Exception {
+        Install.single(TEST_APP).addInstallFlags(
+                0x00080000 /* PackageManager.INSTALL_DISABLE_VERIFICATION */).commit();
+        String packageName = PackageVerificationsBroadcastReceiver.packages.poll(30,
+                TimeUnit.SECONDS);
+        Assert.assertNotNull("Did not receive broadcast", packageName);
+        Assert.assertEquals(TEST_APP_NAME, packageName);
+    }
+}
diff --git a/tests/tests/security/src/android/security/cts/PackageVerificationsBroadcastReceiver.java b/tests/tests/security/src/android/security/cts/PackageVerificationsBroadcastReceiver.java
new file mode 100644
index 0000000..62d409d
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/PackageVerificationsBroadcastReceiver.java
@@ -0,0 +1,45 @@
+/*
+ * 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.content.pm.PackageManager.EXTRA_VERIFICATION_PACKAGE_NAME;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+public final class PackageVerificationsBroadcastReceiver extends BroadcastReceiver {
+
+    private static final String TAG = "PackageInstallerTest";
+
+    static final BlockingQueue<String> packages = new LinkedBlockingQueue<>();
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final String packageName = intent.getStringExtra(EXTRA_VERIFICATION_PACKAGE_NAME);
+        Log.i(TAG, "Received PACKAGE_NEEDS_VERIFICATION broadcast for package " + packageName);
+        try {
+            packages.put(packageName);
+        } catch (InterruptedException e) {
+            throw new AssertionError(e);
+        }
+    }
+}
diff --git a/tests/tests/security/testdata/packageinstallertestapp.xml b/tests/tests/security/testdata/packageinstallertestapp.xml
new file mode 100644
index 0000000..7c35c11
--- /dev/null
+++ b/tests/tests/security/testdata/packageinstallertestapp.xml
@@ -0,0 +1,37 @@
+<?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.security.cts.packageinstallertestapp"
+          android:versionCode="1"
+          android:versionName="1.0" >
+
+
+    <package-verifier android:name="android.security.cts"
+                      android:publicKey="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3rB8dYLa9mhYe9GICodUFVdjzh00SsfzpdMZ4UGIGF6VY/7D/TCdT5vjdXOdOQtsQnM/nZSgUPgBVX8RObm4/PRix68rdl2J58/LstcqdG6EaExb5hPUzHUuvOfd+p+IP+0SFEuRrWeGsmkzvdnxC2ZZjzEpE8UNDS8EtC2qULkF0cAGcHdHsjlktXRvn4FO+RN1GW6yxs8mOyCabNHASe3AynYFa894Iamu99+RK51+3iyw+u4cVUeVPH3CzJ2Pu1PyqT+9l4gKUbw0gfC6D0/PNEfxe4RPrtn3Z8+ES8+jXPjBLLaMTpT9dFcP25kBwNLiV0MJdTOdZ3f30urtJQIDAQAB" />
+
+    <uses-sdk android:minSdkVersion="19" />
+
+    <application android:label="PackageInstallerTest Test App">
+        <activity android:name="android.security.cts.packageinstallertestapp.MainActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java b/tests/tests/security/testdata/src/android/security/cts/packageinstallertestapp/MainActivity.java
similarity index 93%
rename from tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java
rename to tests/tests/security/testdata/src/android/security/cts/packageinstallertestapp/MainActivity.java
index 37276e2..aeb58c5 100644
--- a/tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java
+++ b/tests/tests/security/testdata/src/android/security/cts/packageinstallertestapp/MainActivity.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.tests.atomicinstall.testapp;
+package android.security.cts.packageinstallertestapp;
 
 import android.app.Activity;
 import android.os.Bundle;
@@ -26,3 +26,4 @@
         super.onCreate(savedInstanceState);
     }
 }
+
diff --git a/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java b/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java
index 587c3e8..e4dc206 100644
--- a/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java
+++ b/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java
@@ -82,15 +82,15 @@
      * Check expectations of being able to read/execute dex2oat.
      */
     protected static void checkDex2oatAccess(boolean expectedAllowed) throws Exception {
-        // First check whether there is an Android Runtime APEX dex2oat binary.
-        File dex2oatRuntimeApexBinary = new File("/apex/com.android.runtime/bin/dex2oat");
-        if (dex2oatRuntimeApexBinary.exists()) {
-          checkDex2oatBinaryAccess(dex2oatRuntimeApexBinary, expectedAllowed);
-        }
-        // Also check whether there is a "legacy" system binary.
-        File dex2oatSystemBinary = new File("/system/bin/dex2oat");
-        if (dex2oatSystemBinary.exists()) {
-          checkDex2oatBinaryAccess(dex2oatSystemBinary, expectedAllowed);
+        // Check the dex2oat binary in its current and legacy locations.
+        String[] locations = {"/apex/com.android.art/bin",
+                              "/apex/com.android.runtime/bin",
+                              "/system/bin"};
+        for (String loc : locations) {
+            File dex2oatBinary = new File(loc + "/dex2oat");
+            if (dex2oatBinary.exists()) {
+                checkDex2oatBinaryAccess(dex2oatBinary, expectedAllowed);
+            }
         }
     }
 
diff --git a/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechServiceTest.java b/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechServiceTest.java
index 8bca08c..addd14e 100644
--- a/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechServiceTest.java
+++ b/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechServiceTest.java
@@ -15,8 +15,10 @@
  */
 package android.speech.tts.cts;
 
+import android.os.Bundle;
 import android.os.ConditionVariable;
 import android.os.Environment;
+import android.os.ParcelFileDescriptor;
 import android.speech.tts.TextToSpeech;
 import android.test.AndroidTestCase;
 
@@ -61,28 +63,54 @@
             int result =
                     getTts().synthesizeToFile(
                                     UTTERANCE, createParams("mocktofile"), sampleFile.getPath());
-            assertEquals("synthesizeToFile() failed", TextToSpeech.SUCCESS, result);
-
-            assertTrue("synthesizeToFile() completion timeout", mTts.waitForComplete("mocktofile"));
-            assertTrue("synthesizeToFile() didn't produce a file", sampleFile.exists());
-            assertTrue("synthesizeToFile() produced a non-sound file",
-                    TextToSpeechWrapper.isSoundFile(sampleFile.getPath()));
+            verifySynthesisFile(result, mTts, sampleFile);
         } finally {
             sampleFile.delete();
         }
-        mTts.verify("mocktofile");
+        verifySynthesisResults(mTts);
+    }
 
-        final Map<String, Integer> chunksReceived = mTts.chunksReceived();
-        final Map<String, List<Integer>> timePointsStart = mTts.timePointsStart();
-        final Map<String, List<Integer>> timePointsEnd = mTts.timePointsEnd();
-        final Map<String, List<Integer>> timePointsFrame = mTts.timePointsFrame();
-        assertEquals(Integer.valueOf(10), chunksReceived.get("mocktofile"));
-        // In the mock we set the start, end and frame to exactly these values for the time points.
-        for (int i = 0; i < 10; i++) {
-            assertEquals(Integer.valueOf(i * 5), timePointsStart.get("mocktofile").get(i));
-            assertEquals(Integer.valueOf(i * 5 + 5), timePointsEnd.get("mocktofile").get(i));
-            assertEquals(Integer.valueOf(i * 10), timePointsFrame.get("mocktofile").get(i));
+    public void testSynthesizeToFileWithFileObject() throws Exception {
+        File sampleFile = new File(getContext().getExternalFilesDir(null), SAMPLE_FILE_NAME);
+        try {
+            assertFalse(sampleFile.exists());
+
+            Bundle params = createParamsBundle("mocktofile");
+
+            int result =
+                    getTts().synthesizeToFile(
+                                    UTTERANCE,
+                                    params,
+                                    sampleFile,
+                                    params.getString(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID));
+            verifySynthesisFile(result, mTts, sampleFile);
+        } finally {
+            sampleFile.delete();
         }
+        verifySynthesisResults(mTts);
+    }
+
+    public void testSynthesizeToFileWithFileDescriptor() throws Exception {
+        File sampleFile = new File(getContext().getExternalFilesDir(null), SAMPLE_FILE_NAME);
+        try {
+            assertFalse(sampleFile.exists());
+
+            ParcelFileDescriptor fileDescriptor = ParcelFileDescriptor.open(sampleFile,
+                ParcelFileDescriptor.MODE_WRITE_ONLY
+                | ParcelFileDescriptor.MODE_CREATE
+                | ParcelFileDescriptor.MODE_TRUNCATE);
+
+            Bundle params = createParamsBundle("mocktofile");
+
+            int result =
+                getTts().synthesizeToFile(
+                    UTTERANCE, params, fileDescriptor,
+                    params.getString(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID));
+            verifySynthesisFile(result, mTts, sampleFile);
+        } finally {
+            sampleFile.delete();
+        }
+        verifySynthesisResults(mTts);
     }
 
     public void testMaxSpeechInputLength() {
@@ -184,6 +212,39 @@
         return params;
     }
 
+    private Bundle createParamsBundle(String utteranceId) {
+        Bundle bundle = new Bundle();
+        bundle.putString(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, utteranceId);
+        return bundle;
+    }
+
+    private void verifySynthesisFile(int result, TextToSpeechWrapper mTts, File file)
+        throws InterruptedException {
+
+        assertEquals("synthesizeToFile() failed", TextToSpeech.SUCCESS, result);
+
+        assertTrue("synthesizeToFile() completion timeout", mTts.waitForComplete("mocktofile"));
+        assertTrue("synthesizeToFile() didn't produce a file", file.exists());
+        assertTrue("synthesizeToFile() produced a non-sound file",
+                TextToSpeechWrapper.isSoundFile(file.getPath()));
+    }
+
+    private void verifySynthesisResults(TextToSpeechWrapper mTts) {
+        mTts.verify("mocktofile");
+
+        final Map<String, Integer> chunksReceived = mTts.chunksReceived();
+        final Map<String, List<Integer>> timePointsStart = mTts.timePointsStart();
+        final Map<String, List<Integer>> timePointsEnd = mTts.timePointsEnd();
+        final Map<String, List<Integer>> timePointsFrame = mTts.timePointsFrame();
+        assertEquals(Integer.valueOf(10), chunksReceived.get("mocktofile"));
+        // In the mock we set the start, end and frame to exactly these values for the time points.
+        for (int i = 0; i < 10; i++) {
+            assertEquals(Integer.valueOf(i * 5), timePointsStart.get("mocktofile").get(i));
+            assertEquals(Integer.valueOf(i * 5 + 5), timePointsEnd.get("mocktofile").get(i));
+            assertEquals(Integer.valueOf(i * 10), timePointsFrame.get("mocktofile").get(i));
+        }
+    }
+
     private boolean waitForUtterance(String utteranceId) throws InterruptedException {
         return mTts.waitForComplete(utteranceId);
     }
diff --git a/tests/tests/syncmanager/OWNERS b/tests/tests/syncmanager/OWNERS
new file mode 100644
index 0000000..c475c6e
--- /dev/null
+++ b/tests/tests/syncmanager/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 197138
+omakoto@google.com
+yamasani@google.com
diff --git a/tests/tests/systemintents/OWNERS b/tests/tests/systemintents/OWNERS
new file mode 100644
index 0000000..b465964
--- /dev/null
+++ b/tests/tests/systemintents/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 25692
+ctate@google.com
\ No newline at end of file
diff --git a/tests/tests/telecom/AndroidTest.xml b/tests/tests/telecom/AndroidTest.xml
index c90c724..4dce7f5 100644
--- a/tests/tests/telecom/AndroidTest.xml
+++ b/tests/tests/telecom/AndroidTest.xml
@@ -18,6 +18,7 @@
     <option name="config-descriptor:metadata" key="component" value="telecom" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.TokenRequirement">
         <option name="token" value="sim-card" />
     </target_preparer>
diff --git a/tests/tests/telecom/OWNERS b/tests/tests/telecom/OWNERS
new file mode 100644
index 0000000..f4921e0
--- /dev/null
+++ b/tests/tests/telecom/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 20868
+include ../telephony/OWNERS
diff --git a/tests/tests/telecom2/OWNERS b/tests/tests/telecom2/OWNERS
new file mode 100644
index 0000000..f4921e0
--- /dev/null
+++ b/tests/tests/telecom2/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 20868
+include ../telephony/OWNERS
diff --git a/tests/tests/telecom3/OWNERS b/tests/tests/telecom3/OWNERS
new file mode 100644
index 0000000..f4921e0
--- /dev/null
+++ b/tests/tests/telecom3/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 20868
+include ../telephony/OWNERS
diff --git a/tests/tests/telephony/OWNERS b/tests/tests/telephony/OWNERS
new file mode 100644
index 0000000..73fa25e
--- /dev/null
+++ b/tests/tests/telephony/OWNERS
@@ -0,0 +1,14 @@
+# Bug component: 20868
+amitmahajan@google.com
+fionaxu@google.com
+jackyu@google.com
+rgreenwalt@google.com
+refuhoo@google.com
+mpq@google.com
+jminjie@google.com
+shuoq@google.com
+hallliu@google.com
+tgunn@google.com
+breadley@google.com
+paulye@google.com
+nazaninb@google.com
diff --git a/tests/tests/telephony/current/OWNERS b/tests/tests/telephony/current/OWNERS
deleted file mode 100644
index 7c8e7d8..0000000
--- a/tests/tests/telephony/current/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-# Bug component: 20868
-rgreenwalt@google.com
-jackyu@google.com
-tgunn@google.com
-amitmahajan@google.com
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java
index 26358f0..c81240d 100755
--- a/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java
@@ -443,6 +443,9 @@
 
     @Test
     public void testContentProviderAccessRestriction() throws Exception {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            return;
+        }
         Uri dummySmsUri = null;
         Context context = getInstrumentation().getContext();
         ContentResolver contentResolver = context.getContentResolver();
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 6803431..340841e 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
@@ -134,21 +134,25 @@
     }
 
     /**
-     * Sanity check that the device has a cellular network and a valid default data subId
-     * when {@link PackageManager#FEATURE_TELEPHONY} support.
+     * Sanity check that both {@link PackageManager#FEATURE_TELEPHONY} and
+     * {@link NetworkCapabilities#TRANSPORT_CELLULAR} network must both be
+     * either defined or undefined; you can't cross the streams.
      */
     @Test
     public void testSanity() throws Exception {
-        if (!isSupported()) return;
-
         final boolean hasCellular = findCellularNetwork() != null;
-        if (!hasCellular) {
+        if (isSupported() && !hasCellular) {
             fail("Device claims to support " + PackageManager.FEATURE_TELEPHONY
                     + " but has no active cellular network, which is required for validation");
+        } else if (!isSupported() && hasCellular) {
+            fail("Device has active cellular network, but claims to not support "
+                    + PackageManager.FEATURE_TELEPHONY);
         }
 
-        if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            fail("Device must have a valid default data subId for validation");
+        if (isSupported()) {
+            if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+                fail("Device must have a valid default data subId for validation");
+            }
         }
     }
 
@@ -388,12 +392,12 @@
         }
 
         // Add into subscription group that doesn't exist. This should fail
-        // with IllegalArgumentException.
+        // because we don't have MODIFY_PHONE_STATE or carrier privilege permission.
         try {
             ParcelUuid groupUuid = new ParcelUuid(UUID.randomUUID());
             mSm.addSubscriptionsIntoGroup(subGroup, groupUuid);
             fail();
-        } catch (IllegalArgumentException expected) {
+        } catch (SecurityException expected) {
         }
 
         // Remove from subscription group with current sub Id. This should fail
@@ -449,6 +453,32 @@
     }
 
     @Test
+    public void testAddSubscriptionIntoNewGroupWithPermission() throws Exception {
+        if (!isSupported()) return;
+
+        // Set subscription group with current sub Id.
+        List<Integer> subGroup = new ArrayList();
+        subGroup.add(mSubId);
+        ParcelUuid uuid = new ParcelUuid(UUID.randomUUID());
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSm,
+                (sm) -> sm.addSubscriptionsIntoGroup(subGroup, uuid));
+
+        // Getting subscriptions in group.
+        List<SubscriptionInfo> infoList = mSm.getSubscriptionsInGroup(uuid);
+        assertNotNull(infoList);
+        assertEquals(1, infoList.size());
+        assertEquals(uuid, infoList.get(0).getGroupUuid());
+
+        // Remove from subscription group with current sub Id.
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSm,
+                (sm) -> sm.removeSubscriptionsFromGroup(subGroup, uuid));
+
+        infoList = mSm.getSubscriptionsInGroup(uuid);
+        assertNotNull(infoList);
+        assertTrue(infoList.isEmpty());
+    }
+
+    @Test
     public void testSettingOpportunisticSubscription() throws Exception {
         if (!isSupported()) return;
 
diff --git a/tests/tests/telephony2/OWNERS b/tests/tests/telephony2/OWNERS
index 7c8e7d8..f4921e0 100644
--- a/tests/tests/telephony2/OWNERS
+++ b/tests/tests/telephony2/OWNERS
@@ -1,5 +1,2 @@
 # Bug component: 20868
-rgreenwalt@google.com
-jackyu@google.com
-tgunn@google.com
-amitmahajan@google.com
+include ../telephony/OWNERS
diff --git a/tests/tests/telephony3/OWNERS b/tests/tests/telephony3/OWNERS
new file mode 100644
index 0000000..f4921e0
--- /dev/null
+++ b/tests/tests/telephony3/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 20868
+include ../telephony/OWNERS
diff --git a/tests/tests/telephony4/OWNERS b/tests/tests/telephony4/OWNERS
index 5617896..3a905dd 100644
--- a/tests/tests/telephony4/OWNERS
+++ b/tests/tests/telephony4/OWNERS
@@ -1,2 +1,4 @@
 # Bug component: 20868
-yinxu@google.com
\ No newline at end of file
+include ../telephony/OWNERS
+
+yinxu@google.com
diff --git a/tests/tests/telephonyprovider/OWNERS b/tests/tests/telephonyprovider/OWNERS
index 92458db..7f7694d 100644
--- a/tests/tests/telephonyprovider/OWNERS
+++ b/tests/tests/telephonyprovider/OWNERS
@@ -1,12 +1,3 @@
-amitmahajan@google.com
-fionaxu@google.com
-jackyu@google.com
-rgreenwalt@google.com
-refuhoo@google.com
-mpq@google.com
-jminjie@google.com
-shuoq@google.com
-hallliu@google.com
-tgunn@google.com
-breadley@google.com
-nazaninb@google.com
+# Bug component: 450841
+include ../telephony/OWNERS
+lelandmiller@google.com
diff --git a/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/TelephonyProviderTest.java b/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/TelephonyProviderTest.java
index d54260b..7933791 100644
--- a/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/TelephonyProviderTest.java
+++ b/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/TelephonyProviderTest.java
@@ -18,7 +18,6 @@
 
 import android.content.ContentResolver;
 import android.database.Cursor;
-import android.net.Uri;
 import android.provider.Telephony.Carriers;
 import android.test.InstrumentationTestCase;
 
@@ -42,97 +41,18 @@
     // In JB MR1 access to the TelephonyProvider's Carriers table was clamped down and would
     // throw a SecurityException when queried. That was fixed in JB MR2. Verify that 3rd parties
     // can access the APN info the carriers table, after JB MR1.
+
+    // However, in R, a security bug was discovered that let apps read the password by querying
+    // multiple times and matching passwords against a regex in the query. Due to this hole, we're
+    // locking down the API and no longer allowing the exception. Accordingly, the behavior of this
+    // test is now reversed and we expect a SecurityException to be thrown.
     public void testAccessToApns() {
         try {
             String selection = Carriers.CURRENT + " IS NOT NULL";
             String[] selectionArgs = null;
             Cursor cursor = mContentResolver.query(Carriers.CONTENT_URI,
                     APN_PROJECTION, selection, selectionArgs, null);
-        } catch (SecurityException e) {
-            fail("No access to current APN");
-        }
-    }
-
-    public void testNoAccessToPassword() {
-        try {
-            String selection = Carriers.CURRENT + " IS NOT NULL AND "
-                    + Carriers.PASSWORD + " IS NOT NULL";
-            String[] selectionArgs = null;
-            Cursor cursor = mContentResolver.query(Carriers.CONTENT_URI,
-                    APN_PROJECTION, selection, selectionArgs, null);
-            fail("Expected SecurityException");
-        } catch (SecurityException e) {
-            // expected
-        }
-    }
-
-    public void testNoAccessToPasswordThruSort() {
-        try {
-            String selection = Carriers.CURRENT + " IS NOT NULL";
-            String[] selectionArgs = null;
-            String sort = "LIMIT CASE WHEN ((SELECT COUNT(*) FROM carriers WHERE"
-                    + " password LIKE 'a%') > 0) THEN 1 ELSE 0 END";
-            Cursor cursor = mContentResolver.query(Carriers.CONTENT_URI,
-                    APN_PROJECTION, selection, selectionArgs, sort);
-            fail("Expected SecurityException");
-        } catch (SecurityException e) {
-            // expected
-        }
-    }
-
-    public void testNoAccessToPasswordThruMixedCase() {
-        try {
-            String selection = Carriers.CURRENT + " IS NOT NULL";
-            String[] selectionArgs = null;
-            String sort = "LIMIT CASE WHEN ((SELECT COUNT(*) FROM carriers WHERE"
-                    + " PaSsWoRd LIKE 'a%') > 0) THEN 1 ELSE 0 END";
-            Cursor cursor = mContentResolver.query(Carriers.CONTENT_URI,
-                    APN_PROJECTION, selection, selectionArgs, sort);
-            fail("Expected SecurityException");
-        } catch (SecurityException e) {
-            // expected
-        }
-    }
-
-    public void testNoAccessToUser() {
-        try {
-            String selection = Carriers.CURRENT + " IS NOT NULL AND "
-                    + Carriers.USER + " IS NOT NULL";
-            String[] selectionArgs = null;
-            String sort = "LIMIT CASE WHEN ((SELECT COUNT(*) FROM carriers WHERE"
-                    + " user LIKE 'a%') > 0) THEN 1 ELSE 0 END";
-            Cursor cursor = mContentResolver.query(Carriers.CONTENT_URI,
-                    APN_PROJECTION, selection, selectionArgs, sort);
-            fail("Expected SecurityException");
-        } catch (SecurityException e) {
-            // expected
-        }
-    }
-
-    public void testNoAccessViaSubqueries() {
-        try {
-            String selection = Carriers.CURRENT + " IS NOT NULL";
-            String[] selectionArgs = null;
-            String sort = "LIMIT CASE WHEN ((SELECT COUNT(*) FROM carriers WHERE"
-                    + " mcc LIKE 'a%') > 0) THEN 1 ELSE 0 END";
-            Cursor cursor = mContentResolver.query(Carriers.CONTENT_URI,
-                    APN_PROJECTION, selection, selectionArgs, sort);
-            fail("Expected SecurityException");
-        } catch (SecurityException e) {
-            // expected
-        }
-    }
-
-    public void testNoAccessToUserWithDifferentUri() {
-        try {
-            String selection = Carriers.CURRENT + " IS NOT NULL AND "
-                    + Carriers.USER + " IS NOT NULL";
-            String[] selectionArgs = null;
-            String sort = "LIMIT CASE WHEN ((SELECT COUNT(*) FROM carriers WHERE"
-                    + " user LIKE 'a%') > 0) THEN 1 ELSE 0 END";
-            Cursor cursor = mContentResolver.query(Uri.parse("content://telephony/siminfo"),
-                    APN_PROJECTION, selection, selectionArgs, sort);
-            fail("Expected SecurityException");
+            fail("No SecurityException thrown");
         } catch (SecurityException e) {
             // expected
         }
diff --git a/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java b/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java
index fc4ef5a..783f83d 100644
--- a/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java
+++ b/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java
@@ -29,6 +29,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -43,16 +44,25 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DateUtilsTest {
+    private TimeZone mDefaultTimeZone;
     private long mBaseTime;
     private Context mContext;
 
     @Before
     public void setup() {
         mContext = InstrumentationRegistry.getTargetContext();
+        mDefaultTimeZone = TimeZone.getDefault();
+        // All tests in this class can assume the device time zone is set to GMT.
         TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
         mBaseTime = System.currentTimeMillis();
     }
 
+    @After
+    public void tearDown() {
+        // Set the default time zone back to what it was before setup().
+        TimeZone.setDefault(mDefaultTimeZone);
+    }
+
     @Test
     public void testGetDayOfWeekString() {
         if (!LocaleUtils.isCurrentLocale(mContext, Locale.US)) {
@@ -249,9 +259,25 @@
 
     @Test
     public void testIsToday() {
-        final long ONE_DAY_IN_MS = 24 * 60 * 60 * 1000;
-        assertTrue(DateUtils.isToday(mBaseTime));
+        // This test assumes TimeZone.getDefault() returns GMT. See setup() and comments below for
+        // details.
+
+        final int ONE_HOUR_IN_MS = 60 * 60 * 1000;
+        final int ONE_DAY_IN_MS = 24 * ONE_HOUR_IN_MS;
+
+        // mBaseTime < System.currentTimeMillis(), so subtracting 24 hours is guaranteed to
+        // be yesterday because this test uses GMT, i.e. no DST to consider.
         assertFalse(DateUtils.isToday(mBaseTime - ONE_DAY_IN_MS));
+
+        // We can assume mBaseTime is within a few seconds of the current system clock so adding
+        // one day plus one hour is more than sufficient to ensure isToday() == false.
+        assertFalse(DateUtils.isToday(mBaseTime + ONE_DAY_IN_MS + ONE_HOUR_IN_MS));
+
+        // This assertion is flaky because the method under test uses the system clock. If mBaseTime
+        // is set just before midnight (GMT) and isToday() is run just after midnight (GMT), this
+        // assertion will fail.
+        assertTrue("mBaseTime=" + mBaseTime + ", System.currentTimeMillis() after failure="
+                + System.currentTimeMillis(), DateUtils.isToday(mBaseTime));
     }
 
     @Test
diff --git a/tests/tests/text/src/android/text/format/cts/TimeTest.java b/tests/tests/text/src/android/text/format/cts/TimeTest.java
index ad5c3bf..aecf05b 100644
--- a/tests/tests/text/src/android/text/format/cts/TimeTest.java
+++ b/tests/tests/text/src/android/text/format/cts/TimeTest.java
@@ -37,10 +37,14 @@
 
 import java.time.Duration;
 import java.time.Instant;
+import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.time.LocalTime;
 import java.time.Month;
 import java.time.ZoneId;
+import java.time.ZoneOffset;
 import java.time.temporal.ChronoUnit;
+import java.time.temporal.JulianFields;
 import java.time.zone.ZoneOffsetTransition;
 import java.time.zone.ZoneRules;
 import java.util.ArrayList;
@@ -221,11 +225,44 @@
 
     @Test
     public void testIsEpoch() {
-        Time time = new Time();
+        // Create a Time that uses UTC to provide a behavior baseline.
+        Time time = new Time(Time.TIMEZONE_UTC);
+
+        // Time is initialized to 1970-01-01 00:00:00
         assertTrue(Time.isEpoch(time));
-        time.set(1, 2, 1970);
+
+        // 1970-01-01 23:59:59
+        checkIsEpochResult(time, 1970, 0 /* Jan */, 1, 23, 59, 59, true);
+
+        // 1970-01-02 00:00:00
+        checkIsEpochResult(time, 1970, 0 /* Jan */, 2, 0, 0, 0, false);
+
+        // 1969-12-31 23:59:59
+        checkIsEpochResult(time, 1969, 11 /* Dec */, 31, 23, 59, 59, false);
+
+        // Now demonstrate that the isEpoch() method just checks against the Julian day
+        // calculated for UTC. America/Los_Angeles is UTC-8 so all times have to be adjusted
+        // by 8 hours.
+        time.timezone = "America/Los_Angeles";
+
+        // 1969-12-31 15:59:59 == 1969-12-31 23:59:59 in UTC
+        checkIsEpochResult(time, 1969, 11 /* Dec */, 31, 15, 59, 59, false);
+
+        // 1969-12-31 16:00:00 == 1970-01-01 00:00:00 in UTC
+        checkIsEpochResult(time, 1969, 11 /* Dec */, 31, 16, 0, 0, true);
+
+        // 1970-01-01 15:59:59 == 1970-01-01 23:59:59 in UTC
+        checkIsEpochResult(time, 1970, 0 /* Jan */, 1, 15, 59, 59, true);
+
+        // 1970-01-01 16:00:00 == 1970-01-02 00:00:00 in UTC
+        checkIsEpochResult(time, 1970, 0 /* Jan */, 1, 16, 0, 0, false);
+    }
+
+    private void checkIsEpochResult(Time time, int year, int month, int monthDay, int hour,
+            int minute, int second, boolean expectedIsEpoch) {
+        time.set(second, minute, hour, monthDay, month, year);
         time.normalize(false);
-        assertFalse(Time.isEpoch(time));
+        assertEquals(expectedIsEpoch, Time.isEpoch(time));
     }
 
     @Test
@@ -1913,50 +1950,71 @@
         "Pacific/Midway",
     };
 
+    /**
+     * This test uses java.time classes to construct test times so that it can test various years
+     * including those outside of the int32 seconds range.
+     */
     @Test
     public void testGetJulianDay() {
-        Time time = new Time();
+        int[] years = { 2008, 1900, 1969, 2100 };
+        for (int year : years) {
+            for (String timeZone : mTimeZones) {
+                checkGetJulianDayForYearAndTimeZone(year, timeZone);
+            }
+        }
+    }
 
-        // For every 15th day of 2008, and for each of the timezones listed above,
-        // get the Julian day for 12am and then check that if we change the time we get the
-        // same Julian day. Note that one of the many problems with the Time class
-        // is its lack of error handling. If we accidentally hit a time that doesn't
-        // exist (because it was skipped by a daylight savings transition), rather than
-        // an error, you'll silently get 1970-01-01. We should @deprecate Time.
-        for (int monthDay = 1; monthDay <= 366; monthDay += 15) {
-            for (int zoneIndex = 0; zoneIndex < mTimeZones.length; zoneIndex++) {
-                // We leave the "month" as zero because we are changing the
-                // "monthDay" from 1 to 366. The call to normalize() will
-                // then change the "month" (but we don't really care).
-                time.set(0, 0, 12, monthDay, 0, 2008);
-                time.timezone = mTimeZones[zoneIndex];
-                long millis = time.normalize(true);
-                if (zoneIndex == 0) {
-                    Log.i(TAG, time.format("%B %d, %Y"));
-                }
+    private static void checkGetJulianDayForYearAndTimeZone(int year, String timeZone) {
+        final LocalTime midday = LocalTime.of(12, 0);
 
-                // This is the Julian day for 12pm for this day of the year
-                int julianDay = Time.getJulianDay(millis, time.gmtoff);
+        // For every 15th day of the year get the Julian day for 12pm and then check that if we
+        // change the time we get the same Julian day.
+        final LocalDate startDate = LocalDate.of(year, Month.JANUARY, 1);
+        final LocalDate stopDate = startDate.plusYears(1);
+        for (LocalDate testDate = startDate; testDate.isBefore(stopDate);
+                testDate = testDate.plusDays(15)) {
 
-                // Change the time during the day and check that we get the same
-                // Julian day.
-                for (int hour = 0; hour < 24; hour++) {
-                    for (int minute = 0; minute < 60; minute += 15) {
-                        time.set(0, minute, hour, monthDay, 0, 2008);
-                        millis = time.normalize(true);
-                        if (millis == -1) {
-                            // millis == -1 means the wall time does not exist in the chosen
-                            // timezone due to a DST change. We cannot calculate a JulianDay for -1.
-                            continue;
-                        }
+            LocalDateTime middayLocalDateTime = LocalDateTime.of(testDate, midday);
+            ZoneOffset middayOffset = ZoneId.of(timeZone).getRules().getOffset(middayLocalDateTime);
+            Instant middayInstant = middayLocalDateTime.toInstant(middayOffset);
 
-                        int day = Time.getJulianDay(millis, time.gmtoff);
-                        assertEquals("Julian day: " + day + " at time "
-                                + time.hour + ":" + time.minute
-                                + " != today's Julian day: " + julianDay
-                                + " timezone: " + time.timezone, day, julianDay);
-                    }
-                }
+            // Record the Julian day for the date/time given. Since we want to know the local
+            // calendar date we have to provide the time zone's UTC offset too.
+            int middayJulianDay =
+                    Time.getJulianDay(middayInstant.toEpochMilli(), middayOffset.getTotalSeconds());
+
+            // Check Time.getJulianDay() agrees with java.time's Julian day calculations.
+            assertEquals(middayJulianDay, JulianFields.JULIAN_DAY.getFrom(middayLocalDateTime));
+
+            checkGetJulianDayVariousTimes(timeZone, testDate);
+        }
+    }
+
+    private static void checkGetJulianDayVariousTimes(String timeZone, LocalDate testDate) {
+        long expectedJulianDay = JulianFields.JULIAN_DAY.getFrom(testDate);
+
+        // Change the time during the day and check that we get the same Julian day as the one
+        // for midday.
+        for (int hour = 0; hour < 24; hour++) {
+            for (int minute = 0; minute < 60; minute += 15) {
+                LocalTime localTime = LocalTime.of(hour, minute);
+                LocalDateTime localDateTime = LocalDateTime.of(testDate, localTime);
+                ZoneOffset localDateTimeOffset =
+                        ZoneId.of(timeZone).getRules().getOffset(localDateTime);
+                Instant instant = localDateTime.toInstant(localDateTimeOffset);
+                long millis = instant.toEpochMilli();
+
+                // Find the Julian day for the date/time by supplying the offset. Since we want
+                // to know the local calendar date we have to provide the UTC offset too.
+                int julianDay = Time.getJulianDay(millis, localDateTimeOffset.getTotalSeconds());
+
+                assertEquals("Julian day: " + julianDay + " at time "
+                                + hour + ":" + minute
+                                + " != today's Julian day: " + expectedJulianDay
+                                + " millis: " + millis
+                                + " localDatetime: " + localDateTime
+                                + " timeZone: " + timeZone,
+                        julianDay, expectedJulianDay);
             }
         }
     }
diff --git a/tests/tests/toast/OWNERS b/tests/tests/toast/OWNERS
new file mode 100644
index 0000000..5cb1f47
--- /dev/null
+++ b/tests/tests/toast/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 137825
+svetoslavganov@google.com
+moltmann@google.com
+toddke@google.com
diff --git a/tests/tests/toastlegacy/OWNERS b/tests/tests/toastlegacy/OWNERS
new file mode 100644
index 0000000..d21846d
--- /dev/null
+++ b/tests/tests/toastlegacy/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 137825
+include ../toast/OWNERS
diff --git a/tests/tests/tools/processors/view_inspector/OWNERS b/tests/tests/tools/processors/view_inspector/OWNERS
new file mode 100644
index 0000000..1572c57
--- /dev/null
+++ b/tests/tests/tools/processors/view_inspector/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 25700
+ashleyrose@google.com
+aurimas@google.com
diff --git a/tests/tests/transition/src/android/transition/cts/ActivityTransitionTest.java b/tests/tests/transition/src/android/transition/cts/ActivityTransitionTest.java
index 8df3484..2301438 100644
--- a/tests/tests/transition/src/android/transition/cts/ActivityTransitionTest.java
+++ b/tests/tests/transition/src/android/transition/cts/ActivityTransitionTest.java
@@ -72,6 +72,7 @@
     @Override
     public void setup() {
         super.setup();
+        ActivityOptions.setExitTransitionTimeout(10000);
         setTransitions(new TrackingVisibility(), new TrackingVisibility(),
                 new TrackingTransition());
     }
@@ -102,6 +103,7 @@
             }
         });
         TargetActivity.clearCreated();
+        ActivityOptions.setExitTransitionTimeout(1000);
     }
 
     // When using ActivityOptions.makeBasic(), no transitions should run
diff --git a/tests/tests/uiautomation/Android.bp b/tests/tests/uiautomation/Android.bp
index 22dd8f6..67f3ed7 100644
--- a/tests/tests/uiautomation/Android.bp
+++ b/tests/tests/uiautomation/Android.bp
@@ -23,7 +23,7 @@
         "cts_instant",
     ],
     static_libs: [
-        "compatibility-device-util-axt",
+        "CtsAccessibilityCommon",
         "ctstestrunner-axt",
         "ub-uiautomator",
     ],
diff --git a/tests/tests/uiautomation/AndroidManifest.xml b/tests/tests/uiautomation/AndroidManifest.xml
index af4f4cd..b1e5821 100644
--- a/tests/tests/uiautomation/AndroidManifest.xml
+++ b/tests/tests/uiautomation/AndroidManifest.xml
@@ -21,10 +21,12 @@
         android:targetSandboxVersion="2">
 
   <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
   <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
-  <uses-permission android:name="android.permission.CAMERA" />
+  <uses-permission android:name="android.permission.RECORD_AUDIO" />
 
-  <application android:theme="@android:style/Theme.Holo.NoActionBar" >
+  <application android:theme="@android:style/Theme.Holo.NoActionBar"
+          android:requestLegacyExternalStorage="true">
 
       <uses-library android:name="android.test.runner"/>
 
diff --git a/tests/tests/uiautomation/AndroidTest.xml b/tests/tests/uiautomation/AndroidTest.xml
index f5cf748..05bfc9c 100644
--- a/tests/tests/uiautomation/AndroidTest.xml
+++ b/tests/tests/uiautomation/AndroidTest.xml
@@ -23,10 +23,14 @@
         <option name="test-file-name" value="CtsUiAutomationTestCases.apk" />
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <option name="run-command" value="pm revoke android.app.uiautomation.cts android.permission.CAMERA" />
+        <option name="run-command" value="pm revoke android.app.uiautomation.cts android.permission.RECORD_AUDIO" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.app.uiautomation.cts" />
         <option name="runtime-hint" value="6m42s" />
     </test>
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="directory-keys" value="/sdcard/android.app.uiautomation.cts" />
+        <option name="collect-on-run-ended-only" value="true" />
+    </metrics_collector>
 </configuration>
diff --git a/tests/tests/uiautomation/OWNERS b/tests/tests/uiautomation/OWNERS
index bbcb1a7..a98c458 100644
--- a/tests/tests/uiautomation/OWNERS
+++ b/tests/tests/uiautomation/OWNERS
@@ -1,2 +1,3 @@
 # Bug component: 44215
-pweaver@google.com
\ No newline at end of file
+pweaver@google.com
+rhedjao@google.com
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationLogRule.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationLogRule.java
deleted file mode 100644
index 3f81d3a..0000000
--- a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationLogRule.java
+++ /dev/null
@@ -1,94 +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.
- */
-
-package android.app.uiautomation.cts;
-
-import android.app.UiAutomation;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.test.InstrumentationRegistry;
-
-import com.android.compatibility.common.util.SystemUtil;
-
-import org.junit.AssumptionViolatedException;
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-import java.io.IOException;
-
-/**
- * Improves UiAutomationTest logging, dumps log when a test case gets failed.
- *
- *  <ol>
- *    <li>Call {@code dumpsys accessibility}.
- *  </ol>
- */
-public final class UiAutomationLogRule implements TestRule {
-
-    private final String mTestName;
-
-    public UiAutomationLogRule(@NonNull String testName) {
-        mTestName = testName;
-    }
-
-    @Override
-    public Statement apply(Statement base, Description description) {
-        return new Statement() {
-            @Override
-            public void evaluate() throws Throwable {
-                Throwable throwable = null;
-                // First run the test
-                try {
-                    base.evaluate();
-                } catch (Throwable t) {
-                    throwable = t;
-                }
-
-                // Ignore AssumptionViolatedException. It's not a test fail.
-                if (throwable != null && throwable instanceof AssumptionViolatedException) {
-                    throwable = null;
-                }
-
-                if (throwable != null) {
-                    try {
-                        Log.e(mTestName, "TEST FAIL");
-                        dump();
-                    } catch (Throwable t) {
-                        Log.e(mTestName, "Dump fail", t);
-                    }
-                }
-
-                // Finally, throw exception!
-                if (throwable == null) return;
-                throw throwable;
-            }
-        };
-    }
-
-    private void dump() throws IOException {
-        UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(
-                UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
-        try {
-            final String a11yDump = SystemUtil.runShellCommand(
-                    uiAutomation, "dumpsys accessibility");
-            Log.e(mTestName, "==== dumpsys accessibility ====\n" + a11yDump);
-        } finally {
-            uiAutomation.destroy();
-        }
-    }
-}
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
index 828d9b5..f5df2c0 100755
--- a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
@@ -17,13 +17,13 @@
 package android.app.uiautomation.cts;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import android.Manifest;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -71,8 +71,8 @@
     private static final int TIMEOUT_FOR_SERVICE_ENABLE = 10000; // millis; 10s
 
     @Rule
-    public final UiAutomationLogRule mLogRule = new UiAutomationLogRule(
-            UiAutomationTest.class.getSimpleName());
+    public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+            new AccessibilityDumpOnFailureRule();
 
     @Before
     public void setUp() throws Exception {
@@ -99,7 +99,7 @@
         }
         try {
             packageManager.grantRuntimePermission(context.getPackageName(),
-                    Manifest.permission.CAMERA, Process.myUserHandle());
+                    Manifest.permission.RECORD_AUDIO, Process.myUserHandle());
             fail("Should not be able to access APIs protected by a permission apps cannot get");
         } catch (SecurityException e) {
             /* expected */
@@ -113,17 +113,17 @@
             activityManager.getPackageImportance("foo.bar.baz");
 
             // Grant ourselves a runtime permission (was granted at install)
-            assertSame(packageManager.checkPermission(Manifest.permission.CAMERA,
+            assertSame(packageManager.checkPermission(Manifest.permission.RECORD_AUDIO,
                     context.getPackageName()), PackageManager.PERMISSION_DENIED);
             packageManager.grantRuntimePermission(context.getPackageName(),
-                    Manifest.permission.CAMERA, Process.myUserHandle());
+                    Manifest.permission.RECORD_AUDIO, Process.myUserHandle());
         } catch (SecurityException e) {
             fail("Should be able to access APIs protected by a permission apps cannot get");
         } finally {
             getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
         }
         // Make sure the grant worked
-        assertSame(packageManager.checkPermission(Manifest.permission.CAMERA,
+        assertSame(packageManager.checkPermission(Manifest.permission.RECORD_AUDIO,
                 context.getPackageName()), PackageManager.PERMISSION_GRANTED);
 
 
@@ -136,7 +136,7 @@
         }
         try {
             packageManager.revokeRuntimePermission(context.getPackageName(),
-                    Manifest.permission.CAMERA, Process.myUserHandle());
+                    Manifest.permission.RECORD_AUDIO, Process.myUserHandle());
             fail("Should not be able to access APIs protected by a permission apps cannot get");
         } catch (SecurityException e) {
             /* expected */
diff --git a/tests/tests/uidisolation/OWNERS b/tests/tests/uidisolation/OWNERS
new file mode 100644
index 0000000..94522e3
--- /dev/null
+++ b/tests/tests/uidisolation/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 36824
+include /tests/tests/security/OWNERS
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorFilterAlphaTest.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorFilterAlphaTest.java
index edeb2e8..f9f47a9 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorFilterAlphaTest.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorFilterAlphaTest.java
@@ -26,10 +26,11 @@
 import android.graphics.PorterDuffXfermode;
 import android.graphics.drawable.ColorDrawable;
 import android.uirendering.cts.bitmapverifiers.SamplePointVerifier;
+import android.uirendering.cts.runner.SkipPresubmit;
 import android.uirendering.cts.testinfrastructure.ActivityTestBase;
 import android.uirendering.cts.testinfrastructure.CanvasClient;
 
-import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -38,7 +39,8 @@
 
 import java.util.List;
 
-@LargeTest // Temporarily hidden from presubmit
+@SkipPresubmit // Temporarily hidden from presubmit
+@MediumTest
 @RunWith(Parameterized.class)
 public class ColorFilterAlphaTest extends ActivityTestBase {
     // We care about one point in each of the four rectangles of different alpha values, as well as
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/HardwareBitmapTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/HardwareBitmapTests.java
index b6a09c0..1a7151b 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/HardwareBitmapTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/HardwareBitmapTests.java
@@ -225,6 +225,7 @@
             NinePatchDrawable ninePatch = (NinePatchDrawable) Drawable.createFromResourceStream(
                     mRes, null, is, null, HARDWARE_OPTIONS);
             ninePatch.setBounds(0, 0, width, height);
+            ninePatch.setFilterBitmap(false);
             ninePatch.draw(canvas);
         }, true).runWithVerifier(new GoldenImageVerifier(getActivity(),
                 R.drawable.golden_hardwaretest_ninepatch, new MSSIMComparer(0.95)));
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapAsserter.java b/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapAsserter.java
index 47f4c89..55adc11 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapAsserter.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapAsserter.java
@@ -18,14 +18,16 @@
 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;
 
+import androidx.test.platform.app.InstrumentationRegistry;
+
 public class BitmapAsserter {
+    private static final boolean TAKE_SCREENSHOTS_ON_FAILURE = true;
     private DifferenceVisualizer mDifferenceVisualizer;
     private String mClassName;
 
@@ -64,6 +66,7 @@
 
         if (!success) {
             BitmapDumper.dumpBitmaps(bitmap1, bitmap2, testName, mClassName, mDifferenceVisualizer);
+            onFailure(testName);
         }
 
         assertTrue(debugMessage, success);
@@ -83,9 +86,17 @@
             BitmapDumper.dumpBitmap(croppedBitmap, testName, mClassName);
             BitmapDumper.dumpBitmap(bitmapVerifier.getDifferenceBitmap(), testName + "_verifier",
                     mClassName);
+            onFailure(testName);
         }
         assertTrue(debugMessage, success);
     }
 
+    private void onFailure(String testName) {
+        if (TAKE_SCREENSHOTS_ON_FAILURE) {
+            Bitmap screenshot = InstrumentationRegistry.getInstrumentation()
+                    .getUiAutomation().takeScreenshot();
+            BitmapDumper.dumpBitmap(screenshot, testName + "_fullscreenshot", mClassName);
+        }
 
+    }
 }
diff --git a/tests/tests/util/Android.bp b/tests/tests/util/Android.bp
index 3533502..0ea529e 100644
--- a/tests/tests/util/Android.bp
+++ b/tests/tests/util/Android.bp
@@ -26,6 +26,7 @@
         "androidx.annotation_annotation",
         "androidx.test.rules",
         "ctstestrunner-axt",
+        "cts-install-lib",
     ],
     srcs: ["src/**/*.java"],
     platform_apis: true,
diff --git a/tests/tests/util/AndroidManifest.xml b/tests/tests/util/AndroidManifest.xml
index 0e71890..77ab380 100644
--- a/tests/tests/util/AndroidManifest.xml
+++ b/tests/tests/util/AndroidManifest.xml
@@ -21,6 +21,8 @@
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
     <uses-permission android:name="android.permission.READ_LOGS" />
     <application>
+        <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
+                  android:exported="true" />
         <uses-library android:name="android.test.runner" />
     </application>
 
diff --git a/tests/tests/util/src/android/util/cts/InstallUtilTest.java b/tests/tests/util/src/android/util/cts/InstallUtilTest.java
new file mode 100644
index 0000000..5f48f37
--- /dev/null
+++ b/tests/tests/util/src/android/util/cts/InstallUtilTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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.util.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.Manifest;
+import android.content.pm.PackageInstaller;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.cts.install.lib.Install;
+import com.android.cts.install.lib.InstallUtils;
+import com.android.cts.install.lib.LocalIntentSender;
+import com.android.cts.install.lib.TestApp;
+import com.android.cts.install.lib.Uninstall;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "Instant apps cannot create installer sessions")
+/**
+ * Test for cts.install.lib.
+ * <p>This test also tries to showcase how to use the library.
+ */
+public class InstallUtilTest {
+    /**
+     * Drops adopted shell permissions and uninstalls the test apps.
+     */
+    @After
+    public void teardown() throws InterruptedException, IOException {
+        // Good tests clean up after themselves.
+        // Remember that other tests will be using the same test apps.
+        Uninstall.packages(TestApp.A, TestApp.B);
+
+        InstallUtils.dropShellPermissionIdentity();
+    }
+
+    /**
+     * Adopts common permissions needed to test rollbacks and uninstalls the
+     * test apps.
+     */
+    @Before
+    public void setup() throws InterruptedException, IOException {
+        InstallUtils.adoptShellPermissionIdentity(
+                    Manifest.permission.INSTALL_PACKAGES,
+                    Manifest.permission.DELETE_PACKAGES);
+        // Better tests work regardless of whether other tests clean up after themselves or not.
+        Uninstall.packages(TestApp.A, TestApp.B);
+    }
+
+    @Test
+    public void testCommitSingleTestApp() throws Exception {
+        // Assert that the test app was not previously installed
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
+        // Install version 1 of TestApp.A
+        // Install#commit() asserts that the installation succeeds, so if it fails,
+        // an AssertionError would be thrown.
+        Install.single(TestApp.A1).commit();
+        // Even though the install session of TestApp.A1 is guaranteed to be committed by this stage
+        // it's still good practice to assert that the installed version of the app is the desired
+        // one. This is due to the fact that not all committed sessions are finalized sessions, i.e.
+        // staged install session.
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+        // No need to uninstall test app, as #teardown will do the job.
+    }
+
+    @Test
+    public void testCommitMultiTestApp() throws Exception {
+        // Assert that the test app was not previously installed.
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(-1);
+        // Install version 1 of TestApp.A and version 2 of TestApp.B in one atomic install.
+        // Same notes as the single install case apply in the multi install case.
+        Install.multi(TestApp.A1, TestApp.B2).commit();
+
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
+    }
+
+    @Test
+    public void testOpenAndAbandonSessionForSingleApk() throws Exception {
+        int sessionId = Install.single(TestApp.A1).createSession();
+        PackageInstaller.Session session = InstallUtils
+                .openPackageInstallerSession(sessionId);
+        assertThat(session).isNotNull();
+
+        // TODO: is there a way to verify that the APK has been written?
+
+        // At this stage, the session can be directly manipulated using
+        // PackageInstaller.Session API, i.e., it can be abandoned.
+        session.abandon();
+        // TODO: maybe add session callback and verify that session was abandoned?
+        // Assert session has not been installed
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
+    }
+
+    @Test
+    public void testOpenAndCommitSessionForSingleApk() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
+        int sessionId = Install.single(TestApp.A1).createSession();
+        PackageInstaller.Session session = InstallUtils
+                .openPackageInstallerSession(sessionId);
+        assertThat(session).isNotNull();
+
+        // Session can be committed directly, but a BroadcastReceiver must be provided.
+        session.commit(LocalIntentSender.getIntentSender());
+        InstallUtils.assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
+
+        // Verify app has been installed
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+    }
+
+    @Test
+    public void testOpenSingleSessionWithParameters() throws Exception {
+        int sessionId = Install.single(TestApp.A1).setStaged().createSession();
+        PackageInstaller.Session session = InstallUtils.openPackageInstallerSession(sessionId);
+        assertThat(session.isStaged()).isTrue();
+
+        session.abandon();
+    }
+
+    @Test
+    public void testOpenSessionForMultiPackageSession() throws Exception {
+        int parentSessionId = Install.multi(TestApp.A1, TestApp.B1).setStaged().createSession();
+        PackageInstaller.Session parentSession = InstallUtils
+                .openPackageInstallerSession(parentSessionId);
+        assertThat(parentSession.isMultiPackage()).isTrue();
+
+        assertThat(parentSession.isStaged()).isTrue();
+
+        // Child sessions are consistent with the parent parameters
+        int[] childSessionIds = parentSession.getChildSessionIds();
+        assertThat(childSessionIds.length).isEqualTo(2);
+        for (int childSessionId : childSessionIds) {
+            PackageInstaller.Session childSession = InstallUtils
+                    .openPackageInstallerSession(childSessionId);
+            assertThat(childSession.isStaged()).isTrue();
+        }
+
+        parentSession.abandon();
+
+        // Verify apps have not been installed
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(-1);
+    }
+
+    @Test
+    public void testMutateInstallFlags() throws Exception {
+        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+        params.setInstallAsApex();
+        params.setStaged();
+        InstallUtils.mutateInstallFlags(params, 0x00080000);
+        final Class<?> clazz = params.getClass();
+        Field installFlagsField = clazz.getDeclaredField("installFlags");
+        int installFlags = installFlagsField.getInt(params);
+        assertThat(installFlags & 0x00080000).isEqualTo(0x00080000);
+    }
+}
diff --git a/tests/tests/view/OWNERS b/tests/tests/view/OWNERS
new file mode 100644
index 0000000..8200a30
--- /dev/null
+++ b/tests/tests/view/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 25700
+adamp@google.com
+shepshapard@google.com
+clarabayarri@google.com
diff --git a/tests/tests/view/src/android/view/cts/TextureViewTest.java b/tests/tests/view/src/android/view/cts/TextureViewTest.java
index 38b993c..e97ddb8 100644
--- a/tests/tests/view/src/android/view/cts/TextureViewTest.java
+++ b/tests/tests/view/src/android/view/cts/TextureViewTest.java
@@ -300,22 +300,37 @@
     public void testSamplingWithTransform() throws Throwable {
         final TextureViewCtsActivity activity = mActivityRule.launchActivity(null);
         final TextureView textureView = activity.getTextureView();
+        final int viewWidth = textureView.getWidth();
+        final int viewHeight = textureView.getHeight();
         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, activity.getTextureView(), null);
         // Remove cover and calculate TextureView position on the screen.
         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule,
                 activity.findViewById(android.R.id.content), () -> activity.removeCover());
 
         float[][] matrices = {
-            {1, 0, 0, 0, 1, 0, 0, 0, 1},        // identity matrix
-            {1, 0, 0, 0, 1, 10.3f, 0, 0, 1},    // translation matrix with a fractional offset
-            {1, 0, 0, 0, 0.75f, 0, 0, 0, 1},    // scaling matrix
-            {1, 0, 0, 0, 1, 10f, 0, 0, 1}       // translation matrix with an integer offset
+            {1, 0, 0, 0, 1, 0, 0, 0, 1},            // identity matrix
+            {1, 0, 0, 0, 1, 10.3f, 0, 0, 1},        // translation matrix with a fractional offset
+            {1, 0, 0, 0, 0.75f, 0, 0, 0, 1},        // scaling matrix
+            {1, 0, 0, 0, 1, 10f, 0, 0, 1},          // translation matrix with an integer offset
+            {0, -1, viewWidth, 1, 0, 0, 0, 0, 1},   // 90 rotation matrix + integer translate X
+            {0, 1, 0, -1, 0, viewWidth, 0, 0, 1},   // 270 rotation matrix + integer translate Y
+            {-1, 0, viewWidth, 0, 1, 0, 0, 0, 1},   // H flip matrix + integer translate X
+            {1, 0, 0, 0, -1, viewHeight, 0, 0, 1},  // V flip matrix + integer translate Y
+            {-1, 0, viewWidth, 0, -1, viewHeight, 0, 0, 1}, // 180 rotation + integer translate X Y
+            {0, -1, viewWidth - 10.3f, 1, 0, 0, 0, 0, 1},  // 90 rotation matrix with a fractional
+                                                           // offset
         };
         boolean[] nearestSampling = {
             true,  // nearest sampling for identity
             false, // bilerp sampling for fractional translate
             false, // bilerp sampling for scaling
-            true   // nearest sampling for integer translate
+            true,  // nearest sampling for integer translate
+            true,  // nearest sampling for 90 rotation with integer translate
+            true,  // nearest sampling for 270 rotation with integer translate
+            true,  // nearest sampling for H flip with integer translate
+            true,  // nearest sampling for V flip with integer translate
+            true,  // nearest sampling for 180 rotation with integer translate
+            false, // bilerp sampling for 90 rotation matrix with a fractional offset
         };
         for (int i = 0; i < nearestSampling.length; i++) {
 
@@ -345,8 +360,10 @@
             // "texturePos" has SurfaceTexture position inside the TextureView.
             RectF texturePosF = new RectF(0, 0, viewPos.width(), viewPos.height());
             transform.mapRect(texturePosF);
-            //clip parts outside TextureView
-            texturePosF.intersect(0, 0, viewPos.width(), viewPos.height());
+            // Clip parts outside TextureView.
+            // Matrices are picked, so that the drawing area is not empty.
+            assertTrue("empty test area",
+                    texturePosF.intersect(0, 0, viewPos.width(), viewPos.height()));
             Rect texturePos = new Rect((int) Math.ceil(texturePosF.left),
                     (int) Math.ceil(texturePosF.top), (int) Math.floor(texturePosF.right),
                     (int) Math.floor(texturePosF.bottom));
diff --git a/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationSessionIdTest.java b/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationSessionIdTest.java
new file mode 100644
index 0000000..eef6ccd
--- /dev/null
+++ b/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationSessionIdTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.view.textclassifier.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.view.textclassifier.SelectionEvent;
+import android.view.textclassifier.TextClassificationContext;
+import android.view.textclassifier.TextClassificationManager;
+import android.view.textclassifier.TextClassificationSessionId;
+import android.view.textclassifier.TextClassifier;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@SmallTest
+public class TextClassificationSessionIdTest {
+
+    @Test
+    public void flattenToString() {
+        TextClassificationManager textClassificationManager =
+                ApplicationProvider.getApplicationContext().getSystemService(
+                        TextClassificationManager.class);
+        textClassificationManager.setTextClassifier(TextClassifier.NO_OP);
+        TextClassifier textClassifier = textClassificationManager.createTextClassificationSession(
+                new TextClassificationContext.Builder(
+                        "com.pkg",
+                        TextClassifier.WIDGET_TYPE_TEXTVIEW)
+                        .build());
+        SelectionEvent startedEvent =
+                SelectionEvent.createSelectionStartedEvent(
+                        SelectionEvent.INVOCATION_LINK, /* start= */ 10);
+
+        textClassifier.onSelectionEvent(startedEvent);
+        TextClassificationSessionId sessionId = startedEvent.getSessionId();
+
+        assertThat(sessionId).isNotNull();
+        assertThat(sessionId.flattenToString()).isNotEmpty();
+    }
+}
diff --git a/tests/tests/voiceinteraction/OWNERS b/tests/tests/voiceinteraction/OWNERS
index dbffaa0..fcb224c 100644
--- a/tests/tests/voiceinteraction/OWNERS
+++ b/tests/tests/voiceinteraction/OWNERS
@@ -1,2 +1,4 @@
 # Bug component: 533220
+adamhe@google.com
+svetoslavganov@google.com
 felipeal@google.com
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/DirectActionsTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/DirectActionsTest.java
index 338c361..6c4a3a7 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/DirectActionsTest.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/DirectActionsTest.java
@@ -134,7 +134,6 @@
             mActivityControl.finishActivity();
         }
     }
-
     private final class SessionControl {
         private @Nullable RemoteCallback mControl;
 
diff --git a/tests/tests/voicesettings/OWNERS b/tests/tests/voicesettings/OWNERS
index dbffaa0..fcb224c 100644
--- a/tests/tests/voicesettings/OWNERS
+++ b/tests/tests/voicesettings/OWNERS
@@ -1,2 +1,4 @@
 # Bug component: 533220
+adamhe@google.com
+svetoslavganov@google.com
 felipeal@google.com
diff --git a/tests/tests/widget/OWNERS b/tests/tests/widget/OWNERS
new file mode 100644
index 0000000..12f176d
--- /dev/null
+++ b/tests/tests/widget/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 25700
+adamp@google.com
+mount@google.com
+shepshapard@google.com
+clarabayarri@google.com
diff --git a/tests/tests/widget/res/layout/widget_attribute_layout.xml b/tests/tests/widget/res/layout/widget_attribute_layout.xml
index 1cc5517..872b0eb 100644
--- a/tests/tests/widget/res/layout/widget_attribute_layout.xml
+++ b/tests/tests/widget/res/layout/widget_attribute_layout.xml
@@ -47,4 +47,11 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         style="@style/ExplicitStyle1" />
+
+    <ViewAnimator
+        android:id="@+id/viewAnimator"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:inAnimation="@android:anim/slide_in_left"
+        android:outAnimation="@android:anim/slide_out_right" />
 </LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/widget/src/android/widget/cts/MagnifierTest.java b/tests/tests/widget/src/android/widget/cts/MagnifierTest.java
index a31963c..0264665 100644
--- a/tests/tests/widget/src/android/widget/cts/MagnifierTest.java
+++ b/tests/tests/widget/src/android/widget/cts/MagnifierTest.java
@@ -728,14 +728,15 @@
         WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, () -> {
             mActivity.setContentView(R.layout.magnifier_activity_centered_surfaceview_layout);
         }, false /* forceLayout */);
-        final View view = mActivity.findViewById(R.id.magnifier_centered_view);
-        WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, view, () -> {
+        WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, () -> {
             // Draw something in the SurfaceView for the Magnifier to copy.
+            final View view = mActivity.findViewById(R.id.magnifier_centered_view);
             final SurfaceHolder surfaceHolder = ((SurfaceView) view).getHolder();
             final Canvas canvas = surfaceHolder.lockHardwareCanvas();
             canvas.drawColor(Color.BLUE);
             surfaceHolder.unlockCanvasAndPost(canvas);
-        });
+        }, false /* forceLayout */);
+        final View view = mActivity.findViewById(R.id.magnifier_centered_view);
         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/TextClockTest.java b/tests/tests/widget/src/android/widget/cts/TextClockTest.java
index 9a41bbf..9438ed1 100644
--- a/tests/tests/widget/src/android/widget/cts/TextClockTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextClockTest.java
@@ -104,6 +104,16 @@
             }
         }
 
+        // If the time was already set to 12, we want it to start at locale-specified
+        if (mDefaultTime1224 != null) {
+            final CountDownLatch changeDefault = registerForChanges(Settings.System.TIME_12_24);
+            mActivityRule.runOnUiThread(() -> {
+                Settings.System.putString(resolver, Settings.System.TIME_12_24, null);
+            });
+            assertTrue(changeDefault.await(1, TimeUnit.SECONDS));
+        }
+
+        // Change to 12-hour mode
         final CountDownLatch change12 = registerForChanges(Settings.System.TIME_12_24);
         mActivityRule.runOnUiThread(() -> {
             Settings.System.putInt(resolver, Settings.System.TIME_12_24, 12);
@@ -124,6 +134,7 @@
             return ok.value;
         });
 
+        // Change to 24-hour mode
         final CountDownLatch change24 = registerForChanges(Settings.System.TIME_12_24);
         mActivityRule.runOnUiThread(() -> {
             Settings.System.putInt(resolver, Settings.System.TIME_12_24, 24);
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index 01d5cd8..86e674e 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -169,6 +169,7 @@
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Locale;
 
 /**
@@ -7149,6 +7150,106 @@
                 TextUtils.equals(hintText, info.getHintText()));
     }
 
+    @UiThreadTest
+    @Test
+    public void testOnInitializeA11yNodeInfo_removesClickabilityWithLinkMovementMethod() {
+        mTextView = findTextView(R.id.textview_text);
+        mTextView.setMovementMethod(new LinkMovementMethod());
+
+        assertTrue("clickable should be true", mTextView.isClickable());
+        assertFalse("View should not have onClickListeners", mTextView.hasOnClickListeners());
+        assertTrue("longClickable should be true", mTextView.isLongClickable());
+        assertFalse("View should not have onLongClickListeners",
+                mTextView.hasOnLongClickListeners());
+
+        final AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+        mTextView.onInitializeAccessibilityNodeInfo(info);
+        List<AccessibilityNodeInfo.AccessibilityAction> actionList = info.getActionList();
+        assertFalse("info's isClickable should be false", info.isClickable());
+        assertFalse("info should not have ACTION_CLICK",
+                actionList.contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK));
+        assertFalse("info's isLongClickable should be false",
+                info.isLongClickable());
+        assertFalse("info should not have ACTION_LONG_CLICK",
+                actionList.contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK));
+    }
+
+    @UiThreadTest
+    @Test
+    public void testOnInitializeA11yNodeInfo_keepsClickabilityWithMovementMethod() {
+        mTextView = findTextView(R.id.textview_text);
+        mTextView.setMovementMethod(new ArrowKeyMovementMethod());
+
+        assertTrue("clickable should be true", mTextView.isClickable());
+        assertFalse("View should not have onClickListeners", mTextView.hasOnClickListeners());
+        assertTrue("longClickable should be false", mTextView.isLongClickable());
+        assertFalse("View should not have onLongClickListeners",
+                mTextView.hasOnLongClickListeners());
+
+        final AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+        mTextView.onInitializeAccessibilityNodeInfo(info);
+        List<AccessibilityNodeInfo.AccessibilityAction> actionList = info.getActionList();
+        assertTrue("info's isClickable should be true", info.isClickable());
+        assertTrue("info should have ACTION_CLICK",
+                actionList.contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK));
+        assertTrue("info's isLongClickable should be true",
+                info.isLongClickable());
+        assertTrue("info should have ACTION_LONG_CLICK",
+                actionList.contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK));
+    }
+
+    @UiThreadTest
+    @Test
+    public void testOnInitializeA11yNodeInfo_keepsClickabilityWithOnClickListener() {
+        mTextView = findTextView(R.id.textview_text);
+        mTextView.setMovementMethod(new LinkMovementMethod());
+
+        assertTrue("clickable should be true", mTextView.isClickable());
+        assertFalse("View should not have onClickListeners", mTextView.hasOnClickListeners());
+        assertTrue("longClickable should be true", mTextView.isLongClickable());
+        assertFalse("View should not have onLongClickListeners",
+                mTextView.hasOnLongClickListeners());
+
+        mTextView.setOnClickListener(mock(View.OnClickListener.class));
+
+        final AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+        mTextView.onInitializeAccessibilityNodeInfo(info);
+        List<AccessibilityNodeInfo.AccessibilityAction> actionList = info.getActionList();
+        assertTrue("info's isClickable should be true", info.isClickable());
+        assertTrue("info should have ACTION_CLICK",
+                actionList.contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK));
+        assertFalse("info's isLongClickable should not be true",
+                info.isLongClickable());
+        assertFalse("info should have ACTION_LONG_CLICK",
+                actionList.contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK));
+    }
+
+    @UiThreadTest
+    @Test
+    public void testOnInitializeA11yNodeInfo_keepsLongClickabilityWithOnLongClickListener() {
+        mTextView = findTextView(R.id.textview_text);
+        mTextView.setMovementMethod(new LinkMovementMethod());
+
+        assertTrue("clickable should be true", mTextView.isClickable());
+        assertFalse("View should not have onClickListeners", mTextView.hasOnClickListeners());
+        assertTrue("longClickable should be true", mTextView.isLongClickable());
+        assertFalse("View should not have onLongClickListeners",
+                mTextView.hasOnLongClickListeners());
+
+        mTextView.setOnLongClickListener(mock(View.OnLongClickListener.class));
+
+        final AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+        mTextView.onInitializeAccessibilityNodeInfo(info);
+        List<AccessibilityNodeInfo.AccessibilityAction> actionList = info.getActionList();
+        assertFalse("info's isClickable should be false", info.isClickable());
+        assertFalse("info should not have ACTION_CLICK",
+                actionList.contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK));
+        assertTrue("info's isLongClickable should be true",
+                info.isLongClickable());
+        assertTrue("info should have ACTION_LONG_CLICK",
+                actionList.contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK));
+    }
+
     @Test
     public void testAutosizeWithMaxLines_shouldNotThrowException() throws Throwable {
         // the layout contains an instance of CustomTextViewWithTransformationMethod
diff --git a/tests/tests/widget/src/android/widget/cts/WidgetAttributeTest.kt b/tests/tests/widget/src/android/widget/cts/WidgetAttributeTest.kt
index 5b41755..63ad006 100644
--- a/tests/tests/widget/src/android/widget/cts/WidgetAttributeTest.kt
+++ b/tests/tests/widget/src/android/widget/cts/WidgetAttributeTest.kt
@@ -17,10 +17,6 @@
 package android.widget.cts
 
 import android.app.Activity
-import androidx.test.InstrumentationRegistry
-import androidx.test.filters.MediumTest
-import androidx.test.rule.ActivityTestRule
-import androidx.test.runner.AndroidJUnit4
 import android.support.test.uiautomator.UiDevice
 import android.view.LayoutInflater
 import android.widget.LinearLayout
@@ -28,6 +24,11 @@
 import android.widget.Switch
 import android.widget.TextView
 import android.widget.Toolbar
+import android.widget.ViewAnimator
+import androidx.test.InstrumentationRegistry
+import androidx.test.filters.MediumTest
+import androidx.test.rule.ActivityTestRule
+import androidx.test.runner.AndroidJUnit4
 import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Before
@@ -121,6 +122,12 @@
         assertEquals(R.style.ParentOfExplicitStyle1, stackTextView1textColorHighlight[2])
         assertEquals(android.R.style.Widget_Material_TextView, stackTextView1textColorHighlight[3])
         assertEquals(android.R.style.Widget_TextView, stackTextView1textColorHighlight[4])
+
+        val viewAnimator = rootView.findViewById<ViewAnimator>(R.id.viewAnimator)
+        val viewAnimatorOutAnimation =
+                viewAnimator.getAttributeResolutionStack(android.R.attr.outAnimation)
+        assertEquals(1, viewAnimatorOutAnimation.size.toLong())
+        assertEquals(R.layout.widget_attribute_layout, viewAnimatorOutAnimation[0])
     }
 
     @Test
diff --git a/tests/vr/OWNERS b/tests/vr/OWNERS
new file mode 100644
index 0000000..b0936ba
--- /dev/null
+++ b/tests/vr/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 189591
+krzysio@google.com
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/RcParser.java b/tools/release-parser/src/com/android/cts/releaseparser/RcParser.java
index a7ef925..e8c8afe 100644
--- a/tools/release-parser/src/com/android/cts/releaseparser/RcParser.java
+++ b/tools/release-parser/src/com/android/cts/releaseparser/RcParser.java
@@ -66,8 +66,13 @@
         Map<String, Integer> dependencies = new HashMap<>();
         for (Service service : mServices) {
             // skip /, e.g. /system/bin/sh
-            String file = service.getFile().substring(1);
-            dependencies.put(file, 1);
+            try {
+                String file = service.getFile().substring(1);
+                dependencies.put(file, 1);
+            } catch (Exception e) {
+                System.err.println(
+                        "err Service " + service.getName() + " File:" + service.getFile());
+            }
         }
 
         for (String importRc : mImportRc) {
@@ -140,7 +145,7 @@
 
     private void parseService(String line, BufferedReader buffReader) throws IOException {
         Service.Builder serviceBld = Service.newBuilder();
-        String[] phases = line.split(" ");
+        String[] phases = line.split("\\s+");
         serviceBld.setName(phases[1]);
         serviceBld.setFile(phases[2]);
         if (phases.length > 3) {
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/ReleaseParser.java b/tools/release-parser/src/com/android/cts/releaseparser/ReleaseParser.java
index 5d4b9b4..13a01bf 100644
--- a/tools/release-parser/src/com/android/cts/releaseparser/ReleaseParser.java
+++ b/tools/release-parser/src/com/android/cts/releaseparser/ReleaseParser.java
@@ -122,80 +122,84 @@
         List<Entry> entryList = new ArrayList<Entry>();
 
         // walks through all files
-        for (File file : fileList) {
-            if (file.isFile()) {
-                String fileRelativePath =
-                        mRootPath.relativize(Paths.get(file.getAbsolutePath())).toString();
-                FileParser fParser = FileParser.getParser(file);
-                Entry.Builder fileEntryBuilder = fParser.getFileEntryBuilder();
-                fileEntryBuilder.setRelativePath(fileRelativePath);
+        System.out.println("Parsing: " + folderRelativePath);
+        // skip if it's a symbolic link to a folder
+        if (fileList != null) {
+            for (File file : fileList) {
+                if (file.isFile()) {
+                    String fileRelativePath =
+                            mRootPath.relativize(Paths.get(file.getAbsolutePath())).toString();
+                    FileParser fParser = FileParser.getParser(file);
+                    Entry.Builder fileEntryBuilder = fParser.getFileEntryBuilder();
+                    fileEntryBuilder.setRelativePath(fileRelativePath);
 
-                if (folderRelativePath.isEmpty()) {
-                    fileEntryBuilder.setParentFolder(ROOT_FOLDER_TAG);
-                } else {
-                    fileEntryBuilder.setParentFolder(folderRelativePath);
-                }
+                    if (folderRelativePath.isEmpty()) {
+                        fileEntryBuilder.setParentFolder(ROOT_FOLDER_TAG);
+                    } else {
+                        fileEntryBuilder.setParentFolder(folderRelativePath);
+                    }
 
-                Entry.EntryType eType = fParser.getType();
-                switch (eType) {
-                    case TEST_SUITE_TRADEFED:
-                        mRelContentBuilder.setTestSuiteTradefed(fileRelativePath);
-                        TestSuiteTradefedParser tstParser = (TestSuiteTradefedParser) fParser;
-                        // get [cts]-known-failures.xml
-                        mRelContentBuilder.addAllKnownFailures(tstParser.getKnownFailureList());
-                        mRelContentBuilder.setName(tstParser.getName());
-                        mRelContentBuilder.setFullname(tstParser.getFullName());
-                        mRelContentBuilder.setBuildNumber(tstParser.getBuildNumber());
-                        mRelContentBuilder.setTargetArch(tstParser.getTargetArch());
-                        mRelContentBuilder.setVersion(tstParser.getVersion());
-                        mRelContentBuilder.setReleaseType(ReleaseType.TEST_SUITE);
-                        break;
-                    case BUILD_PROP:
-                        BuildPropParser bpParser = (BuildPropParser) fParser;
-                        try {
-                            mRelContentBuilder.setReleaseType(ReleaseType.DEVICE_BUILD);
-                            mRelContentBuilder.setName(bpParser.getName());
-                            mRelContentBuilder.setFullname(bpParser.getFullName());
-                            mRelContentBuilder.setBuildNumber(bpParser.getBuildNumber());
-                            mRelContentBuilder.setVersion(bpParser.getVersion());
-                            mRelContentBuilder.putAllProperties(bpParser.getProperties());
-                        } catch (Exception e) {
-                            System.err.println(
-                                    "No product name, version & etc. in "
-                                            + file.getAbsoluteFile()
-                                            + ", err:"
-                                            + e.getMessage());
-                        }
-                        break;
-                    default:
-                }
-                // System.err.println("File:" + file.getAbsoluteFile());
-                if (fParser.getDependencies() != null) {
-                    fileEntryBuilder.addAllDependencies(fParser.getDependencies());
-                }
-                if (fParser.getDynamicLoadingDependencies() != null) {
-                    fileEntryBuilder.addAllDynamicLoadingDependencies(
-                            fParser.getDynamicLoadingDependencies());
-                }
-                fileEntryBuilder.setAbiBits(fParser.getAbiBits());
-                fileEntryBuilder.setAbiArchitecture(fParser.getAbiArchitecture());
+                    Entry.EntryType eType = fParser.getType();
+                    switch (eType) {
+                        case TEST_SUITE_TRADEFED:
+                            mRelContentBuilder.setTestSuiteTradefed(fileRelativePath);
+                            TestSuiteTradefedParser tstParser = (TestSuiteTradefedParser) fParser;
+                            // get [cts]-known-failures.xml
+                            mRelContentBuilder.addAllKnownFailures(tstParser.getKnownFailureList());
+                            mRelContentBuilder.setName(tstParser.getName());
+                            mRelContentBuilder.setFullname(tstParser.getFullName());
+                            mRelContentBuilder.setBuildNumber(tstParser.getBuildNumber());
+                            mRelContentBuilder.setTargetArch(tstParser.getTargetArch());
+                            mRelContentBuilder.setVersion(tstParser.getVersion());
+                            mRelContentBuilder.setReleaseType(ReleaseType.TEST_SUITE);
+                            break;
+                        case BUILD_PROP:
+                            BuildPropParser bpParser = (BuildPropParser) fParser;
+                            try {
+                                mRelContentBuilder.setReleaseType(ReleaseType.DEVICE_BUILD);
+                                mRelContentBuilder.setName(bpParser.getName());
+                                mRelContentBuilder.setFullname(bpParser.getFullName());
+                                mRelContentBuilder.setBuildNumber(bpParser.getBuildNumber());
+                                mRelContentBuilder.setVersion(bpParser.getVersion());
+                                mRelContentBuilder.putAllProperties(bpParser.getProperties());
+                            } catch (Exception e) {
+                                System.err.println(
+                                        "No product name, version & etc. in "
+                                                + file.getAbsoluteFile()
+                                                + ", err:"
+                                                + e.getMessage());
+                            }
+                            break;
+                        default:
+                    }
+                    // System.err.println("File:" + file.getAbsoluteFile());
+                    if (fParser.getDependencies() != null) {
+                        fileEntryBuilder.addAllDependencies(fParser.getDependencies());
+                    }
+                    if (fParser.getDynamicLoadingDependencies() != null) {
+                        fileEntryBuilder.addAllDynamicLoadingDependencies(
+                                fParser.getDynamicLoadingDependencies());
+                    }
+                    fileEntryBuilder.setAbiBits(fParser.getAbiBits());
+                    fileEntryBuilder.setAbiArchitecture(fParser.getAbiArchitecture());
 
-                Entry fEntry = fileEntryBuilder.build();
-                entryList.add(fEntry);
-                mEntries.put(fEntry.getRelativePath(), fEntry);
-                folderSize += file.length();
-            } else if (file.isDirectory()) {
-                // Checks subfolders
-                Entry.Builder subFolderEntry = parseFolder(file.getAbsolutePath());
-                if (folderRelativePath.isEmpty()) {
-                    subFolderEntry.setParentFolder(ROOT_FOLDER_TAG);
-                } else {
-                    subFolderEntry.setParentFolder(folderRelativePath);
+                    Entry fEntry = fileEntryBuilder.build();
+                    entryList.add(fEntry);
+                    mEntries.put(fEntry.getRelativePath(), fEntry);
+                    folderSize += file.length();
+                } else if (file.isDirectory()) {
+                    // Checks subfolders
+                    Entry.Builder subFolderEntry = parseFolder(file.getAbsolutePath());
+                    if (folderRelativePath.isEmpty()) {
+                        subFolderEntry.setParentFolder(ROOT_FOLDER_TAG);
+                    } else {
+                        subFolderEntry.setParentFolder(folderRelativePath);
+                    }
+                    Entry sfEntry = subFolderEntry.build();
+                    entryList.add(sfEntry);
+                    mEntries.put(sfEntry.getRelativePath(), sfEntry);
+                    folderSize += sfEntry.getSize();
                 }
-                Entry sfEntry = subFolderEntry.build();
-                entryList.add(sfEntry);
-                mEntries.put(sfEntry.getRelativePath(), sfEntry);
-                folderSize += sfEntry.getSize();
             }
         }
         folderEntry.setName(folderRelativePath);
diff --git a/tools/vm-tests-tf/OWNERS b/tools/vm-tests-tf/OWNERS
new file mode 100644
index 0000000..29fea99
--- /dev/null
+++ b/tools/vm-tests-tf/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 86431
+include /hostsidetests/classloaders/OWNERS