Merge "Delta should be greater than reserved size when check storage" into pie-cts-dev am: 760aa0a75f am: eea22c9a41 am: 96ef9f737a am: a670426615 am: 61e9524b7b
am: 3852bb41a3

Change-Id: Ic1d870da5490289e2b88643f15a73b3f6cd148ed
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..bc1d3a6 100644
--- a/apps/CameraITS/pymodules/its/device.py
+++ b/apps/CameraITS/pymodules/its/device.py
@@ -27,6 +27,7 @@
 
 from collections import namedtuple
 
+
 class ItsSession(object):
     """Controls a device over adb to run ITS scripts.
 
@@ -320,6 +321,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.
 
@@ -1091,7 +1109,7 @@
     return device_bfp
 
 def parse_camera_ids(ids):
-    """ Parse the string of camera IDs into array of CameraIdCombo tuples.
+    """Parse the string of camera IDs into array of CameraIdCombo tuples.
     """
     CameraIdCombo = namedtuple('CameraIdCombo', ['id', 'sub_id'])
     id_combos = []
@@ -1105,6 +1123,35 @@
             assert(False), 'Camera id parameters must be either ID, or ID:SUB_ID'
     return id_combos
 
+
+def get_build_sdk_version(device_id=None):
+    """Get the build version of the device."""
+    if not device_id:
+        device_id = get_device_id()
+    cmd = 'adb -s %s shell getprop ro.build.version.sdk' % device_id
+    try:
+        build_sdk_version = int(subprocess.check_output(cmd.split()).rstrip())
+        print 'Build SDK version: %d' % build_sdk_version
+    except (subprocess.CalledProcessError, ValueError):
+        print 'No build_sdk_version.'
+        assert 0
+    return build_sdk_version
+
+
+def get_first_api_level(device_id=None):
+    """Get the first API level for device."""
+    if not device_id:
+        device_id = get_device_id()
+    cmd = 'adb -s %s shell getprop ro.product.first_api_level' % device_id
+    try:
+        first_api_level = int(subprocess.check_output(cmd.split()).rstrip())
+        print 'First API level: %d' % first_api_level
+    except (subprocess.CalledProcessError, ValueError):
+        print 'No first_api_level. Setting to build version.'
+        first_api_level = get_build_sdk_version(device_id)
+    return first_api_level
+
+
 def _run(cmd):
     """Replacement for os.system, with hiding of stdout+stderr messages.
     """
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..c5c8077 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
 
@@ -42,6 +40,7 @@
 CHART_SCALE_STOP = 1.35
 CHART_SCALE_STEP = 0.025
 FACING_EXTERNAL = 2
+NOT_YET_MANDATED_ALL = 100
 NUM_TRYS = 2
 PROC_TIMEOUT_CODE = -101  # terminated process return -process_id
 PROC_TIMEOUT_TIME = 900  # timeout in seconds for a process (15 minutes)
@@ -51,21 +50,63 @@
 VGA_HEIGHT = 480
 VGA_WIDTH = 640
 
-# Not yet mandated tests
+# 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 ['test', first_api_level mandatory]
+# ie. ['test_test_patterns', 28] is MANDATED for first_api_level >= 28
 NOT_YET_MANDATED = {
         'scene0': [
-                'test_test_patterns',
-                'test_tonemap_curve'
+                ['test_test_patterns', 28],
+                ['test_tonemap_curve', 28]
         ],
-        'scene1': [
-                'test_ae_precapture_trigger',
-                'test_channel_saturation'
+        'scene1_1': [
+                ['test_ae_precapture_trigger', 28],
+                ['test_channel_saturation', 29]
         ],
-        'scene2': [
-                'test_auto_per_frame_control'
+        'scene1_2': [],
+        'scene2_a': [
+                ['test_auto_per_frame_control', NOT_YET_MANDATED_ALL]
         ],
-        'scene2b': [],
-        'scene2c': [],
+        'scene2_b': [],
+        'scene2_c': [],
         'scene3': [],
         'scene4': [],
         'scene5': [],
@@ -80,19 +121,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 +146,67 @@
         ]
 }
 
+# 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 determine_not_yet_mandated_tests(device_id):
+    """Determine from NEW_YET_MANDATED & phone info not_yet_mandated tests.
+
+    Args:
+        device_id:      string of device id number
+
+    Returns:
+        dict of not yet mandated tests
+    """
+    # initialize not_yet_mandated
+    not_yet_mandated = {}
+    for scene in ALL_SCENES:
+        not_yet_mandated[scene] = []
+
+    # Determine first API level for device
+    first_api_level = its.device.get_first_api_level(device_id)
+
+    # Determine which scenes are not yet mandated for first api level
+    for scene, tests in NOT_YET_MANDATED.items():
+        for test in tests:
+            if test[1] >= first_api_level:
+                not_yet_mandated[scene].append(test[0])
+    return not_yet_mandated
+
+
+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 +295,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 +353,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 +363,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 +383,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 +479,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
@@ -414,11 +498,15 @@
 
         tot_tests = []
         tot_pass = 0
+        not_yet_mandated = determine_not_yet_mandated_tests(device_id)
         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 +516,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 +539,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'),
@@ -536,7 +624,7 @@
                 elif test_code == SKIP_RET_CODE:
                     retstr = "SKIP "
                     numskip += 1
-                elif test_code != 0 and testname in NOT_YET_MANDATED[scene]:
+                elif test_code != 0 and testname in not_yet_mandated[scene]:
                     retstr = "FAIL*"
                     num_not_mandated_fail += 1
                 else:
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 238d522..d752ab6 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.automotive: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/proguard.flags b/apps/CtsVerifier/proguard.flags
index 85f378e..a05d149 100644
--- a/apps/CtsVerifier/proguard.flags
+++ b/apps/CtsVerifier/proguard.flags
@@ -19,7 +19,7 @@
 }
 
 # ensure we keep public camera test methods, these are needed at runtime
--keepclassmembers class * extends android.hardware.camera2.cts.testcases.Camera2AndroidTestCase {
+-keepclassmembers class * extends android.hardware.camera2.cts.testcases.Camera2AndroidBasicTestCase {
     public <methods>;
 }
 
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 9a93fbf..2be358b 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/performance/CameraPerformanceActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/performance/CameraPerformanceActivity.java
index 5510521..59b7a03 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/performance/CameraPerformanceActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/performance/CameraPerformanceActivity.java
@@ -21,7 +21,7 @@
 import android.app.ProgressDialog;
 import android.content.Context;
 import android.hardware.camera2.cts.PerformanceTest;
-import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
+import android.hardware.camera2.cts.testcases.Camera2AndroidBasicTestCase;
 import android.hardware.cts.CameraTestCase;
 import android.hardware.cts.LegacyCameraPerformanceTest;
 import android.os.Bundle;
@@ -201,8 +201,8 @@
                 Enumeration<Test> tests = ((TestSuite) s).tests();
                 while (tests.hasMoreElements()) {
                     Test test = tests.nextElement();
-                    if (test instanceof Camera2AndroidTestCase) {
-                        Camera2AndroidTestCase testCase = (Camera2AndroidTestCase) test;
+                    if (test instanceof Camera2AndroidBasicTestCase) {
+                        Camera2AndroidBasicTestCase testCase = (Camera2AndroidBasicTestCase) test;
 
                         // The base case class has one internal test that can
                         // be ignored for the purpose of this test activity.
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..b628bce 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
@@ -54,7 +54,12 @@
     public static final int S_IXOTH = 00001;
 
     static {
-        System.loadLibrary("cts_jni");
+        try {
+            // Required only for the native methods.
+            System.loadLibrary("cts_jni");
+        } catch (UnsatisfiedLinkError e) {
+            System.out.println("JNI not loaded");
+        }
     }
 
     public static class FileStatus {
@@ -93,13 +98,18 @@
      * @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
+     *
+     * If you call this method, make sure to link in the libcts_jni library.
      */
     public native static boolean getFileStatus(String path, FileStatus status, boolean statLinks);
 
+    /** If you call this method, make sure to link in the libcts_jni library. */
     public native static String getUserName(int uid);
 
+    /** If you call this method, make sure to link in the libcts_jni library. */
     public native static String getGroupName(int gid);
 
+    /** If you call this method, make sure to link in the libcts_jni library. */
     public native static int setPermissions(String file, int mode);
 
     /**
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/angle/app/common/src/com/android/angleIntegrationTest/common/AngleIntegrationTestActivity.java b/hostsidetests/angle/app/common/src/com/android/angleIntegrationTest/common/AngleIntegrationTestActivity.java
deleted file mode 100644
index e167176..0000000
--- a/hostsidetests/angle/app/common/src/com/android/angleIntegrationTest/common/AngleIntegrationTestActivity.java
+++ /dev/null
@@ -1,44 +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.angleIntegrationTest.common;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.util.Log;
-
-import java.lang.Override;
-
-public class AngleIntegrationTestActivity extends Activity {
-
-    private final String TAG = this.getClass().getSimpleName();
-
-    private GlesView mGlesView;
-
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        mGlesView = new GlesView(this);
-        setContentView(mGlesView);
-
-        Log.i(TAG, "ANGLE Manifest activity complete");
-    }
-
-    public GlesView getGlesView() {
-        return mGlesView;
-    }
-}
diff --git a/hostsidetests/angle/app/common/src/com/android/angleIntegrationTest/common/GlesView.java b/hostsidetests/angle/app/common/src/com/android/angleIntegrationTest/common/GlesView.java
index d0fbe58..f1c053f 100644
--- a/hostsidetests/angle/app/common/src/com/android/angleIntegrationTest/common/GlesView.java
+++ b/hostsidetests/angle/app/common/src/com/android/angleIntegrationTest/common/GlesView.java
@@ -19,14 +19,10 @@
 import static javax.microedition.khronos.egl.EGL10.EGL_STENCIL_SIZE;
 
 import android.annotation.TargetApi;
-import android.content.Context;
 import android.opengl.EGL14;
 import android.opengl.GLES20;
-import android.opengl.GLSurfaceView;
 import android.os.Build.VERSION_CODES;
 import android.util.Log;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
 import javax.microedition.khronos.egl.EGL10;
 import javax.microedition.khronos.egl.EGLConfig;
 import javax.microedition.khronos.egl.EGLContext;
@@ -38,28 +34,21 @@
  */
 
 @TargetApi(VERSION_CODES.GINGERBREAD)
-public class GlesView extends SurfaceView implements SurfaceHolder.Callback2 {
+public class GlesView {
 
     private static final EGL10 EGL = (EGL10) EGLContext.getEGL();
-    private EGLConfig eglConfig;
-    private EGLDisplay display;
-    private SurfaceHolder mSurfaceHolder;
     private String mRenderer = "";
 
     private final String TAG = this.getClass().getSimpleName();
 
-    public GlesView(Context context) {
-      super(context);
-      this.setWillNotDraw(false);
-      getHolder().addCallback(this);
-      createEGL();
-      getHolder().getSurface();
+    public GlesView() {
+        createEGL();
     }
 
     @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
     private void createEGL() {
         int[] version = new int[2];
-        display = EGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+        EGLDisplay display = EGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
         EGL.eglInitialize(display, version);
 
         int[] numConfigs = new int[1];
@@ -68,7 +57,7 @@
         EGL.eglGetConfigs(display, allConfigs, numConfigs[0], numConfigs);
 
         int[] configAttrib =
-            new int[] {
+            new int[]{
                 EGL10.EGL_RENDERABLE_TYPE,
                 EGL14.EGL_OPENGL_ES2_BIT,
                 EGL10.EGL_SURFACE_TYPE,
@@ -90,6 +79,7 @@
 
         EGLConfig[] selectedConfig = new EGLConfig[1];
         EGL.eglChooseConfig(display, configAttrib, selectedConfig, 1, numConfigs);
+        EGLConfig eglConfig;
         if (selectedConfig[0] != null) {
             eglConfig = selectedConfig[0];
             Log.i(TAG, "Found matching EGL config");
@@ -97,33 +87,35 @@
             Log.e(TAG, "Could not find matching EGL config");
             throw new RuntimeException("No Matching EGL Config Found");
         }
-    }
 
-    @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
-    @Override
-    public void surfaceCreated(SurfaceHolder holder) {
-      EGLSurface surface = EGL.eglCreateWindowSurface(display, eglConfig, this, null);
-        int[] contextAttribs = new int[] {EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE};
-      EGLContext context = EGL
-          .eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, contextAttribs);
+        EGLSurface surface = EGL.eglCreatePbufferSurface(display, eglConfig, null);
+        int[] contextAttribs = new int[]{EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE};
+        EGLContext context = EGL
+            .eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, contextAttribs);
         EGL.eglMakeCurrent(display, surface, surface, context);
 
         Log.i(TAG, "CTS ANGLE Test :: GLES GL_VENDOR   : " + GLES20.glGetString(GLES20.GL_VENDOR));
         Log.i(TAG, "CTS ANGLE Test :: GLES GL_VERSION  : " + GLES20.glGetString(GLES20.GL_VERSION));
-        Log.i(TAG, "CTS ANGLE Test :: GLES GL_RENDERER : " + GLES20.glGetString(GLES20.GL_RENDERER));
+        Log.i(TAG,
+            "CTS ANGLE Test :: GLES GL_RENDERER : " + GLES20.glGetString(GLES20.GL_RENDERER));
         mRenderer = GLES20.glGetString(GLES20.GL_RENDERER);
     }
 
-    @Override
-    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
-
-    @Override
-    public void surfaceDestroyed(SurfaceHolder holder) {}
-
-    @Override
-    public void surfaceRedrawNeeded(SurfaceHolder holder) {}
-
     public String getRenderer() {
         return mRenderer;
     }
+
+    public boolean validateDeveloperOption(boolean angleEnabled)  {
+        if (angleEnabled) {
+            if (!mRenderer.toLowerCase().contains("angle")) {
+                return false;
+            }
+        } else {
+            if (mRenderer.toLowerCase().contains("angle")) {
+                return false;
+            }
+        }
+
+        return true;
+    }
 }
diff --git a/hostsidetests/angle/app/driverTest/src/com/android/angleIntegrationTest/driverTest/AngleDriverTestActivity.java b/hostsidetests/angle/app/driverTest/src/com/android/angleIntegrationTest/driverTest/AngleDriverTestActivity.java
index bb98488..450461a 100644
--- a/hostsidetests/angle/app/driverTest/src/com/android/angleIntegrationTest/driverTest/AngleDriverTestActivity.java
+++ b/hostsidetests/angle/app/driverTest/src/com/android/angleIntegrationTest/driverTest/AngleDriverTestActivity.java
@@ -18,13 +18,10 @@
 
 import static org.junit.Assert.fail;
 
-import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.angleIntegrationTest.common.AngleIntegrationTestActivity;
 import com.android.angleIntegrationTest.common.GlesView;
 
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -33,29 +30,18 @@
 
     private final String TAG = this.getClass().getSimpleName();
 
-    @Rule
-    public ActivityTestRule<AngleIntegrationTestActivity> rule =
-            new ActivityTestRule<>(AngleIntegrationTestActivity.class);
-
     private void validateDeveloperOption(boolean angleEnabled) throws Exception {
-        AngleIntegrationTestActivity activity = rule.getActivity();
-        GlesView glesView = activity.getGlesView();
-        String renderer = glesView.getRenderer();
+        GlesView glesView = new GlesView();
 
-        while(renderer.length() == 0) {
-            renderer = glesView.getRenderer();
-        }
-
-        if (angleEnabled) {
-            if (!renderer.toLowerCase().contains("ANGLE".toLowerCase())) {
+        if (!glesView.validateDeveloperOption(angleEnabled)) {
+            if (angleEnabled) {
+                String renderer = glesView.getRenderer();
                 fail("Failure - ANGLE was not loaded: '" + renderer + "'");
-            }
-        } else {
-            if (renderer.toLowerCase().contains("ANGLE".toLowerCase())) {
+            } else {
+                String renderer = glesView.getRenderer();
                 fail("Failure - ANGLE was loaded: '" + renderer + "'");
             }
         }
-
     }
 
     @Test
diff --git a/hostsidetests/angle/app/driverTestSecondary/src/com/android/angleIntegrationTest/driverTestSecondary/AngleDriverTestActivity.java b/hostsidetests/angle/app/driverTestSecondary/src/com/android/angleIntegrationTest/driverTestSecondary/AngleDriverTestActivity.java
index ce7e5e8..7b25c39 100644
--- a/hostsidetests/angle/app/driverTestSecondary/src/com/android/angleIntegrationTest/driverTestSecondary/AngleDriverTestActivity.java
+++ b/hostsidetests/angle/app/driverTestSecondary/src/com/android/angleIntegrationTest/driverTestSecondary/AngleDriverTestActivity.java
@@ -18,13 +18,10 @@
 
 import static org.junit.Assert.fail;
 
-import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.angleIntegrationTest.common.AngleIntegrationTestActivity;
 import com.android.angleIntegrationTest.common.GlesView;
 
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -33,25 +30,18 @@
 
     private final String TAG = this.getClass().getSimpleName();
 
-    @Rule
-    public ActivityTestRule<AngleIntegrationTestActivity> rule =
-            new ActivityTestRule<>(AngleIntegrationTestActivity.class);
-
     private void validateDeveloperOption(boolean angleEnabled) throws Exception {
-        AngleIntegrationTestActivity activity = rule.getActivity();
-        GlesView glesView = activity.getGlesView();
-        String renderer = glesView.getRenderer();
+        GlesView glesView = new GlesView();
 
-        if (angleEnabled) {
-            if (!renderer.toLowerCase().contains("ANGLE".toLowerCase())) {
+        if (!glesView.validateDeveloperOption(angleEnabled)) {
+            if (angleEnabled) {
+                String renderer = glesView.getRenderer();
                 fail("Failure - ANGLE was not loaded: '" + renderer + "'");
-            }
-        } else {
-            if (renderer.toLowerCase().contains("ANGLE".toLowerCase())) {
+            } else {
+                String renderer = glesView.getRenderer();
                 fail("Failure - ANGLE was loaded: '" + renderer + "'");
             }
         }
-
     }
 
     @Test
diff --git a/hostsidetests/angle/src/android/angle/cts/CtsAngleCommon.java b/hostsidetests/angle/src/android/angle/cts/CtsAngleCommon.java
index 85841af..c576253 100644
--- a/hostsidetests/angle/src/android/angle/cts/CtsAngleCommon.java
+++ b/hostsidetests/angle/src/android/angle/cts/CtsAngleCommon.java
@@ -24,6 +24,11 @@
 class CtsAngleCommon {
     private static final int TEST_WAIT_TIME_MS = 1000;
 
+    // General
+    static final int NUM_ATTEMPTS = 5;
+    static final int APPLY_SLEEP_MSEC = 500;
+    static final int REATTEMPT_SLEEP_MSEC = 5000;
+
     // Settings.Global
     static final String SETTINGS_GLOBAL_ALL_USE_ANGLE = "angle_gl_driver_all_angle";
     static final String SETTINGS_GLOBAL_DRIVER_PKGS = "angle_gl_driver_selection_pkgs";
diff --git a/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java b/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java
index 5373a3b..5cff73c 100644
--- a/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java
+++ b/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java
@@ -45,12 +45,38 @@
         setGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_PKGS, pkgName);
         setGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_VALUES, driverValue);
 
+        // SETTINGS_GLOBAL_DRIVER_PKGS
+        for (int i = 0; i < NUM_ATTEMPTS; i++)
+        {
+            setGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_PKGS, pkgName);
+            Thread.sleep(APPLY_SLEEP_MSEC);
+            String devOption = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_PKGS);
+            if (devOption.equals(pkgName))
+            {
+                break;
+            }
+            Thread.sleep(REATTEMPT_SLEEP_MSEC);
+        }
+
         String devOption = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_PKGS);
         Assert.assertEquals(
                 "Developer option '" + SETTINGS_GLOBAL_DRIVER_PKGS +
                         "' was not set correctly: '" + devOption + "'",
                 pkgName, devOption);
 
+        // SETTINGS_GLOBAL_DRIVER_VALUES
+        for (int i = 0; i < NUM_ATTEMPTS; i++)
+        {
+            setGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_VALUES, driverValue);
+            Thread.sleep(APPLY_SLEEP_MSEC);
+            devOption = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_VALUES);
+            if (devOption.equals(driverValue))
+            {
+                break;
+            }
+            Thread.sleep(REATTEMPT_SLEEP_MSEC);
+        }
+
         devOption = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_VALUES);
         Assert.assertEquals(
                 "Developer option '" + SETTINGS_GLOBAL_DRIVER_VALUES +
@@ -72,6 +98,18 @@
                 sDriverTestMethodMap.get(driver));
     }
 
+    private void installApp(String appName) throws Exception {
+        for (int i = 0; i < NUM_ATTEMPTS; i++)
+        {
+            try {
+                installPackage(appName);
+                return;
+            } catch(Exception e) {
+                Thread.sleep(REATTEMPT_SLEEP_MSEC);
+            }
+        }
+    }
+
     @Before
     public void setUp() throws Exception {
         clearSettings(getDevice());
@@ -92,8 +130,8 @@
     public void testEnableAngleForAll() throws Exception {
         Assume.assumeTrue(isAngleLoadable(getDevice()));
 
-        installPackage(ANGLE_DRIVER_TEST_APP);
-        installPackage(ANGLE_DRIVER_TEST_SEC_APP);
+        installApp(ANGLE_DRIVER_TEST_APP);
+        installApp(ANGLE_DRIVER_TEST_SEC_APP);
 
         setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG,
                 sDriverGlobalSettingMap.get(OpenGlDriverChoice.DEFAULT));
@@ -117,7 +155,7 @@
     public void testUseDefaultDriver() throws Exception {
         Assume.assumeTrue(isAngleLoadable(getDevice()));
 
-        installPackage(ANGLE_DRIVER_TEST_APP);
+        installApp(ANGLE_DRIVER_TEST_APP);
 
         setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG,
                 sDriverGlobalSettingMap.get(OpenGlDriverChoice.DEFAULT));
@@ -134,7 +172,7 @@
     public void testUseAngleDriver() throws Exception {
         Assume.assumeTrue(isAngleLoadable(getDevice()));
 
-        installPackage(ANGLE_DRIVER_TEST_APP);
+        installApp(ANGLE_DRIVER_TEST_APP);
 
         setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG,
                 sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE));
@@ -151,7 +189,7 @@
     public void testUseNativeDriver() throws Exception {
         Assume.assumeTrue(isAngleLoadable(getDevice()));
 
-        installPackage(ANGLE_DRIVER_TEST_APP);
+        installApp(ANGLE_DRIVER_TEST_APP);
 
         setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG,
                 sDriverGlobalSettingMap.get(OpenGlDriverChoice.NATIVE));
@@ -168,8 +206,8 @@
     public void testSettingsLengthMismatch() throws Exception {
         Assume.assumeTrue(isAngleLoadable(getDevice()));
 
-        installPackage(ANGLE_DRIVER_TEST_APP);
-        installPackage(ANGLE_DRIVER_TEST_SEC_APP);
+        installApp(ANGLE_DRIVER_TEST_APP);
+        installApp(ANGLE_DRIVER_TEST_SEC_APP);
 
         setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG + "," +
                         ANGLE_DRIVER_TEST_SEC_PKG,
@@ -191,7 +229,7 @@
     public void testUseInvalidDriver() throws Exception {
         Assume.assumeTrue(isAngleLoadable(getDevice()));
 
-        installPackage(ANGLE_DRIVER_TEST_APP);
+        installApp(ANGLE_DRIVER_TEST_APP);
 
         setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG, "timtim");
 
@@ -207,7 +245,7 @@
     public void testUpdateDriverValues() throws Exception {
         Assume.assumeTrue(isAngleLoadable(getDevice()));
 
-        installPackage(ANGLE_DRIVER_TEST_APP);
+        installApp(ANGLE_DRIVER_TEST_APP);
 
         for (OpenGlDriverChoice firstDriver : OpenGlDriverChoice.values()) {
             for (OpenGlDriverChoice secondDriver : OpenGlDriverChoice.values()) {
@@ -229,8 +267,8 @@
     public void testMultipleDevOptionsAngleNative() throws Exception {
         Assume.assumeTrue(isAngleLoadable(getDevice()));
 
-        installPackage(ANGLE_DRIVER_TEST_APP);
-        installPackage(ANGLE_DRIVER_TEST_SEC_APP);
+        installApp(ANGLE_DRIVER_TEST_APP);
+        installApp(ANGLE_DRIVER_TEST_SEC_APP);
 
         setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG + "," +
                         ANGLE_DRIVER_TEST_SEC_PKG,
@@ -253,8 +291,8 @@
     public void testMultipleUpdateDriverValues() throws Exception {
         Assume.assumeTrue(isAngleLoadable(getDevice()));
 
-        installPackage(ANGLE_DRIVER_TEST_APP);
-        installPackage(ANGLE_DRIVER_TEST_SEC_APP);
+        installApp(ANGLE_DRIVER_TEST_APP);
+        installApp(ANGLE_DRIVER_TEST_SEC_APP);
 
         // Set the first PKG to always use ANGLE
         setAndValidatePkgDriver(ANGLE_DRIVER_TEST_PKG, OpenGlDriverChoice.ANGLE);
@@ -316,7 +354,7 @@
         Assume.assumeTrue(isAngleLoadable(getDevice()));
 
         // Install the package so the setting isn't removed because the package isn't present.
-        installPackage(ANGLE_DRIVER_TEST_APP);
+        installApp(ANGLE_DRIVER_TEST_APP);
 
         setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG,
                 sDriverGlobalSettingMap.get(OpenGlDriverChoice.DEFAULT));
@@ -376,8 +414,8 @@
     public void testMultipleDevOptionsAngleDefault() throws Exception {
         Assume.assumeTrue(isAngleLoadable(getDevice()));
 
-        installPackage(ANGLE_DRIVER_TEST_APP);
-        installPackage(ANGLE_DRIVER_TEST_SEC_APP);
+        installApp(ANGLE_DRIVER_TEST_APP);
+        installApp(ANGLE_DRIVER_TEST_SEC_APP);
 
         setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG + "," + ANGLE_DRIVER_TEST_SEC_PKG,
                 sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE) + "," +
@@ -408,7 +446,7 @@
     public void testMultipleDevOptionsAngleNativeUninstall() throws Exception {
         Assume.assumeTrue(isAngleLoadable(getDevice()));
 
-        installPackage(ANGLE_DRIVER_TEST_SEC_APP);
+        installApp(ANGLE_DRIVER_TEST_SEC_APP);
 
         setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG + "," + ANGLE_DRIVER_TEST_SEC_PKG,
                 sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE) + "," +
diff --git a/hostsidetests/angle/src/android/angle/cts/CtsAngleRulesFileTest.java b/hostsidetests/angle/src/android/angle/cts/CtsAngleRulesFileTest.java
index 596630b..50f55b0 100644
--- a/hostsidetests/angle/src/android/angle/cts/CtsAngleRulesFileTest.java
+++ b/hostsidetests/angle/src/android/angle/cts/CtsAngleRulesFileTest.java
@@ -61,22 +61,42 @@
         setProperty(getDevice(), PROPERTY_TEMP_RULES_FILE, DEVICE_TEMP_RULES_FILE_PATH);
     }
 
+    private void setAndValidateAngleDevOptionWhitelist(String whiteList) throws Exception {
+        // SETTINGS_GLOBAL_WHITELIST
+        for (int i = 0; i < NUM_ATTEMPTS; i++)
+        {
+            setGlobalSetting(getDevice(), SETTINGS_GLOBAL_WHITELIST, whiteList);
+            Thread.sleep(APPLY_SLEEP_MSEC);
+            String devOption = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_WHITELIST);
+            if (devOption.equals(whiteList))
+            {
+                break;
+            }
+            Thread.sleep(REATTEMPT_SLEEP_MSEC);
+        }
+
+        String devOption = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_WHITELIST);
+        Assert.assertEquals(
+            "Developer option '" + SETTINGS_GLOBAL_WHITELIST +
+                "' was not set correctly: '" + devOption + "'",
+            whiteList, devOption);
+    }
+
     @Before
     public void setUp() throws Exception {
         clearSettings(getDevice());
 
         mWhiteList = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_WHITELIST);
 
-        // Application must be whitelisted to load temp rules
-        setGlobalSetting(getDevice(), SETTINGS_GLOBAL_WHITELIST,
-                ANGLE_DRIVER_TEST_PKG + "," + ANGLE_DRIVER_TEST_SEC_PKG);
+        final String whitelist = ANGLE_DRIVER_TEST_PKG + "," + ANGLE_DRIVER_TEST_SEC_PKG;
+        setAndValidateAngleDevOptionWhitelist(whitelist);
     }
 
     @After
     public void tearDown() throws Exception {
         clearSettings(getDevice());
 
-        setGlobalSetting(getDevice(), SETTINGS_GLOBAL_WHITELIST, mWhiteList);
+        setAndValidateAngleDevOptionWhitelist(mWhiteList);
 
         FileUtil.deleteFile(mRulesFile);
     }
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/OverlayHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/OverlayHostTest.java
index 20978c6..fbf03be 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/OverlayHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/OverlayHostTest.java
@@ -146,7 +146,7 @@
             getDevice().executeShellCommand("cmd overlay enable " + overlayPackage);
             waitForOverlayState(overlayPackage, STATE_ENABLED);
 
-            runDeviceTests(TEST_APP_PACKAGE, TEST_APP_CLASS, testMethod);
+            runDeviceTests(TEST_APP_PACKAGE, TEST_APP_CLASS, testMethod, false /* instant */);
         } finally {
             getDevice().uninstallPackage(TARGET_PACKAGE);
             getDevice().uninstallPackage(overlayPackage);
@@ -291,7 +291,7 @@
     @Test
     public void testFrameworkDoesNotDefineOverlayable() throws Exception {
         String testMethod = "testFrameworkDoesNotDefineOverlayable";
-        runDeviceTests(TEST_APP_PACKAGE, TEST_APP_CLASS, testMethod);
+        runDeviceTests(TEST_APP_PACKAGE, TEST_APP_CLASS, testMethod, false /* instant */);
     }
 
     /** Overlays must not overlay assets. */
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
index d0cc258..2442638 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,16 @@
     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");
-        }
+        assertThrows(
+                instanceOf(AssertionError.class, hasMessageThat(containsString("Expected"))),
+                () -> runDeviceTests(USES_PERMISSION_PKG,
+                        "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 +155,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 +239,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 +286,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 +305,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 +482,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 ba5fd4b..2ec3002 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))));
     }
@@ -610,12 +598,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/appsecurity/test-apps/rro/OverlayApp/src/com/android/cts/overlay/app/OverlayableTest.java b/hostsidetests/appsecurity/test-apps/rro/OverlayApp/src/com/android/cts/overlay/app/OverlayableTest.java
index d693691..2b8bad6 100644
--- a/hostsidetests/appsecurity/test-apps/rro/OverlayApp/src/com/android/cts/overlay/app/OverlayableTest.java
+++ b/hostsidetests/appsecurity/test-apps/rro/OverlayApp/src/com/android/cts/overlay/app/OverlayableTest.java
@@ -139,7 +139,7 @@
 
     @Test
     public void testFrameworkDoesNotDefineOverlayable() throws Exception {
-        assertTrue(InstrumentationRegistry.getTargetContext().getAssets().getOverlayableMap(
+        assertTrue(InstrumentationRegistry.getTargetContext().getAssets().getOverlayablesToString(
                 "android").isEmpty());
     }
 
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/OWNERS b/hostsidetests/backup/OWNERS
index c28c4d8..e0e5e22 100644
--- a/hostsidetests/backup/OWNERS
+++ b/hostsidetests/backup/OWNERS
@@ -2,6 +2,7 @@
 # Use this reviewer by default.
 br-framework-team+reviews@google.com
 
+alsutton@google.com
 anniemeng@google.com
 brufino@google.com
 nathch@google.com
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..a14f0f0 100644
--- a/hostsidetests/backup/src/android/cts/backup/BackupPreparer.java
+++ b/hostsidetests/backup/src/android/cts/backup/BackupPreparer.java
@@ -16,6 +16,10 @@
 
 package android.cts.backup;
 
+import static org.junit.Assert.fail;
+
+import com.android.compatibility.common.util.BackupHostSideUtils;
+import com.android.compatibility.common.util.BackupUtils;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.config.Option;
 import com.android.tradefed.config.OptionClass;
@@ -27,7 +31,12 @@
 import com.android.tradefed.targetprep.ITargetCleaner;
 import com.android.tradefed.targetprep.TargetSetupError;
 
+import java.io.IOException;
+import java.util.Optional;
+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 +47,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;
@@ -52,20 +62,20 @@
 
     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 final int USER_SYSTEM = 0;
 
     private boolean mIsBackupSupported;
     private boolean mWasBackupEnabled;
+    private Optional<Boolean> mWasBackupActivated = Optional.empty();
     private String mOldTransport;
     private ITestDevice mDevice;
+    private BackupUtils mBackupUtils;
 
     @Override
     public void setUp(ITestDevice device, IBuildInfo buildInfo)
             throws TargetSetupError, BuildError, DeviceNotAvailableException {
         mDevice = device;
-
+        mBackupUtils = BackupHostSideUtils.createBackupUtils(mDevice);
         mIsBackupSupported = mDevice.hasFeature("feature:" + FEATURE_BACKUP);
 
         // In case the device was just rebooted, wait for the broadcast queue to get idle to avoid
@@ -73,11 +83,12 @@
         waitForBroadcastIdle();
 
         if (mIsBackupSupported) {
+            CLog.i("Activating backup on %s", mDevice.getSerialNumber());
+            mWasBackupActivated = Optional.of(setBackupActive(true));
+
             // 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 +98,11 @@
                     mOldTransport = setBackupTransport(LOCAL_TRANSPORT);
                     CLog.d("Old transport : %s", mOldTransport);
                 }
-                waitForBackupInitialization();
+                try {
+                    mBackupUtils.waitForBackupInitialization();
+                } catch (IOException e) {
+                    throw new TargetSetupError("Backup not initialized", e);
+                }
             }
         }
     }
@@ -98,29 +113,73 @@
         mDevice = device;
 
         if (mIsBackupSupported) {
-            if (mEnableBackup) {
-                CLog.i("Returning backup to it's previous state on %s", mDevice.getSerialNumber());
-                enableBackup(mWasBackupEnabled);
-                if (mSelectLocalTransport) {
-                    CLog.i("Returning selected transport to it's previous value on %s",
+            if (mWasBackupActivated.isPresent()) {
+                setBackupActive(mWasBackupActivated.get());
+
+                if (mEnableBackup) {
+                    CLog.i("Returning backup to it's previous state on %s",
                             mDevice.getSerialNumber());
-                    setBackupTransport(mOldTransport);
+                    enableBackup(mWasBackupEnabled);
+                    if (mSelectLocalTransport) {
+                        CLog.i("Returning selected transport to it's previous value on %s",
+                                mDevice.getSerialNumber());
+                        setBackupTransport(mOldTransport);
+                    }
                 }
             }
         }
     }
 
-    // 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 +208,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 +228,25 @@
         }
     }
 
+    private boolean setBackupActive(boolean active) {
+        boolean wasBackupActive;
+        try {
+            wasBackupActive  = mBackupUtils.isBackupActivatedForUser(USER_SYSTEM);
+            mBackupUtils.activateBackupForUser(active, USER_SYSTEM);
+        } catch (IOException e) {
+            throw new RuntimeException("Failed set backup active status");
+        }
+
+        return wasBackupActive;
+    }
+
+    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/checkpoint/Android.mk b/hostsidetests/checkpoint/Android.mk
index 7f044b0..fc66d79 100644
--- a/hostsidetests/checkpoint/Android.mk
+++ b/hostsidetests/checkpoint/Android.mk
@@ -25,9 +25,7 @@
 LOCAL_CTS_TEST_PACKAGE := android.checkpoint
 
 # tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests gts
-
-LOCAL_MIN_SDK_VERSION := 4
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
 include $(BUILD_CTS_HOST_JAVA_LIBRARY)
 
diff --git a/hostsidetests/checkpoint/AndroidTest.xml b/hostsidetests/checkpoint/AndroidTest.xml
index 1b8aba3..54aeaa8 100644
--- a/hostsidetests/checkpoint/AndroidTest.xml
+++ b/hostsidetests/checkpoint/AndroidTest.xml
@@ -15,7 +15,6 @@
 -->
 <configuration description="Config for the CTS Checkpoint host tests">
     <option name="test-suite-tag" value="cts" />
-    <option name="test-suite-tag" value="gts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
diff --git a/hostsidetests/checkpoint/src/android/checkpoint/cts/CheckpointHostTest.java b/hostsidetests/checkpoint/src/android/checkpoint/cts/CheckpointHostTest.java
index ea876d9..3426443 100644
--- a/hostsidetests/checkpoint/src/android/checkpoint/cts/CheckpointHostTest.java
+++ b/hostsidetests/checkpoint/src/android/checkpoint/cts/CheckpointHostTest.java
@@ -16,7 +16,6 @@
 
 package android.checkpoint.cts;
 
-import com.android.compatibility.common.util.CtsDownstreamingTest;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.DeviceTestCase;
 import junit.framework.Assert;
@@ -28,7 +27,6 @@
 public class CheckpointHostTest extends DeviceTestCase {
     private static final String TAG = "CheckpointHostTest";
 
-    @CtsDownstreamingTest
     public void testLogEntries() throws Exception {
         // Clear buffer to make it easier to find new logs
         getDevice().executeShellCommand("logcat --clear");
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..3d86cf3
--- /dev/null
+++ b/hostsidetests/devicepolicy/TEST_MAPPING
@@ -0,0 +1,20 @@
+{
+  "presubmit-devicepolicy": [
+    {
+      "name": "CtsDevicePolicyManagerTestCases",
+      "options": [
+        {
+          "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+        },
+        {
+          "exclude-annotation": "android.platform.test.annotations.LargeTest"
+        }
+      ]
+    }
+  ],
+  "postsubmit-devicepolicy": [
+    {
+      "name": "CtsDevicePolicyManagerTestCases"
+    }
+  ]
+}
diff --git a/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/CertSelectionDelegateTest.java b/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/CertSelectionDelegateTest.java
index 609ea1f..f9313ee 100644
--- a/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/CertSelectionDelegateTest.java
+++ b/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/CertSelectionDelegateTest.java
@@ -18,6 +18,8 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.junit.Assert.assertNull;
+
 import android.app.Activity;
 import android.app.admin.DelegatedAdminReceiver;
 import android.app.admin.DevicePolicyManager;
@@ -114,6 +116,16 @@
         }
     }
 
+    // Tests that if the delegated app returns {@link
+    // android.security.KeyChain.KEY_ALIAS_SELECTION_DENIED}
+    // the caller of {@link android.app.admin.DelegatedAdminReceiver#onChoosePrivateKeyAlias}
+    // receives {@code null}.
+    public void testNotChosenAnyAlias() throws Exception {
+        assertThat(mDpm.getDelegatedScopes(null, mContext.getPackageName())).contains(
+                DevicePolicyManager.DELEGATION_CERT_SELECTION);
+        assertNull(new KeyChainAliasFuture(KeyChain.KEY_ALIAS_SELECTION_DENIED).get());
+    }
+
     private class KeyChainAliasFuture implements KeyChainAliasCallback {
         private final CountDownLatch mLatch = new CountDownLatch(1);
         private String mChosenAlias = null;
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..1fae525 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
@@ -60,10 +60,9 @@
         assertEquals(10, dpm.getPasswordMinimumLength(mAdminComponent));
         assertPasswordSufficiency(true); // length not checked for this quality
 
-        // TODO(ascull): fix resetPassword() logic so these succeed
-        assertPasswordFails("1234", caseDescription);
-        assertPasswordFails("abcd", caseDescription);
-        assertPasswordFails("abcd1234", caseDescription);
+        assertPasswordSucceeds("1234", caseDescription);
+        assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordSucceeds("abcd1234", caseDescription);
 
         dpm.setPasswordMinimumLength(mAdminComponent, 4);
         caseDescription = "minimum password length = 4";
@@ -378,17 +377,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/DevicePolicyLoggingTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DevicePolicyLoggingTest.java
index d6856a9..4e58d92 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DevicePolicyLoggingTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DevicePolicyLoggingTest.java
@@ -50,7 +50,7 @@
 
     public void testPasswordMethodsLogged() {
         mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
-                DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
+                DevicePolicyManager.PASSWORD_QUALITY_COMPLEX);
         mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 13);
         mDevicePolicyManager.setPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT, 14);
         mDevicePolicyManager.setPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT, 15);
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PasswordRequirementsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PasswordRequirementsTest.java
new file mode 100644
index 0000000..f9ce726
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PasswordRequirementsTest.java
@@ -0,0 +1,158 @@
+/*
+ * 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.deviceandprofileowner;
+
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
+import static org.testng.Assert.assertThrows;
+
+/**
+ * Class that tests password constraints API preconditions.
+ */
+public class PasswordRequirementsTest extends BaseDeviceAdminTest {
+    private static final int TEST_VALUE = 5;
+    private static final int DEFAULT_NUMERIC = 1;
+    private static final int DEFAULT_LETTERS = 1;
+    private static final int DEFAULT_UPPERCASE = 0;
+    private static final int DEFAULT_LOWERCASE = 0;
+    private static final int DEFAULT_NON_LETTER = 0;
+    private static final int DEFAULT_SYMBOLS = 1;
+    private static final int DEFAULT_LENGTH = 0;
+
+    public void testPasswordConstraintsDoesntThrowAndPreservesValuesPreR() {
+        // Pre-R password restrictions can be set in any order.
+        mDevicePolicyManager.setPasswordQuality(
+                ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_SOMETHING);
+        // These shouldn't throw.
+        mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+        mDevicePolicyManager.setPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+        mDevicePolicyManager.setPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+        mDevicePolicyManager.setPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+        mDevicePolicyManager.setPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+        mDevicePolicyManager.setPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+        mDevicePolicyManager.setPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+
+        // Make sure these values are preserved and not reset when quality is set low.
+        mDevicePolicyManager.setPasswordQuality(
+                ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_UNSPECIFIED);
+        assertEquals(TEST_VALUE,
+                mDevicePolicyManager.getPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT));
+        assertEquals(TEST_VALUE,
+                mDevicePolicyManager.getPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT));
+        assertEquals(TEST_VALUE,
+                mDevicePolicyManager.getPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT));
+        assertEquals(TEST_VALUE,
+                mDevicePolicyManager.getPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT));
+        assertEquals(TEST_VALUE,
+                mDevicePolicyManager.getPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT));
+        assertEquals(TEST_VALUE,
+                mDevicePolicyManager.getPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT));
+        assertEquals(TEST_VALUE,
+                mDevicePolicyManager.getPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT));
+    }
+
+    public void testSettingConstraintsWithLowQualityThrowsOnRPlus() {
+        // On R and above quality should be set first.
+        mDevicePolicyManager.setPasswordQuality(
+                ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_SOMETHING);
+
+        assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+                .setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+        assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+                .setPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+        assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+                .setPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+        assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+                .setPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+        assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+                .setPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+        assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+                .setPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+        assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+                .setPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+    }
+
+    public void testSettingConstraintsWithNumericQualityOnlyLengthAllowedOnRPlus() {
+        // On R and above quality should be set first.
+        mDevicePolicyManager.setPasswordQuality(
+                ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_NUMERIC);
+
+        // This should be allowed now.
+        mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+
+        // These are still not allowed.
+        assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+                .setPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+        assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+                .setPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+        assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+                .setPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+        assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+                .setPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+        assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+                .setPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+        assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+                .setPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+    }
+
+    public void testSettingConstraintsWithComplexQualityAndResetWithLowerQuality() {
+        // On R and above when quality is lowered, irrelevant requirements are getting reset.
+        mDevicePolicyManager.setPasswordQuality(
+                ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_COMPLEX);
+        // These shouldn't throw anymore
+        mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+        mDevicePolicyManager.setPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+        mDevicePolicyManager.setPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+        mDevicePolicyManager.setPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+        mDevicePolicyManager.setPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+        mDevicePolicyManager.setPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+        mDevicePolicyManager.setPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+
+        // Downgrade to NUMERIC, only length makes sense after that.
+        mDevicePolicyManager.setPasswordQuality(
+                ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_NUMERIC);
+
+        // Length shouldn't be reset
+        assertEquals(TEST_VALUE,
+                mDevicePolicyManager.getPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT));
+
+        // But other requirements should.
+        assertEquals(DEFAULT_NUMERIC,
+                mDevicePolicyManager.getPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT));
+        assertEquals(DEFAULT_LETTERS,
+                mDevicePolicyManager.getPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT));
+        assertEquals(DEFAULT_UPPERCASE,
+                mDevicePolicyManager.getPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT));
+        assertEquals(DEFAULT_LOWERCASE,
+                mDevicePolicyManager.getPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT));
+        assertEquals(DEFAULT_NON_LETTER,
+                mDevicePolicyManager.getPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT));
+        assertEquals(DEFAULT_SYMBOLS,
+                mDevicePolicyManager.getPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT));
+
+        // Downgrade to SOMETHING.
+        mDevicePolicyManager.setPasswordQuality(
+                ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_SOMETHING);
+
+        // Now length should also be reset.
+        assertEquals(DEFAULT_LENGTH,
+                mDevicePolicyManager.getPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT));
+
+    }
+}
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 1c633c3..4dc7da0 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,33 +359,33 @@
                 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.checkPermissionForDataDelivery(mContext, PERMISSION_NAME,
+                PermissionChecker.checkPermissionForDataDelivery(mContext, permission,
                         PermissionChecker.PID_UNKNOWN, packageInfo.applicationInfo.uid,
-                        SIMPLE_PRE_M_APP_PACKAGE_NAME));
+                        SIMPLE_PRE_M_APP_PACKAGE_NAME, null /*message*/));
     }
 
-    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);
 
@@ -367,8 +395,8 @@
         // 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.checkPermissionForDataDelivery(mContext,
-                PERMISSION_NAME, PermissionChecker.PID_UNKNOWN,
-                packageInfo.applicationInfo.uid, SIMPLE_PRE_M_APP_PACKAGE_NAME)
+                permission, PermissionChecker.PID_UNKNOWN,
+                packageInfo.applicationInfo.uid, SIMPLE_PRE_M_APP_PACKAGE_NAME, null /*message*/)
                 == PackageManager.PERMISSION_GRANTED);
         switch (value) {
             case DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED:
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ResetPasswordWithTokenTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ResetPasswordWithTokenTest.java
index 72f01aa..7ab2e9d 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ResetPasswordWithTokenTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ResetPasswordWithTokenTest.java
@@ -154,26 +154,6 @@
         assertPasswordSucceeds("1234", caseDescription);
         assertPasswordSucceeds("abcd", caseDescription); // can't change.
         assertPasswordSucceeds("abcd1234", caseDescription);
-
-        mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 10);
-        caseDescription = "minimum password length = 10";
-        assertEquals(10, mDevicePolicyManager.getPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT));
-        assertPasswordSufficiency(true); // length not checked for this quality
-
-        // TODO(ascull): fix resetPassword() logic so these succeed
-        assertPasswordFails("1234", caseDescription);
-        assertPasswordFails("abcd", caseDescription);
-        assertPasswordFails("abcd1234", caseDescription);
-
-        mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 4);
-        caseDescription = "minimum password length = 4";
-        assertEquals(4, mDevicePolicyManager.getPasswordMinimumLength(
-                ADMIN_RECEIVER_COMPONENT));
-        assertPasswordSufficiency(true);
-
-        assertPasswordSucceeds("1234", caseDescription);
-        assertPasswordSucceeds("abcd", caseDescription);
-        assertPasswordSucceeds("abcd1234", caseDescription);
     }
 
     public void testPasswordQuality_numeric() {
@@ -527,7 +507,6 @@
         // First remove device lock
         mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
                 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
-        mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 0);
         assertTrue(mDevicePolicyManager.resetPasswordWithToken(ADMIN_RECEIVER_COMPONENT, null,
                 TOKEN0, 0));
 
@@ -557,7 +536,14 @@
     }
 
     private void resetComplexPasswordRestrictions() {
+        final int quality = mDevicePolicyManager.getPasswordQuality(ADMIN_RECEIVER_COMPONENT);
+        if (quality < PASSWORD_QUALITY_NUMERIC) {
+            return;
+        }
         mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 0);
+        if (quality < PASSWORD_QUALITY_COMPLEX) {
+            return;
+        }
         mDevicePolicyManager.setPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT, 0);
         mDevicePolicyManager.setPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT, 0);
         mDevicePolicyManager.setPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT, 0);
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/HasLauncherActivityApp/Android.bp b/hostsidetests/devicepolicy/app/HasLauncherActivityApp/Android.bp
index f2c0649..8dc698e 100644
--- a/hostsidetests/devicepolicy/app/HasLauncherActivityApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/HasLauncherActivityApp/Android.bp
@@ -25,6 +25,7 @@
     srcs: ["src/**/*.java"],
     // tag this module as a cts test artifact
     test_suites: [
+        "arcts",
         "cts",
         "vts",
         "general-tests",
@@ -44,6 +45,7 @@
     srcs: ["src/**/*.java"],
     // tag this module as a cts test artifact
     test_suites: [
+        "arcts",
         "cts",
         "vts",
         "general-tests",
@@ -64,6 +66,7 @@
     srcs: ["src/**/*.java"],
     // tag this module as a cts test artifact
     test_suites: [
+        "arcts",
         "cts",
         "vts",
         "general-tests",
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileCalendarTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileCalendarTest.java
index 083848d..5ced0e6 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileCalendarTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileCalendarTest.java
@@ -102,9 +102,6 @@
     private static final long TEST_VIEW_EVENT_END = 10000;
     private static final boolean TEST_VIEW_EVENT_ALL_DAY = false;
     private static final int TEST_VIEW_EVENT_FLAG = Intent.FLAG_ACTIVITY_NEW_TASK;
-    private static final int TIMEOUT_SEC = 10;
-    private static final String ID_TEXTVIEW =
-            "com.android.cts.managedprofile:id/view_event_text";
 
     private ContentResolver mResolver;
     private DevicePolicyManager mDevicePolicyManager;
@@ -335,29 +332,6 @@
         assertThat(cursor.getCount()).isEqualTo(1);
     }
 
-    // This test should be run when the test package is whitelisted.
-    public void testViewEventCrossProfile_intentReceivedWhenWhitelisted() throws Exception {
-        requireRunningOnPrimaryProfile();
-
-        // Get UiDevice and start view event activity.
-        final UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-        device.wakeUp();
-
-        assertThat(CalendarContract.startViewCalendarEventInManagedProfile(mContext,
-                TEST_VIEW_EVENT_ID, TEST_VIEW_EVENT_START, TEST_VIEW_EVENT_END,
-                TEST_VIEW_EVENT_ALL_DAY, TEST_VIEW_EVENT_FLAG)).isTrue();
-        final String textviewString = getViewEventCrossProfileString(TEST_VIEW_EVENT_ID,
-                TEST_VIEW_EVENT_START, TEST_VIEW_EVENT_END, TEST_VIEW_EVENT_ALL_DAY,
-                TEST_VIEW_EVENT_FLAG);
-
-        // Look for the text view to verify that activity is started in work profile.
-        UiObject2 textView = device.wait(
-                Until.findObject(By.res(ID_TEXTVIEW)),
-                TIMEOUT_SEC);
-        assertThat(textView).isNotNull();
-        assertThat(textView.getText()).isEqualTo(textviewString);
-    }
-
     // This test should be run when the test package is whitelisted and cross-profile calendar
     // is enabled in settings.
     public void testPrimaryProfile_getExceptionWhenQueryNonWhitelistedColumns() {
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/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java
index 17cea9b..6cce328 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java
@@ -78,6 +78,7 @@
             .add("getRequiredStrongAuthTimeout")
             .add("setRequiredStrongAuthTimeout")
             .add("isDeviceIdAttestationSupported")
+            .add("isUniqueDeviceAttestationSupported")
             .build();
 
     private static final String LOG_TAG = "ParentProfileTest";
diff --git a/hostsidetests/devicepolicy/app/SimpleApp/AndroidManifest.xml b/hostsidetests/devicepolicy/app/SimpleApp/AndroidManifest.xml
index 9ae0a86..e498048 100644
--- a/hostsidetests/devicepolicy/app/SimpleApp/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/SimpleApp/AndroidManifest.xml
@@ -34,7 +34,11 @@
         <activity android:name=".NonLauncherActivity">
             android:exported="true">
         </activity>
-        <activity android:name=".SimpleActivityStartService" android:exported="true" />
+        <activity android:name=".SimpleActivityStartService"
+            android:turnScreenOn="true"
+            android:excludeFromRecents="true"
+            android:exported="true" />
+        <activity android:name=".SimpleActivityStartFgService" android:exported="true" />
         <activity android:name=".SimpleActivityImmediateExit" >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -47,10 +51,13 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+
         <service android:name=".SimpleService" android:exported="true">
         </service>
         <service android:name=".SimpleService2" android:exported="true" android:process=":other">
         </service>
+        <service android:name=".SimpleService3" android:exported="true" />
+
         <receiver android:name=".SimpleReceiverStartService" android:exported="true">
         </receiver>
         <receiver android:name=".SimpleReceiver" android:exported="true">
diff --git a/hostsidetests/devicepolicy/app/SimpleApp/OWNERS b/hostsidetests/devicepolicy/app/SimpleApp/OWNERS
new file mode 100644
index 0000000..d9a2060
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/SimpleApp/OWNERS
@@ -0,0 +1,2 @@
+ctate@google.com
+hackbod@google.com
diff --git a/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleActivityStartFgService.java b/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleActivityStartFgService.java
new file mode 100644
index 0000000..b17b802
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleActivityStartFgService.java
@@ -0,0 +1,95 @@
+/*
+ * 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.launcherapps.simpleapp;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * Test being able to start a service (with no background check restrictions) as soon as
+ * an activity is created.
+ */
+public class SimpleActivityStartFgService extends Activity {
+    private static final String TAG = "SimpleActivityStartFgService";
+
+    static final String ACTION_START_THEN_FG =
+            "com.android.cts.launcherapps.simpleapp.SimpleActivityStartFgService.START_THEN_FG";
+    public static String ACTION_FINISH_EVERYTHING =
+            "com.android.cts.launcherapps.simpleapp.SimpleActivityStartFgService.FINISH_ALL";
+    public static String ACTION_SIMPLE_ACTIVITY_START_FG_SERVICE_RESULT =
+            "com.android.cts.launcherapps.simpleapp.SimpleActivityStartFgService.NOW_FOREGROUND";
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        final String action = getIntent().getAction();
+        Log.i(TAG, "onCreate() with intent " + action);
+        if (ACTION_START_THEN_FG.equals(action)) {
+            if (!attemptStartService()) {
+                // If we couldn't run the test properly, finish immediately
+                Log.i(TAG, "   Couldn't start service, finishing immediately!");
+                finish();
+            }
+        } else if (ACTION_FINISH_EVERYTHING.equals(action)) {
+            Log.i(TAG, "   *** told to stop service & finish");
+            Intent serviceIntent = getIntent().getParcelableExtra("service");
+            stopService(serviceIntent);
+            finish();
+        }
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        final String action = intent.getAction();
+        if (ACTION_FINISH_EVERYTHING.equals(action)) {
+            Intent serviceIntent = getIntent().getParcelableExtra("service");
+            stopService(serviceIntent);
+            finish();
+        }
+    }
+
+    private boolean attemptStartService() {
+        Intent reply = new Intent(ACTION_SIMPLE_ACTIVITY_START_FG_SERVICE_RESULT);
+        reply.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        Intent serviceIntent = getIntent().getParcelableExtra("service");
+
+        try {
+            final ComponentName svcName = startService(serviceIntent);
+            if (svcName != null) {
+                // Successful start -> the service will send the broadcast once it's
+                // transitioned to the foreground
+                return true;
+            }
+        } catch (Exception e) {
+            // fall through
+        }
+
+        // Failure of some sort: the service isn't going to run as expected,
+        // so report the failure here before exiting.
+        reply.putExtra("result", Activity.RESULT_CANCELED);
+        sendBroadcast(reply);
+        return false;
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleActivityStartService.java b/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleActivityStartService.java
index cda6b6a..0813c24 100644
--- a/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleActivityStartService.java
+++ b/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleActivityStartService.java
@@ -17,8 +17,12 @@
 package com.android.cts.launcherapps.simpleapp;
 
 import android.app.Activity;
+import android.app.KeyguardManager;
+import android.app.KeyguardManager.KeyguardDismissCallback;
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
 
 /**
  * Test being able to start a service (with no background check restrictions) as soon as
@@ -31,18 +35,34 @@
             "com.android.cts.launcherapps.simpleapp.SimpleActivityStartService.RESULT";
 
     @Override
-    public void onCreate(Bundle icicle) {
+    protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
-        attemptStartService();
-        finish();
-    }
+        getSystemService(KeyguardManager.class).requestDismissKeyguard(this,
+                new KeyguardDismissCallback() {
+            @Override
+            public void onDismissCancelled() {
+                Log.i(TAG, "onDismissCancelled");
+            }
 
-    @Override
-    public void onStart() {
-        super.onStart();
+            @Override
+            public void onDismissError() {
+                Log.i(TAG, "onDismissError");
+            }
+
+            @Override
+            public void onDismissSucceeded() {
+                Log.i(TAG, "onDismissSucceeded");
+            }
+        });
+        // No matter if the dismiss was successful or not, continue the test after 2000ms
+        (new Handler()).postDelayed(()-> {
+            attemptStartService();
+            finish();
+        }, 2000);
     }
 
     void attemptStartService() {
+        Log.i(TAG, "attemptStartService");
         Intent reply = new Intent(ACTION_SIMPLE_ACTIVITY_START_SERVICE_RESULT);
         reply.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         Intent serviceIntent = getIntent().getParcelableExtra("service");
diff --git a/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleService3.java b/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleService3.java
new file mode 100644
index 0000000..3820825
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleService3.java
@@ -0,0 +1,106 @@
+/*
+ * 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.launcherapps.simpleapp;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Log;
+
+public class SimpleService3 extends Service {
+    private final static String TAG = SimpleService3.class.getSimpleName();
+
+    static final String ACTION_START_THEN_FG = "com.android.test.action.START_THEN_FG";
+    static final String ACTION_STOP_FOREGROUND = "com.android.test.action.STOP_FOREGROUND";
+    static final String ACTION_STOP_SERVICE = "com.android.test.action.STOP";
+
+    public static String ACTION_SIMPLE_ACTIVITY_START_FG_SERVICE_RESULT =
+            SimpleActivityStartFgService.ACTION_SIMPLE_ACTIVITY_START_FG_SERVICE_RESULT;
+
+    static final String CHANNEL_NAME = "SimpleService3";
+
+    final Binder mBinder = new Binder() {
+        @Override
+        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+                throws RemoteException {
+            switch (code) {
+                case FIRST_CALL_TRANSACTION:
+                    Process.killProcess(Process.myPid());
+                    return true;
+            }
+            return super.onTransact(code, data, reply, flags);
+        }
+    };
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.i(TAG, "onCreate called");
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Log.i(TAG, "Starting: " + intent);
+        if (ACTION_START_THEN_FG.equals(intent.getAction())) {
+            // Set the info for the views that show in the notification panel.
+            NotificationManager nm = getSystemService(NotificationManager.class);
+            if (nm.getNotificationChannel(CHANNEL_NAME) == null) {
+                nm.createNotificationChannel(new NotificationChannel(CHANNEL_NAME, CHANNEL_NAME,
+                        NotificationManager.IMPORTANCE_DEFAULT));
+            }
+            Notification notification = new Notification.Builder(this, CHANNEL_NAME)
+                    .setSmallIcon(android.R.drawable.ic_popup_reminder)  // the status icon
+                    .setWhen(System.currentTimeMillis())  // the time stamp
+                    .setContentTitle("This is a test notification")  // the label
+                    .setContentText("This is a test notification")  // the contents of the entry
+                    .build();
+            startForeground(100, notification);
+
+            // And send the broadcast reporting success
+            Intent reply = new Intent(ACTION_SIMPLE_ACTIVITY_START_FG_SERVICE_RESULT);
+            reply.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+            reply.putExtra("result", Activity.RESULT_FIRST_USER);
+            sendBroadcast(reply);
+        } else if (ACTION_STOP_FOREGROUND.equals(intent.getAction())) {
+            stopForeground(true);
+        } else if (ACTION_STOP_SERVICE.equals(intent.getAction())) {
+            stopSelf();
+        }
+        return super.onStartCommand(intent, flags, startId);
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        Log.i(TAG, "onBind called");
+        return mBinder;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        Log.i(TAG, "onDestroy called");
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferDeviceOwnerIncomingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferDeviceOwnerIncomingTest.java
index f39dbbe..7fecb35 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferDeviceOwnerIncomingTest.java
+++ b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferDeviceOwnerIncomingTest.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.testng.Assert.assertThrows;
 
+import android.app.admin.DevicePolicyManager;
 import android.app.admin.SystemUpdatePolicy;
 
 import androidx.test.filters.SmallTest;
@@ -37,6 +38,9 @@
         assertTrue(mDevicePolicyManager.getCameraDisabled(mIncomingComponentName));
         assertEquals(Collections.singletonList("test.package"),
                 mDevicePolicyManager.getKeepUninstalledPackages(mIncomingComponentName));
+        assertEquals(
+                DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
+                mDevicePolicyManager.getPasswordQuality(mIncomingComponentName));
         assertEquals(123, mDevicePolicyManager.getPasswordMinimumLength(mIncomingComponentName));
         assertSystemPoliciesEqual(SystemUpdatePolicy.createWindowedInstallPolicy(123, 456),
                 mDevicePolicyManager.getSystemUpdatePolicy());
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferProfileOwnerIncomingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferProfileOwnerIncomingTest.java
index 4dc9a5b..92c63c0 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferProfileOwnerIncomingTest.java
+++ b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferProfileOwnerIncomingTest.java
@@ -36,9 +36,13 @@
         assertTrue(mDevicePolicyManager.getCameraDisabled(mIncomingComponentName));
         assertTrue(mDevicePolicyManager.getCrossProfileCallerIdDisabled(mIncomingComponentName));
         assertEquals(
+                DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
+                mDevicePolicyManager.getPasswordQuality(mIncomingComponentName));
+        assertEquals(
                 passwordLength,
                 mDevicePolicyManager.getPasswordMinimumLength(mIncomingComponentName));
 
+
         DevicePolicyManager targetParentProfileInstance =
                 mDevicePolicyManager.getParentProfileInstance(mIncomingComponentName);
         if (mHasSecureLockScreen) {
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferDeviceOwnerOutgoingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferDeviceOwnerOutgoingTest.java
index b42b2bd..365c154 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferDeviceOwnerOutgoingTest.java
+++ b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferDeviceOwnerOutgoingTest.java
@@ -43,6 +43,8 @@
     public void testTransferWithPoliciesOutgoing() throws Throwable {
         int passwordLength = 123;
         mDevicePolicyManager.setCameraDisabled(mOutgoingComponentName, true);
+        mDevicePolicyManager.setPasswordQuality(
+                mOutgoingComponentName, DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
         mDevicePolicyManager.setPasswordMinimumLength(mOutgoingComponentName, passwordLength);
         mDevicePolicyManager.setKeepUninstalledPackages(mOutgoingComponentName,
                 Collections.singletonList("test.package"));
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferProfileOwnerOutgoingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferProfileOwnerOutgoingTest.java
index 157e840..cebeaf1 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferProfileOwnerOutgoingTest.java
+++ b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferProfileOwnerOutgoingTest.java
@@ -43,6 +43,8 @@
         DevicePolicyManager parentDevicePolicyManager =
                 mDevicePolicyManager.getParentProfileInstance(mOutgoingComponentName);
         mDevicePolicyManager.setCameraDisabled(mOutgoingComponentName, true);
+        mDevicePolicyManager.setPasswordQuality(
+                mOutgoingComponentName, DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
         mDevicePolicyManager.setPasswordMinimumLength(mOutgoingComponentName, passwordLength);
         mDevicePolicyManager.setCrossProfileCallerIdDisabled(mOutgoingComponentName, true);
         parentDevicePolicyManager.setPasswordExpirationTimeout(
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/AdbProvisioningTests.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AdbProvisioningTests.java
index 218d6b7..5100aca 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AdbProvisioningTests.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AdbProvisioningTests.java
@@ -19,6 +19,7 @@
 import static com.android.cts.devicepolicy.DeviceAndProfileOwnerTest.DEVICE_ADMIN_APK;
 import static com.android.cts.devicepolicy.DeviceAndProfileOwnerTest.DEVICE_ADMIN_PKG;
 import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
 
 import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
 import com.android.tradefed.device.DeviceNotAvailableException;
@@ -47,7 +48,7 @@
     }
 
     public void testAdbDeviceOwnerLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
         assertMetricsLogged(getDevice(), () -> {
@@ -61,7 +62,7 @@
     }
 
     public void testAdbProfileOwnerLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
         assertMetricsLogged(getDevice(), () -> {
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..f30ee5e 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
@@ -1,7 +1,10 @@
 package com.android.cts.devicepolicy;
 
 import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
 
+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 +13,7 @@
 import java.io.FileNotFoundException;
 import java.util.Collections;
 import java.util.Map;
+
 import javax.annotation.Nullable;
 
 /**
@@ -57,10 +61,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 +76,7 @@
         verifyCrossProfileAppsApi(mPrimaryUserId, mProfileId, TARGET_USER_TEST_CLASS);
     }
 
+    @LargeTest
     public void testManagedProfileToPrimaryUser() throws Exception {
         if (!mHasManagedUserFeature) {
             return;
@@ -75,6 +84,7 @@
         verifyCrossProfileAppsApi(mProfileId, mPrimaryUserId, TARGET_USER_TEST_CLASS);
     }
 
+    @LargeTest
     public void testStartActivity() throws Exception {
         if (!mHasManagedUserFeature) {
             return;
@@ -82,6 +92,7 @@
         verifyCrossProfileAppsApi(mProfileId, mPrimaryUserId, START_ACTIVITY_TEST_CLASS);
     }
 
+    @LargeTest
     public void testPrimaryUserToSecondaryUser() throws Exception {
         if (!mCanTestMultiUser) {
             return;
@@ -89,6 +100,7 @@
         verifyCrossProfileAppsApi(mPrimaryUserId, mSecondaryUserId, NON_TARGET_USER_TEST_CLASS);
     }
 
+    @LargeTest
     public void testSecondaryUserToManagedProfile() throws Exception {
         if (!mCanTestMultiUser || !mHasManagedUserFeature) {
             return;
@@ -97,6 +109,7 @@
 
     }
 
+    @LargeTest
     public void testManagedProfileToSecondaryUser() throws Exception {
         if (!mCanTestMultiUser || !mHasManagedUserFeature) {
             return;
@@ -104,8 +117,9 @@
         verifyCrossProfileAppsApi(mProfileId, mSecondaryUserId, NON_TARGET_USER_TEST_CLASS);
     }
 
+    @LargeTest
     public void testStartMainActivity_logged() throws Exception {
-        if (!mHasManagedUserFeature) {
+        if (!mHasManagedUserFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
         assertMetricsLogged(
@@ -123,8 +137,9 @@
                         .build());
     }
 
+    @LargeTest
     public void testGetTargetUserProfiles_logged() throws Exception {
-        if (!mHasManagedUserFeature) {
+        if (!mHasManagedUserFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
         assertMetricsLogged(
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..05bc832 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -17,10 +17,14 @@
 package com.android.cts.devicepolicy;
 
 import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
 
+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;
@@ -152,7 +156,7 @@
         "cmd netpolicy set restrict-background false";
 
     // The following constants were copied from DevicePolicyManager
-    private static final int PASSWORD_QUALITY_SOMETHING = 0x10000;
+    private static final int PASSWORD_QUALITY_COMPLEX = 0x60000;
     private static final int KEYGUARD_DISABLE_FEATURES_NONE = 0;
     private static final int KEYGUARD_DISABLE_FINGERPRINT = 1 << 5;
     private static final int KEYGUARD_DISABLE_TRUST_AGENTS = 1 << 4;
@@ -212,7 +216,7 @@
     }
 
     public void testInstallCaCertLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
         assertMetricsLogged(getDevice(), () -> {
@@ -278,13 +282,15 @@
             // The DPC should still be able to manage app restrictions normally.
             executeDeviceTestClass(".ApplicationRestrictionsTest");
 
-            assertMetricsLogged(getDevice(), () -> {
-                executeDeviceTestMethod(".ApplicationRestrictionsTest",
-                        "testSetApplicationRestrictions");
-            }, new DevicePolicyEventWrapper.Builder(EventId.SET_APPLICATION_RESTRICTIONS_VALUE)
-                    .setAdminPackageName(DEVICE_ADMIN_PKG)
-                    .setStrings(APP_RESTRICTIONS_TARGET_APP_PKG)
-                    .build());
+            if (isStatsdEnabled(getDevice())) {
+                assertMetricsLogged(getDevice(), () -> {
+                    executeDeviceTestMethod(".ApplicationRestrictionsTest",
+                            "testSetApplicationRestrictions");
+                }, new DevicePolicyEventWrapper.Builder(EventId.SET_APPLICATION_RESTRICTIONS_VALUE)
+                        .setAdminPackageName(DEVICE_ADMIN_PKG)
+                        .setStrings(APP_RESTRICTIONS_TARGET_APP_PKG)
+                        .build());
+            }
         } finally {
             changeApplicationRestrictionsManagingPackage(null);
         }
@@ -541,7 +547,7 @@
 
     @RequiresDevice
     public void testAlwaysOnVpnPackageLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
         // Will be uninstalled in tearDown().
@@ -564,6 +570,14 @@
         executeDeviceTestMethod(".PermissionsTest", "testPermissionPolicy");
     }
 
+    public void testAutoGrantMultiplePermissionsInGroup() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        installAppPermissionAppAsUser();
+        executeDeviceTestMethod(".PermissionsTest", "testAutoGrantMultiplePermissionsInGroup");
+    }
+
     public void testPermissionMixedPolicies() throws Exception {
         if (!mHasFeature) {
             return;
@@ -636,14 +650,16 @@
             return;
         }
         executeDeviceTestClass(".PersistentIntentResolvingTest");
-        assertMetricsLogged(getDevice(), () -> {
-            executeDeviceTestMethod(".PersistentIntentResolvingTest",
-                    "testAddPersistentPreferredActivityYieldsReceptionAtTarget");
-        }, new DevicePolicyEventWrapper.Builder(EventId.ADD_PERSISTENT_PREFERRED_ACTIVITY_VALUE)
+        if (isStatsdEnabled(getDevice())) {
+            assertMetricsLogged(getDevice(), () -> {
+                executeDeviceTestMethod(".PersistentIntentResolvingTest",
+                        "testAddPersistentPreferredActivityYieldsReceptionAtTarget");
+            }, new DevicePolicyEventWrapper.Builder(EventId.ADD_PERSISTENT_PREFERRED_ACTIVITY_VALUE)
                     .setAdminPackageName(DEVICE_ADMIN_PKG)
                     .setStrings(DEVICE_ADMIN_PKG,
                             "com.android.cts.deviceandprofileowner.EXAMPLE_ACTION")
                     .build());
+        }
     }
 
     public void testScreenCaptureDisabled() throws Exception {
@@ -688,17 +704,20 @@
             return;
         }
         executeDeviceTestClass(".SupportMessageTest");
-        assertMetricsLogged(getDevice(), () -> {
-            executeDeviceTestMethod(
-                    ".SupportMessageTest", "testShortSupportMessageSetGetAndClear");
-        }, new DevicePolicyEventWrapper.Builder(EventId.SET_SHORT_SUPPORT_MESSAGE_VALUE)
+        if (isStatsdEnabled(getDevice())) {
+            assertMetricsLogged(getDevice(), () -> {
+                executeDeviceTestMethod(
+                        ".SupportMessageTest", "testShortSupportMessageSetGetAndClear");
+            }, new DevicePolicyEventWrapper.Builder(EventId.SET_SHORT_SUPPORT_MESSAGE_VALUE)
                     .setAdminPackageName(DEVICE_ADMIN_PKG)
                     .build());
-        assertMetricsLogged(getDevice(), () -> {
-            executeDeviceTestMethod(".SupportMessageTest", "testLongSupportMessageSetGetAndClear");
-        }, new DevicePolicyEventWrapper.Builder(EventId.SET_LONG_SUPPORT_MESSAGE_VALUE)
+            assertMetricsLogged(getDevice(), () -> {
+                executeDeviceTestMethod(".SupportMessageTest",
+                        "testLongSupportMessageSetGetAndClear");
+            }, new DevicePolicyEventWrapper.Builder(EventId.SET_LONG_SUPPORT_MESSAGE_VALUE)
                     .setAdminPackageName(DEVICE_ADMIN_PKG)
                     .build());
+        }
     }
 
     public void testApplicationHidden() throws Exception {
@@ -707,11 +726,12 @@
         }
         installAppPermissionAppAsUser();
         executeDeviceTestClass(".ApplicationHiddenTest");
-        installAppAsUser(PERMISSIONS_APP_APK, mUserId);
-        assertMetricsLogged(getDevice(), () -> {
-            executeDeviceTestMethod(".ApplicationHiddenTest",
-                    "testSetApplicationHidden");
-        }, new DevicePolicyEventWrapper.Builder(EventId.SET_APPLICATION_HIDDEN_VALUE)
+        if (isStatsdEnabled(getDevice())) {
+            installAppAsUser(PERMISSIONS_APP_APK, mUserId);
+            assertMetricsLogged(getDevice(), () -> {
+                executeDeviceTestMethod(".ApplicationHiddenTest",
+                        "testSetApplicationHidden");
+            }, new DevicePolicyEventWrapper.Builder(EventId.SET_APPLICATION_HIDDEN_VALUE)
                     .setAdminPackageName(DEVICE_ADMIN_PKG)
                     .setBoolean(false)
                     .setStrings(PERMISSIONS_APP_PKG, "hidden")
@@ -721,6 +741,7 @@
                     .setBoolean(false)
                     .setStrings(PERMISSIONS_APP_PKG, "not_hidden")
                     .build());
+        }
     }
 
     public void testAccountManagement_deviceAndProfileOwnerAlwaysAllowed() throws Exception {
@@ -807,13 +828,15 @@
 
 
         runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".DelegatedCertInstallerTest", mUserId);
-        assertMetricsLogged(getDevice(), () -> {
+        if (isStatsdEnabled(getDevice())) {
+            assertMetricsLogged(getDevice(), () -> {
                 runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".DelegatedCertInstallerTest",
                         "testInstallKeyPair", mUserId);
-                }, new DevicePolicyEventWrapper.Builder(EventId.SET_CERT_INSTALLER_PACKAGE_VALUE)
-                .setAdminPackageName(DEVICE_ADMIN_PKG)
-                .setStrings(CERT_INSTALLER_PKG)
-                .build());
+            }, new DevicePolicyEventWrapper.Builder(EventId.SET_CERT_INSTALLER_PACKAGE_VALUE)
+                    .setAdminPackageName(DEVICE_ADMIN_PKG)
+                    .setStrings(CERT_INSTALLER_PKG)
+                    .build());
+        }
     }
 
     public interface DelegatedCertInstallerTestAction {
@@ -848,6 +871,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 +910,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 +931,10 @@
         if (!mHasFeature) {
             return;
         }
+        if (!hasService("wallpaper")) {
+            CLog.d("testDisallowSetWallpaper_allowed(): device does not support wallpapers");
+            return;
+        }
         executeDeviceTestMethod(".CustomizationRestrictionsTest",
                 "testDisallowSetWallpaper_allowed");
     }
@@ -1042,7 +1104,7 @@
     }
 
     public void testDisallowAdjustVolumeMutedLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
         assertMetricsLogged(getDevice(), () -> {
@@ -1058,6 +1120,7 @@
                     .build());
     }
 
+    @FlakyTest(bugId = 132226089)
     public void testLockTask() throws Exception {
         if (!mHasFeature) {
             return;
@@ -1065,14 +1128,17 @@
         try {
             installAppAsUser(INTENT_RECEIVER_APK, mUserId);
             executeDeviceTestClass(".LockTaskTest");
-            assertMetricsLogged(
-                    getDevice(),
-                    () -> executeDeviceTestMethod(".LockTaskTest", "testStartLockTask"),
-                    new DevicePolicyEventWrapper.Builder(EventId.SET_LOCKTASK_MODE_ENABLED_VALUE)
-                            .setAdminPackageName(DEVICE_ADMIN_PKG)
-                            .setBoolean(true)
-                            .setStrings(DEVICE_ADMIN_PKG)
-                            .build());
+            if (isStatsdEnabled(getDevice())) {
+                assertMetricsLogged(
+                        getDevice(),
+                        () -> executeDeviceTestMethod(".LockTaskTest", "testStartLockTask"),
+                        new DevicePolicyEventWrapper.Builder(
+                                EventId.SET_LOCKTASK_MODE_ENABLED_VALUE)
+                                .setAdminPackageName(DEVICE_ADMIN_PKG)
+                                .setBoolean(true)
+                                .setStrings(DEVICE_ADMIN_PKG)
+                                .build());
+            }
         } catch (AssertionError ex) {
             // STOPSHIP(b/32771855), remove this once we fixed the bug.
             executeShellCommand("dumpsys activity activities");
@@ -1084,6 +1150,7 @@
         }
     }
 
+    @LargeTest
     public void testLockTaskAfterReboot() throws Exception {
         if (!mHasFeature) {
             return;
@@ -1104,6 +1171,7 @@
         }
     }
 
+    @LargeTest
     public void testLockTaskAfterReboot_tryOpeningSettings() throws Exception {
         if (!mHasFeature) {
             return;
@@ -1163,6 +1231,7 @@
         }
     }
 
+    @FlakyTest(bugId = 141314026)
     public void testSuspendPackage() throws Exception {
         if (!mHasFeature) {
             return;
@@ -1190,6 +1259,7 @@
         executeDeviceTestMethod(".SuspendPackageTest", "testSuspendNotSuspendablePackages");
     }
 
+    @FlakyTest(bugId = 141314026)
     public void testSuspendPackageWithPackageManager() throws Exception {
         if (!mHasFeature) {
             return;
@@ -1215,6 +1285,7 @@
         executeDeviceTestClass(".TrustAgentInfoTest");
     }
 
+    @FlakyTest(bugId = 141161038)
     public void testCannotRemoveUserIfRestrictionSet() throws Exception {
         // Outside of the primary user, setting DISALLOW_REMOVE_USER would not work.
         if (!mHasFeature || !canCreateAdditionalUsers(1) || mUserId != getPrimaryUser()) {
@@ -1270,7 +1341,7 @@
     }
 
     public void testSetCameraDisabledLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
         assertMetricsLogged(getDevice(), () -> {
@@ -1285,6 +1356,7 @@
                     .build());
     }
 
+    @LockSettingsTest
     public void testResetPasswordWithToken() throws Exception {
         if (!mHasFeature || !mHasSecureLockScreen) {
             return;
@@ -1306,6 +1378,19 @@
         executeDeviceTestClass(".PasswordSufficientInitiallyTest");
     }
 
+    public void testPasswordRequirementsApi() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+
+        executeDeviceTestMethod(".PasswordRequirementsTest",
+                "testSettingConstraintsWithLowQualityThrowsOnRPlus");
+        executeDeviceTestMethod(".PasswordRequirementsTest",
+                "testSettingConstraintsWithNumericQualityOnlyLengthAllowedOnRPlus");
+        executeDeviceTestMethod(".PasswordRequirementsTest",
+                "testSettingConstraintsWithComplexQualityAndResetWithLowerQuality");
+    }
+
     public void testGetCurrentFailedPasswordAttempts() throws Exception {
         if (!mHasFeature || !mHasSecureLockScreen) {
             return;
@@ -1435,7 +1520,7 @@
     }
 
     public void testInstallKeyPairLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
 
@@ -1452,7 +1537,7 @@
     }
 
     public void testGenerateKeyPairLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
 
@@ -1475,7 +1560,7 @@
     }
 
     public void testSetKeyPairCertificateLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
 
@@ -1493,10 +1578,11 @@
         }
 
         executeDeviceTestClass(".AccessibilityServicesTest");
-        assertMetricsLogged(getDevice(), () -> {
-            executeDeviceTestMethod(".AccessibilityServicesTest",
-                    "testPermittedAccessibilityServices");
-        }, new DevicePolicyEventWrapper
+        if (isStatsdEnabled(getDevice())) {
+            assertMetricsLogged(getDevice(), () -> {
+                executeDeviceTestMethod(".AccessibilityServicesTest",
+                        "testPermittedAccessibilityServices");
+            }, new DevicePolicyEventWrapper
                     .Builder(EventId.SET_PERMITTED_ACCESSIBILITY_SERVICES_VALUE)
                     .setAdminPackageName(DEVICE_ADMIN_PKG)
                     .setStrings((String[]) null)
@@ -1511,6 +1597,7 @@
                     .setAdminPackageName(DEVICE_ADMIN_PKG)
                     .setStrings("com.google.pkg.one", "com.google.pkg.two")
                     .build());
+        }
     }
 
     public void testPermittedInputMethods() throws Exception {
@@ -1519,10 +1606,10 @@
         }
 
         executeDeviceTestClass(".InputMethodsTest");
-        assertMetricsLogged(getDevice(), () -> {
-            executeDeviceTestMethod(".InputMethodsTest",
-                    "testPermittedInputMethods");
-        }, new DevicePolicyEventWrapper.Builder(EventId.SET_PERMITTED_INPUT_METHODS_VALUE)
+        if (isStatsdEnabled(getDevice())) {
+            assertMetricsLogged(getDevice(), () -> {
+                executeDeviceTestMethod(".InputMethodsTest", "testPermittedInputMethods");
+            }, new DevicePolicyEventWrapper.Builder(EventId.SET_PERMITTED_INPUT_METHODS_VALUE)
                     .setAdminPackageName(DEVICE_ADMIN_PKG)
                     .setStrings((String[]) null)
                     .build(),
@@ -1534,6 +1621,7 @@
                     .setAdminPackageName(DEVICE_ADMIN_PKG)
                     .setStrings("com.google.pkg.one", "com.google.pkg.two")
                     .build());
+        }
     }
 
     public void testSetStorageEncryption() throws Exception {
@@ -1547,7 +1635,7 @@
     }
 
     public void testPasswordMethodsLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
 
@@ -1555,7 +1643,7 @@
             executeDeviceTestMethod(".DevicePolicyLoggingTest", "testPasswordMethodsLogged");
         }, new DevicePolicyEventWrapper.Builder(EventId.SET_PASSWORD_QUALITY_VALUE)
                     .setAdminPackageName(DEVICE_ADMIN_PKG)
-                    .setInt(PASSWORD_QUALITY_SOMETHING)
+                    .setInt(PASSWORD_QUALITY_COMPLEX)
                     .setBoolean(false)
                     .build(),
             new DevicePolicyEventWrapper.Builder(EventId.SET_PASSWORD_MINIMUM_LENGTH_VALUE)
@@ -1589,7 +1677,7 @@
     }
 
     public void testLockNowLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
         assertMetricsLogged(getDevice(), () -> {
@@ -1601,7 +1689,7 @@
     }
 
     public void testSetKeyguardDisabledFeaturesLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
         assertMetricsLogged(getDevice(), () -> {
@@ -1630,7 +1718,7 @@
     }
 
     public void testSetUserRestrictionLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
         assertMetricsLogged(getDevice(), () -> {
@@ -1664,7 +1752,7 @@
     }
 
     public void testSetSecureSettingLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
         assertMetricsLogged(getDevice(), () -> {
@@ -1690,7 +1778,7 @@
     }
 
     public void testSetPermissionPolicyLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
         assertMetricsLogged(getDevice(), () -> {
@@ -1714,7 +1802,7 @@
     }
 
     public void testSetPermissionGrantStateLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
         installAppPermissionAppAsUser();
@@ -1758,7 +1846,7 @@
     }
 
     public void testEnableSystemAppLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
         final List<String> enabledSystemPackageNames = getEnabledSystemPackageNames();
@@ -1777,7 +1865,7 @@
     }
 
     public void testEnableSystemAppWithIntentLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
         final String systemPackageToEnable = getLaunchableSystemPackage();
@@ -1797,7 +1885,7 @@
     }
 
     public void testSetUninstallBlockedLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
         installAppAsUser(PERMISSIONS_APP_APK, mUserId);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTestApi25.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTestApi25.java
index cacb226..0ee24a6 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTestApi25.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTestApi25.java
@@ -16,6 +16,7 @@
 
 package com.android.cts.devicepolicy;
 
+import android.platform.test.annotations.FlakyTest;
 import android.platform.test.annotations.RequiresDevice;
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
@@ -72,6 +73,7 @@
     }
 
     /** Additional test for resetPassword for FBE-enabled devices. */
+    @FlakyTest(bugId = 141161690)
     public void testResetPasswordFbe() throws Exception {
         if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
             return;
@@ -95,6 +97,15 @@
         executeDeviceTestMethod(".PermissionsTest", "testPermissionGrantStateAppPreMDeviceAdminPreQ");
     }
 
+    public void testPasswordRequirementsApi() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+
+        executeDeviceTestMethod(".PasswordRequirementsTest",
+                "testPasswordConstraintsDoesntThrowAndPreservesValuesPreR");
+    }
+
     protected void executeDeviceTestClass(String className) throws Exception {
         runDeviceTestsAsUser(DEVICE_ADMIN_PKG, className, mUserId);
     }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusProfileOwnerTest.java
index 25168ff..2acb6cb 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusProfileOwnerTest.java
@@ -17,12 +17,16 @@
 package com.android.cts.devicepolicy;
 
 import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
+
+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 +97,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 +121,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 +143,7 @@
      * {@link #testBindDeviceAdminServiceAsUser_corpOwnedManagedProfileWithManagedProvisioning}
      * except we don't enable the profile.
      */
+    @FlakyTest
     public void testBindDeviceAdminServiceAsUser_canBindEvenIfProfileNotEnabled() throws Exception {
         if (!mHasFeature) {
             return;
@@ -174,6 +181,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 +203,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;
@@ -229,6 +238,7 @@
         assertTrue(getDevice().removeUser(profileUserId));
     }
 
+    @FlakyTest(bugId = 141161038)
     public void testCannotRemoveUserIfRestrictionSet() throws Exception {
         if (!mHasFeature || !canCreateAdditionalUsers(1)) {
             return;
@@ -326,7 +336,7 @@
     }
 
     public void testWipeData_managedProfileLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
         int profileUserId = setupManagedProfile(COMP_DPC_APK, COMP_DPC_PKG, COMP_DPC_ADMIN);
@@ -353,7 +363,7 @@
     }
 
     public void testWipeData_secondaryUserLogged() throws Exception {
-        if (!mHasFeature || !canCreateAdditionalUsers(1)) {
+        if (!mHasFeature || !canCreateAdditionalUsers(1) || !isStatsdEnabled(getDevice())) {
             return;
         }
         int secondaryUserId = setupManagedSecondaryUser();
@@ -416,6 +426,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 7c335c2..1c15666 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -17,7 +17,10 @@
 package com.android.cts.devicepolicy;
 
 import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
 
+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 +103,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();
@@ -143,23 +155,28 @@
             return;
         }
         executeDeviceOwnerTest("LockScreenInfoTest");
-        assertMetricsLogged(getDevice(), () -> {
-            executeDeviceTestMethod(".LockScreenInfoTest", "testSetAndGetLockInfo");
-        }, new DevicePolicyEventWrapper.Builder(EventId.SET_DEVICE_OWNER_LOCK_SCREEN_INFO_VALUE)
-                .setAdminPackageName(DEVICE_OWNER_PKG)
-                .build());
+        if (isStatsdEnabled(getDevice())) {
+            assertMetricsLogged(getDevice(), () -> {
+                executeDeviceTestMethod(".LockScreenInfoTest", "testSetAndGetLockInfo");
+            }, new DevicePolicyEventWrapper.Builder(EventId.SET_DEVICE_OWNER_LOCK_SCREEN_INFO_VALUE)
+                    .setAdminPackageName(DEVICE_OWNER_PKG)
+                    .build());
+        }
     }
 
+    @FlakyTest(bugId = 137088260)
     public void testWifi() throws Exception {
         if (!mHasFeature || !hasDeviceFeature("android.hardware.wifi")) {
             return;
         }
         executeDeviceOwnerTest("WifiTest");
-        assertMetricsLogged(getDevice(), () -> {
-            executeDeviceTestMethod(".WifiTest", "testGetWifiMacAddress");
-        }, new DevicePolicyEventWrapper.Builder(EventId.GET_WIFI_MAC_ADDRESS_VALUE)
-                .setAdminPackageName(DEVICE_OWNER_PKG)
-                .build());
+        if (isStatsdEnabled(getDevice())) {
+            assertMetricsLogged(getDevice(), () -> {
+                executeDeviceTestMethod(".WifiTest", "testGetWifiMacAddress");
+            }, new DevicePolicyEventWrapper.Builder(EventId.GET_WIFI_MAC_ADDRESS_VALUE)
+                    .setAdminPackageName(DEVICE_OWNER_PKG)
+                    .build());
+        }
     }
 
     public void testRemoteBugreportWithTwoUsers() throws Exception {
@@ -175,6 +192,7 @@
         }
     }
 
+    @FlakyTest(bugId = 137071121)
     public void testCreateAndManageUser_LowStorage() throws Exception {
         if (!mHasCreateAndManageUserFeature) {
             return;
@@ -231,6 +249,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 +433,7 @@
         }
     }
 
+    @FlakyTest(bugId = 126955083)
     public void testUserAddedOrRemovedBroadcasts() throws Exception {
         if (mHasCreateAndManageUserFeature) {
             executeDeviceTestMethod(".CreateAndManageUserTest",
@@ -448,6 +468,7 @@
         }
     }
 
+    @FlakyTest(bugId = 137093665)
     public void testSecurityLoggingWithSingleUser() throws Exception {
         if (!mHasFeature) {
             return;
@@ -491,7 +512,7 @@
     }
 
     public void testSecurityLoggingEnabledLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
         assertMetricsLogged(getDevice(), () -> {
@@ -531,6 +552,7 @@
         }
     }
 
+    @FlakyTest(bugId = 137092833)
     public void testNetworkLoggingWithSingleUser() throws Exception {
         if (!mHasFeature) {
             return;
@@ -548,6 +570,7 @@
                 Collections.singletonMap(ARG_NETWORK_LOGGING_BATCH_COUNT, Integer.toString(2)));
     }
 
+    @LargeTest
     public void testNetworkLogging_rebootResetsId() throws Exception {
         if (!mHasFeature) {
             return;
@@ -573,6 +596,7 @@
         executeDeviceTestMethod(".AffiliationTest", "testSetAffiliationId_containsEmptyString");
     }
 
+    @LargeTest
     public void testSystemUpdatePolicy() throws Exception {
         if (!mHasFeature) {
             return;
@@ -581,7 +605,7 @@
     }
 
     public void testSetSystemUpdatePolicyLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
         assertMetricsLogged(getDevice(), () -> {
@@ -610,6 +634,7 @@
                     .build());
     }
 
+    @FlakyTest(bugId = 127101449)
     public void testWifiConfigLockdown() throws Exception {
         final boolean hasWifi = hasDeviceFeature("android.hardware.wifi");
         if (hasWifi && mHasFeature) {
@@ -698,24 +723,28 @@
                 "testIsProvisioningAllowedTrueForManagedProfileAction");
     }
 
+    @FlakyTest(bugId = 137096267)
     public void testAdminActionBookkeeping() throws Exception {
         if (!mHasFeature) {
             return;
         }
         executeDeviceOwnerTest("AdminActionBookkeepingTest");
-        assertMetricsLogged(getDevice(), () -> {
-            executeDeviceTestMethod(".AdminActionBookkeepingTest", "testRetrieveSecurityLogs");
-        }, new DevicePolicyEventWrapper.Builder(EventId.RETRIEVE_SECURITY_LOGS_VALUE)
+        if (isStatsdEnabled(getDevice())) {
+            assertMetricsLogged(getDevice(), () -> {
+                executeDeviceTestMethod(".AdminActionBookkeepingTest", "testRetrieveSecurityLogs");
+            }, new DevicePolicyEventWrapper.Builder(EventId.RETRIEVE_SECURITY_LOGS_VALUE)
                     .setAdminPackageName(DEVICE_OWNER_PKG)
                     .build(),
-            new DevicePolicyEventWrapper.Builder(EventId.RETRIEVE_PRE_REBOOT_SECURITY_LOGS_VALUE)
+            new DevicePolicyEventWrapper.Builder(
+                    EventId.RETRIEVE_PRE_REBOOT_SECURITY_LOGS_VALUE)
                     .setAdminPackageName(DEVICE_OWNER_PKG)
                     .build());
-        assertMetricsLogged(getDevice(), () -> {
-            executeDeviceTestMethod(".AdminActionBookkeepingTest", "testRequestBugreport");
-        }, new DevicePolicyEventWrapper.Builder(EventId.REQUEST_BUGREPORT_VALUE)
-                .setAdminPackageName(DEVICE_OWNER_PKG)
-                .build());
+            assertMetricsLogged(getDevice(), () -> {
+                executeDeviceTestMethod(".AdminActionBookkeepingTest", "testRequestBugreport");
+            }, new DevicePolicyEventWrapper.Builder(EventId.REQUEST_BUGREPORT_VALUE)
+                    .setAdminPackageName(DEVICE_OWNER_PKG)
+                    .build());
+        }
     }
 
     public void testBluetoothRestriction() throws Exception {
@@ -837,6 +866,7 @@
         }
     }
 
+    @LargeTest
     public void testPackageInstallCache_multiUser() throws Exception {
         if (!mHasFeature || !canCreateAdditionalUsers(1)) {
             return;
@@ -906,6 +936,7 @@
         executeDeviceOwnerTest("OverrideApnTest");
     }
 
+    @FlakyTest(bugId = 134487729)
     public void testPrivateDnsPolicy() throws Exception {
         if (!mHasFeature) {
             return;
@@ -927,7 +958,7 @@
     }
 
     public void testInstallUpdateLogged() throws Exception {
-        if (!mHasFeature || !isDeviceAb()) {
+        if (!mHasFeature || !isDeviceAb() || !isStatsdEnabled(getDevice())) {
             return;
         }
         pushUpdateFileToDevice("wrongHash.zip");
@@ -961,7 +992,7 @@
     }
 
     public void testSetKeyguardDisabledLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
         assertMetricsLogged(getDevice(), () -> {
@@ -972,7 +1003,7 @@
     }
 
     public void testSetStatusBarDisabledLogged() throws Exception {
-        if (!mHasFeature) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
         assertMetricsLogged(getDevice(), () -> {
@@ -1011,6 +1042,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 f8a78f0..2b6c245 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..bf81c28
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileContactsTest.java
@@ -0,0 +1,420 @@
+/*
+ * 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 static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
+
+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();
+                if (isStatsdEnabled(getDevice())) {
+                    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..ded287c
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
@@ -0,0 +1,511 @@
+/*
+ * 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 static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
+
+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);
+
+        if (isStatsdEnabled(getDevice())) {
+            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 || !isStatsdEnabled(getDevice())) {
+            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);
+        } 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);
+        } 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..ce0bbc0a
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfilePasswordTest.java
@@ -0,0 +1,248 @@
+/*
+ * 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 static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
+
+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 || !isStatsdEnabled(getDevice())) {
+            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/ManagedProfileProvisioningTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningTest.java
index 93a17e0..263c7f0 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningTest.java
@@ -15,6 +15,8 @@
  */
 package com.android.cts.devicepolicy;
 
+import android.platform.test.annotations.FlakyTest;
+
 public class ManagedProfileProvisioningTest extends BaseDevicePolicyTest {
     private static final String MANAGED_PROFILE_PKG = "com.android.cts.managedprofile";
     private static final String MANAGED_PROFILE_APK = "CtsManagedProfileApp.apk";
@@ -49,7 +51,7 @@
         }
         super.tearDown();
     }
-
+    @FlakyTest(bugId = 141747631)
     public void testManagedProfileProvisioning() throws Exception {
         if (!mHasFeature) {
             return;
@@ -61,6 +63,7 @@
                 "testIsManagedProfile", mProfileUserId);
     }
 
+    @FlakyTest(bugId = 127275983)
     public void testEXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE() throws Exception {
         if (!mHasFeature) {
             return;
@@ -72,6 +75,7 @@
                 "testVerifyAdminExtraBundle", mProfileUserId);
     }
 
+    @FlakyTest(bugId = 141747631)
     public void testVerifySuccessfulIntentWasReceived() throws Exception {
         if (!mHasFeature) {
             return;
@@ -83,6 +87,7 @@
                 "testVerifySuccessfulIntentWasReceived", mProfileUserId);
     }
 
+    @FlakyTest(bugId = 141747631)
     public void testAccountMigration() throws Exception {
         if (!mHasFeature) {
             return;
@@ -97,6 +102,7 @@
                 "testAccountNotExist", mParentUserId);
     }
 
+    @FlakyTest(bugId = 141747631)
     public void testAccountCopy() throws Exception {
         if (!mHasFeature) {
             return;
@@ -111,6 +117,7 @@
                 "testAccountExist", mParentUserId);
     }
 
+    @FlakyTest(bugId = 141747631)
     public void testWebview() 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..433255b 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -13,12 +13,12 @@
  * 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 static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
 
-
+import android.platform.test.annotations.LargeTest;
 import android.stats.devicepolicy.EventId;
 
 import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
@@ -28,130 +28,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 +60,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 +101,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 +138,7 @@
         assertAppLinkResult("testReceivedByAppLinkActivityInManaged");
     }
 
+    @LargeTest
     public void testAppLinks_enabledStatus() throws Exception {
         if (!mHasFeature) {
             return;
@@ -580,134 +201,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 +286,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;
@@ -891,21 +296,15 @@
                 "testDefaultOrganizationNameIsNull", mProfileUserId);
         runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".OrganizationInfoTest",
                 mProfileUserId);
-        assertMetricsLogged(getDevice(), () -> {
-            runDeviceTestsAsUser(
-                    MANAGED_PROFILE_PKG, MANAGED_PROFILE_PKG + ".OrganizationInfoTest",
-                    "testSetOrganizationColor", mProfileUserId);
-        }, new DevicePolicyEventWrapper.Builder(EventId.SET_ORGANIZATION_COLOR_VALUE)
-                .setAdminPackageName(MANAGED_PROFILE_PKG)
-                .build());
-    }
-
-    public void testPasswordMinimumRestrictions() throws Exception {
-        if (!mHasFeature) {
-            return;
+        if (isStatsdEnabled(getDevice())) {
+            assertMetricsLogged(getDevice(), () -> {
+                runDeviceTestsAsUser(
+                        MANAGED_PROFILE_PKG, MANAGED_PROFILE_PKG + ".OrganizationInfoTest",
+                        "testSetOrganizationColor", mProfileUserId);
+            }, new DevicePolicyEventWrapper.Builder(EventId.SET_ORGANIZATION_COLOR_VALUE)
+                    .setAdminPackageName(MANAGED_PROFILE_PKG)
+                    .build());
         }
-        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".PasswordMinimumRestrictionsTest",
-                mProfileUserId);
     }
 
     public void testDevicePolicyManagerParentSupport() throws Exception {
@@ -950,6 +349,7 @@
                 /*expectFailure*/ true));
     }
 
+    @LargeTest
     public void testCannotSetDeviceOwnerWhenProfilePresent() throws Exception {
         if (!mHasFeature) {
             return;
@@ -985,84 +385,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 +399,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 +439,7 @@
         }
     }
 
+    @LargeTest
     public void testManagedCall() throws Exception {
         if (!mHasFeature) {
             return;
@@ -1177,52 +490,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,332 +565,8 @@
                 "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) {
+        if (!mHasFeature || !isStatsdEnabled(getDevice())) {
             return;
         }
         assertMetricsLogged(getDevice(), () -> {
@@ -1635,23 +578,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 +588,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 +596,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 +604,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..5d62c83
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileWipeTest.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.devicepolicy;
+
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
+
+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 || !isStatsdEnabled(getDevice())) {
+            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..3e3a723 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(bugId = 141161038)
+    @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..8e26ae4 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTestApi25.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTestApi25.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.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 +79,8 @@
      *  unlocked, and cannot remove the password even when FBE is unlocked.
      */
     @Override
+    @LargeTest
+    @FlakyTest(bugId = 141161690)
     public void testResetPasswordFbe() throws Exception {
         if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
             return;
@@ -94,4 +101,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..36aee24 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,46 @@
         }
         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();
+    }
+
+    @Override
+    @FlakyTest(bugId = 140932104)
+    public void testLockTaskAfterReboot() throws Exception {
+        super.testLockTaskAfterReboot();
+    }
+
+    @Override
+    @FlakyTest(bugId = 140932104)
+    public void testLockTaskAfterReboot_tryOpeningSettings() throws Exception {
+        super.testLockTaskAfterReboot_tryOpeningSettings();
+    }
+
+    @Override
+    @FlakyTest(bugId = 140932104)
+    public void testLockTask_exitIfNoLongerWhitelisted() throws Exception {
+        super.testLockTask_exitIfNoLongerWhitelisted();
+    }
 }
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/devicepolicy/src/com/android/cts/devicepolicy/metrics/DevicePolicyEventLogVerifier.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/DevicePolicyEventLogVerifier.java
index 264bd08..16e9e7f 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/DevicePolicyEventLogVerifier.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/DevicePolicyEventLogVerifier.java
@@ -19,6 +19,7 @@
 
 import com.android.os.AtomsProto.Atom;
 import com.android.os.StatsLog.EventMetricData;
+import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import java.util.ArrayList;
 import java.util.List;
@@ -37,12 +38,14 @@
 
     /**
      * Asserts that <code>expectedLogs</code> were logged as a result of executing
-     * <code>action</code>, in the same order.
+     * <code>action</code>, in the same order. Note that {@link Action#apply() } is always
+     * invoked on the <code>action</code> parameter, even if statsd logs are disabled.
      */
     public static void assertMetricsLogged(ITestDevice device, Action action,
             DevicePolicyEventWrapper... expectedLogs) throws Exception {
         final AtomMetricTester logVerifier = new AtomMetricTester(device);
         if (logVerifier.isStatsdDisabled()) {
+            action.apply();
             return;
         }
         try {
@@ -61,6 +64,11 @@
         }
     }
 
+    public static boolean isStatsdEnabled(ITestDevice device) throws DeviceNotAvailableException {
+        final AtomMetricTester logVerifier = new AtomMetricTester(device);
+        return !logVerifier.isStatsdDisabled();
+    }
+
     private static void assertExpectedMetricLogged(List<EventMetricData> data,
             DevicePolicyEventWrapper expectedLog) {
         final List<DevicePolicyEventWrapper> closestMatches = new ArrayList<>();
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/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/Android.bp b/hostsidetests/multiuser/Android.bp
index cc4bd81..ce653dd 100644
--- a/hostsidetests/multiuser/Android.bp
+++ b/hostsidetests/multiuser/Android.bp
@@ -21,6 +21,7 @@
         "cts-tradefed",
         "tradefed",
         "platform-test-annotations-host",
+	"compatibility-host-util",
     ],
     // tag this module as a cts test artifact
     test_suites: [
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/multiuser/src/android/host/multiuser/CreateUsersPermissionTest.java b/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersPermissionTest.java
index 41cbeab..fc385b1 100644
--- a/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersPermissionTest.java
+++ b/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersPermissionTest.java
@@ -17,6 +17,7 @@
 
 import static com.android.tradefed.log.LogUtil.CLog;
 
+import com.android.compatibility.common.util.CddTest;
 import com.android.ddmlib.Log;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -69,6 +70,7 @@
                 + "command output: " + output, isErrorOutput);
     }
 
+    @CddTest(requirement="9.5/A-1-3")
     @Test
     public void testCanCreateGuestUserWhenUserLimitReached() throws Exception {
         if (!isAutomotiveDevice()) {
diff --git a/hostsidetests/multiuser/src/android/host/multiuser/SecondaryUsersTest.java b/hostsidetests/multiuser/src/android/host/multiuser/SecondaryUsersTest.java
index 1f4ab36..d4a6ef2 100644
--- a/hostsidetests/multiuser/src/android/host/multiuser/SecondaryUsersTest.java
+++ b/hostsidetests/multiuser/src/android/host/multiuser/SecondaryUsersTest.java
@@ -15,6 +15,7 @@
  */
 package android.host.multiuser;
 
+import com.android.compatibility.common.util.CddTest;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
 import java.util.concurrent.TimeUnit;
@@ -31,6 +32,7 @@
 
     private static final long POLL_INTERVAL_MS = 100;
 
+    @CddTest(requirement="9.5/A-1-2")
     @Test
     public void testSwitchToSecondaryUserBeforeBootComplete() throws Exception {
         if (!isAutomotiveDevice() || !mSupportsMultiUser) {
@@ -58,4 +60,4 @@
         }
         Assert.assertTrue("Must switch to secondary user before boot complete", isUserSecondary);
     }
-}
\ No newline at end of file
+}
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/os/app/AndroidManifest.xml b/hostsidetests/os/app/AndroidManifest.xml
index fa9d9ae..88791ea 100755
--- a/hostsidetests/os/app/AndroidManifest.xml
+++ b/hostsidetests/os/app/AndroidManifest.xml
@@ -19,9 +19,16 @@
     package="android.os.app"
     android:targetSandboxVersion="2">
 
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+
     <application>
         <activity android:name=".TestNonExported"
                 android:exported="false" />
+
+        <service android:name=".TestFgService"
+                android:exported="true" />
+
     </application>
+
 </manifest>
 
diff --git a/hostsidetests/os/app/src/android/os/app/TestFgService.java b/hostsidetests/os/app/src/android/os/app/TestFgService.java
new file mode 100644
index 0000000..3548105
--- /dev/null
+++ b/hostsidetests/os/app/src/android/os/app/TestFgService.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 android.os.app;
+
+import android.app.Notification;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.Process;
+import android.util.Log;
+
+public class TestFgService extends Service {
+    private static final String TAG = "TestFgService";
+
+    // intentionally invalid resource configuration
+    private static final int NOTIFICATION_ID = 5038;
+    private static final String CHANNEL = "fgservice";
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Log.i(TAG, "onStartCommand() called");
+        Notification notification = new Notification.Builder(this, CHANNEL)
+                .setContentTitle("Foreground service")
+                .setContentText("Ongoing test app foreground service is live")
+                .setSmallIcon(NOTIFICATION_ID)
+                .build();
+
+        Log.i(TAG, "TestFgService starting foreground: pid=" + Process.myPid());
+        startForeground(NOTIFICATION_ID, notification);
+
+        return START_NOT_STICKY;
+    }
+}
\ No newline at end of file
diff --git a/hostsidetests/os/src/android/os/cts/OsHostTests.java b/hostsidetests/os/src/android/os/cts/OsHostTests.java
index c557c9e..8e3f5c5 100644
--- a/hostsidetests/os/src/android/os/cts/OsHostTests.java
+++ b/hostsidetests/os/src/android/os/cts/OsHostTests.java
@@ -22,14 +22,17 @@
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.CollectingOutputReceiver;
 import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.result.InputStreamSource;
 import com.android.tradefed.testtype.DeviceTestCase;
 import com.android.tradefed.testtype.IAbi;
 import com.android.tradefed.testtype.IAbiReceiver;
 import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.util.AbiUtils;
 
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.io.InputStreamReader;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Scanner;
@@ -38,12 +41,19 @@
 
 public class OsHostTests extends DeviceTestCase implements IBuildReceiver, IAbiReceiver {
     private static final String TEST_APP_PACKAGE = "android.os.app";
-    private static final String TEST_NON_EXPORTED_ACTIVITY_CLASS = "TestNonExported";
 
+    private static final String TEST_NON_EXPORTED_ACTIVITY_CLASS = "TestNonExported";
     private static final String START_NON_EXPORTED_ACTIVITY_COMMAND = String.format(
             "am start -n %s/%s.%s",
             TEST_APP_PACKAGE, TEST_APP_PACKAGE, TEST_NON_EXPORTED_ACTIVITY_CLASS);
 
+    private static final String TEST_FG_SERVICE_CLASS = "TestFgService";
+    private static final String START_FG_SERVICE_COMMAND = String.format(
+            "am start-foreground-service -n %s/%s.%s",
+            TEST_APP_PACKAGE, TEST_APP_PACKAGE, TEST_FG_SERVICE_CLASS);
+    private static final String FILTER_FG_SERVICE_REGEXP =
+            "TestFgService starting foreground: pid=([0-9]*)";
+
     // Testing the intent filter verification mechanism
     private static final String HOST_VERIFICATION_APK = "CtsHostLinkVerificationApp.apk";
     private static final String HOST_VERIFICATION_PKG = "com.android.cts.openlinksskeleton";
@@ -104,6 +114,40 @@
         }
     }
 
+    /**
+     * Test behavior of malformed Notifications w.r.t. foreground services
+     * @throws Exception
+     */
+    @AppModeFull(reason = "Instant apps may not start foreground services")
+    public void testForegroundServiceBadNotification() throws Exception {
+        final Pattern pattern = Pattern.compile(FILTER_FG_SERVICE_REGEXP);
+
+        mDevice.clearLogcat();
+        mDevice.executeShellCommand(START_FG_SERVICE_COMMAND);
+        Thread.sleep(2500);
+
+        String pid = null;
+        try (InputStreamSource logSource = mDevice.getLogcat()) {
+            InputStreamReader streamReader = new InputStreamReader(logSource.createInputStream());
+            BufferedReader logReader = new BufferedReader(streamReader);
+
+            String line;
+            while ((line = logReader.readLine()) != null) {
+                Matcher matcher = pattern.matcher(line);
+                if (matcher.find()) {
+                    pid = matcher.group(1);
+                    break;
+                }
+            }
+        }
+        assertTrue("Didn't find test service statement in logcat", pid != null);
+
+        final String procStr = "/proc/" + pid;
+        final String lsOut = mDevice.executeShellCommand("ls -d " + procStr).trim();
+        assertTrue("Looking for nonexistence of service process " + pid,
+                lsOut.contains("No such file"));
+    }
+
     public void testIntentFilterHostValidation() throws Exception {
         String line = null;
         try {
diff --git a/hostsidetests/rollback/Android.bp b/hostsidetests/rollback/Android.bp
index c3dbe18..7c323b2 100644
--- a/hostsidetests/rollback/Android.bp
+++ b/hostsidetests/rollback/Android.bp
@@ -17,19 +17,29 @@
     defaults: ["cts_defaults"],
     srcs: ["src/**/*.java"],
     libs: ["cts-tradefed", "tradefed", "truth-prebuilt"],
-    data: [":CtsRollbackManagerHostTestHelperApp"],
+    data: [":CtsRollbackManagerHostTestHelperApp", ":CtsRollbackManagerHostTestHelperApp2"],
     test_suites: ["cts", "general-tests"],
 }
 
 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:  [
+        ":ApexKeyRotationTestV2_SignedBobRot",
         ":StagedInstallTestApexV2",
         ":StagedInstallTestApexV3",
     ],
     sdk_version: "test_current",
     test_suites: ["device-tests"],
 }
+
+android_test_helper_app {
+    name: "CtsRollbackManagerHostTestHelperApp2",
+    srcs:  ["app2/src/**/*.java"],
+    static_libs: ["androidx.test.rules", "cts-rollback-lib", "cts-install-lib"],
+    manifest : "app2/AndroidManifest.xml",
+    sdk_version: "test_current",
+    test_suites: ["device-tests"],
+}
diff --git a/hostsidetests/rollback/AndroidTest.xml b/hostsidetests/rollback/AndroidTest.xml
index 9f0ae07..23e362f 100644
--- a/hostsidetests/rollback/AndroidTest.xml
+++ b/hostsidetests/rollback/AndroidTest.xml
@@ -21,6 +21,7 @@
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsRollbackManagerHostTestHelperApp.apk" />
+        <option name="test-file-name" value="CtsRollbackManagerHostTestHelperApp2.apk" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.HostTest" >
         <option name="class" value="com.android.cts.rollback.host.RollbackManagerHostTest" />
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..5cdfe86 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;
@@ -43,14 +42,16 @@
  */
 @RunWith(JUnit4.class)
 public class HostTestHelper {
+    private static final TestApp Apex2SignedBobRot = new TestApp(
+            "Apex2SignedBobRot", TestApp.Apex, 2, /*isApex*/true,
+            "com.android.apex.cts.shim.v2_signed_bob_rot.apex");
 
     /**
      * Adopts common permissions needed to test rollbacks.
      */
     @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 +62,7 @@
      */
     @After
     public void teardown() throws InterruptedException, IOException {
-        InstrumentationRegistry.getInstrumentation().getUiAutomation()
-                .dropShellPermissionIdentity();
+        InstallUtils.dropShellPermissionIdentity();
     }
 
 
@@ -72,7 +72,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 +88,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 +108,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 +121,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 +136,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 +154,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 +167,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 +184,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 +197,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 +206,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 +251,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 +266,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 +280,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 +302,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 +316,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 +341,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 +355,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 +369,50 @@
      */
     @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();
+    }
+
+    /**
+     * Test rollback with key downgrade for apex only
+     */
+    @Test
+    public void testApexKeyRotation_EnableRollback() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+        Install.single(Apex2SignedBobRot).setStaged().setEnableRollback().commit();
+    }
+
+    @Test
+    public void testApexKeyRotation_CommitRollback() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+        RollbackInfo available = RollbackUtils.getAvailableRollback(TestApp.Apex);
+        assertThat(available).isStaged();
+        assertThat(available).packagesContainsExactly(
+                Rollback.from(Apex2SignedBobRot).to(TestApp.Apex1));
+
+        RollbackUtils.rollback(available.getRollbackId(), Apex2SignedBobRot);
+        RollbackInfo committed = RollbackUtils.getCommittedRollbackById(available.getRollbackId());
+        assertThat(committed).isNotNull();
+        assertThat(committed).isStaged();
+        assertThat(committed).packagesContainsExactly(
+                Rollback.from(Apex2SignedBobRot).to(TestApp.Apex1));
+        assertThat(committed).causePackagesContainsExactly(Apex2SignedBobRot);
+        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 testApexKeyRotation_CofirmRollback() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+    }
+
+    @Test
+    public void testInstallTestAppA_EnableRollback() throws Exception {
+        Install.single(TestApp.A1).commit();
+        Install.single(TestApp.A2).setEnableRollback().commit();
     }
 }
diff --git a/hostsidetests/rollback/app2/AndroidManifest.xml b/hostsidetests/rollback/app2/AndroidManifest.xml
new file mode 100644
index 0000000..be6d483
--- /dev/null
+++ b/hostsidetests/rollback/app2/AndroidManifest.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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.cts.rollback.host.app2" >
+
+    <application>
+        <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
+                  android:exported="true" />
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.cts.rollback.host.app2"
+                     android:label="Helper for CTS host tests of RollbackManager"/>
+
+</manifest>
diff --git a/hostsidetests/rollback/app2/src/com/android/cts/rollback/host/app2/HostTestHelper.java b/hostsidetests/rollback/app2/src/com/android/cts/rollback/host/app2/HostTestHelper.java
new file mode 100644
index 0000000..56e9251
--- /dev/null
+++ b/hostsidetests/rollback/app2/src/com/android/cts/rollback/host/app2/HostTestHelper.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.rollback.host.app2;
+
+import static com.android.cts.rollback.lib.RollbackInfoSubject.assertThat;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.Manifest;
+import android.content.rollback.RollbackInfo;
+
+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.RollbackUtils;
+
+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.IOException;
+
+/**
+ * On-device helper test methods used for host-driven rollback tests.
+ */
+@RunWith(JUnit4.class)
+public class HostTestHelper {
+    /**
+     * Adopts common permissions needed to test rollbacks.
+     */
+    @Before
+    public void setup() throws InterruptedException, IOException {
+        InstallUtils.adoptShellPermissionIdentity(
+                    Manifest.permission.INSTALL_PACKAGES,
+                    Manifest.permission.DELETE_PACKAGES,
+                    Manifest.permission.TEST_MANAGE_ROLLBACKS);
+    }
+
+    /**
+     * Drops adopted shell permissions.
+     */
+    @After
+    public void teardown() throws InterruptedException, IOException {
+        InstallUtils.dropShellPermissionIdentity();
+    }
+
+    @Test
+    public void testRollbackTestAppA() throws Exception {
+        RollbackInfo rollbackA = RollbackUtils.waitForAvailableRollback(TestApp.A);
+        assertThat(rollbackA).packagesContainsExactly(Rollback.from(TestApp.A2).to(TestApp.A1));
+        RollbackUtils.rollback(rollbackA.getRollbackId());
+        // TODO(b/127452064): fix the rollback bug and enable the assertion
+        // Rollback should fail since TestApp.A is installed by another installer.
+        // assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+    }
+}
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..41350d0 100644
--- a/hostsidetests/rollback/src/com/android/cts/rollback/host/RollbackManagerHostTest.java
+++ b/hostsidetests/rollback/src/com/android/cts/rollback/host/RollbackManagerHostTest.java
@@ -20,6 +20,7 @@
 
 import static org.junit.Assume.assumeTrue;
 
+import com.android.ddmlib.Log;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -35,8 +36,10 @@
 @RunWith(DeviceJUnit4ClassRunner.class)
 public class RollbackManagerHostTest extends BaseHostJUnit4Test {
 
+    private static final String TAG = "RollbackManagerHostTest";
+
     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.
@@ -51,6 +54,16 @@
     }
 
     /**
+     * Runs the helper app test method on device targeted for
+     * com.android.cts.rollback.host.app2.HostTestHelper.
+     */
+    private void run2(String method) throws Exception {
+        assertThat(runDeviceTests("com.android.cts.rollback.host.app2",
+                "com.android.cts.rollback.host.app2.HostTestHelper",
+                method)).isTrue();
+    }
+
+    /**
      * Return {@code true} if and only if device supports updating apex.
      */
     private boolean isApexUpdateSupported() throws Exception {
@@ -68,17 +81,16 @@
             // 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);
-        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(b/140813980): use ApexInfo.sourceDir to decide whenever to issue an uninstall.
+            Log.w(TAG, "Failed to uninstall shim APEX : " + errorMessage);
         }
-        getDevice().reboot();
         assertThat(getShimApex().versionCode).isEqualTo(1L);
     }
 
@@ -133,6 +145,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
@@ -163,4 +189,27 @@
         run("testApexRollbackExpirationConfirmExpiration");
     }
 
+    /**
+     * Tests staged rollbacks involving apex with rotated keys.
+     */
+    @Test
+    public void testApexKeyRotationStagedRollback() throws Exception {
+        assumeTrue("Device does not support updating APEX", isApexUpdateSupported());
+
+        run("testApexKeyRotation_EnableRollback");
+        getDevice().reboot();
+        run("testApexKeyRotation_CommitRollback");
+        getDevice().reboot();
+        run("testApexKeyRotation_CofirmRollback");
+    }
+
+    /**
+     * Tests installer B can't rollback a package installed by A.
+     */
+    @Test
+    public void testApkRollbackByAnotherInstaller() throws Exception {
+        run("testInstallTestAppA_EnableRollback");
+        run2("testRollbackTestAppA");
+    }
+
 }
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/securitybulletin/securityPatch/CVE-2016-3746/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3746/poc.cpp
index 7b67095..1e55834 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2016-3746/poc.cpp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3746/poc.cpp
@@ -80,7 +80,7 @@
   sp<MemoryDealer> dealerIn = new MemoryDealer(inSize);
   IOMX::buffer_id inBufferId = 0;
   memory = dealerIn->allocate(inSize);
-  if (memory.get() == nullptr || memory->pointer() == nullptr) {
+  if (memory.get() == nullptr || memory->unsecurePointer() == nullptr) {
     ALOGE("memory allocate failed for port index 0, err: %d", err);
     mOMXNode->freeNode();
     client.disconnect();
@@ -93,7 +93,7 @@
    */
 
   OMXBuffer omxInBuf(memory);
-  memset(memory->pointer(), 0xCF, inSize);
+  memset(memory->unsecurePointer(), 0xCF, inSize);
   err = mOMXNode->useBuffer(0, omxInBuf, &inBufferId);
   ALOGI("useBuffer, port index 0, err: %d", err);
 
@@ -106,7 +106,7 @@
   sp<MemoryDealer> dealerOut = new MemoryDealer(outSize);
   IOMX::buffer_id outBufferId = 0;
   memory = dealerOut->allocate(outSize);
-  if (memory.get() == nullptr || memory->pointer() == nullptr) {
+  if (memory.get() == nullptr || memory->unsecurePointer() == nullptr) {
     ALOGE("memory allocate failed for port index 1, err: %d", err);
     mOMXNode->freeNode();
     client.disconnect();
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 49f7f87..b3c2bfa 100644
--- a/hostsidetests/stagedinstall/Android.bp
+++ b/hostsidetests/stagedinstall/Android.bp
@@ -45,54 +45,35 @@
     manifest : "app/AndroidManifest.xml",
 
     java_resources:  [
+        ":ApexKeyRotationTestV2_SignedBob",
+        ":ApexKeyRotationTestV2_SignedBobRot",
+        ":ApexKeyRotationTestV2_SignedEve",
+        ":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"],
@@ -101,6 +82,48 @@
 }
 
 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: "ApexKeyRotationTestV2_SignedEve",
+    src: "testdata/apex/com.android.apex.cts.shim.v2_signed_eve.apex",
+    filename: "com.android.apex.cts.shim.v2_signed_eve.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",
@@ -155,3 +178,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..748e45e 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,29 @@
     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 Apex2SignedEve = new TestApp(
+            "Apex2SignedEve", SHIM_PACKAGE_NAME, 2, /*isApex*/true,
+            "com.android.apex.cts.shim.v2_signed_eve.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 +158,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 +180,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 +207,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 +239,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 +249,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 +260,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 +270,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 +298,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 +318,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 +326,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 +347,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 +361,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 +421,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 +431,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 +443,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 +497,29 @@
     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 testInstallV3SignedBobApex_Commit() throws Exception {
+        int sessionId = stageSingleApk(Apex2SignedBobRot).assertSuccessful().getSessionId();
+        waitForIsReadyBroadcast(sessionId);
+        assertSessionReady(sessionId);
+        storeSessionId(sessionId);
+    }
+
+    @Test
+    public void testInstallV3SignedBobApex_VerifyPostReboot() throws Exception {
+        int sessionId = retrieveLastSessionId();
+        assertSessionApplied(sessionId);
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
     }
 
     @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 +533,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 +552,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 +573,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 +655,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 +678,150 @@
         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 {
+        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 {
+        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 {
+        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();
+    }
+
+    // Key downgrade should fail if new key is not ancestor of current key
+    @Test
+    public void testKeyDowngradeFailIfMismatch()
+            throws Exception {
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+        int sessionId = stageDowngradeSingleApk(Apex2SignedEve).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");
+    }
+
+    @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 +838,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 +935,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 +1006,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..2f421c3 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,12 +285,30 @@
         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();
         runPhase("testInstallV3Apex_VerifyPostReboot");
     }
 
+    private void installV3SignedBobApex() throws Exception {
+        runPhase("testInstallV3SignedBobApex_Commit");
+        getDevice().reboot();
+        runPhase("testInstallV3SignedBobApex_VerifyPostReboot");
+    }
+
     @Test
     public void testFailsInvalidApexInstall() throws Exception {
         assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
@@ -274,16 +316,103 @@
         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 testKeyDowngradeFailIfMismatch() throws Exception {
+        assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+        installV3SignedBobApex();
+        runPhase("testKeyDowngradeFailIfMismatch");
+    }
+
+    @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 +426,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..efa3d87
--- /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..6f3bc3c 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..f6bb67a 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..a99d938 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..117cbda
--- /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..72d22e8
--- /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..bdca0e5
--- /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_signed_eve.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_signed_eve.apex
new file mode 100644
index 0000000..2cf3d09
--- /dev/null
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_signed_eve.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..3c5aad1 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..a5512ef 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..21b282d 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..d3575f4 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..2a68a26
--- /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..abd87a7
--- /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..b11deee 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/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
index c679341..fc1b57e 100644
--- a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
@@ -18,7 +18,7 @@
 
 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
 
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.accounts.Account;
 import android.accounts.AccountManager;
@@ -325,7 +325,7 @@
 
         Context context = InstrumentationRegistry.getContext();
         JobScheduler js = context.getSystemService(JobScheduler.class);
-        assertTrue("JobScheduler service not available", js != null);
+        assertWithMessage("JobScheduler service not available").that(js).isNotNull();
 
         JobInfo.Builder builder = new JobInfo.Builder(1, name);
         builder.setOverrideDeadline(0);
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/Checkers.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/Checkers.java
index 1db1c0a..3728cef 100644
--- a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/Checkers.java
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/Checkers.java
@@ -16,7 +16,7 @@
 
 package com.android.server.cts.device.statsd;
 
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
 
 import android.net.wifi.WifiManager;
 import android.os.Vibrator;
@@ -34,12 +34,12 @@
     @Test
     public void checkVibratorSupported() {
         Vibrator v = InstrumentationRegistry.getContext().getSystemService(Vibrator.class);
-        assertTrue(v.hasVibrator());
+        assertThat(v.hasVibrator()).isTrue();
     }
 
     @Test
     public void checkWifiEnhancedPowerReportingSupported() {
         WifiManager wm = InstrumentationRegistry.getContext().getSystemService(WifiManager.class);
-        assertTrue(wm.isEnhancedPowerReportingSupported());
+        assertThat(wm.isEnhancedPowerReportingSupported()).isTrue();
     }
 }
diff --git a/hostsidetests/statsd/src/android/cts/statsd/alarm/AlarmTests.java b/hostsidetests/statsd/src/android/cts/statsd/alarm/AlarmTests.java
index 4983d06..00930cd 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/alarm/AlarmTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/alarm/AlarmTests.java
@@ -15,6 +15,8 @@
  */
 package android.cts.statsd.alarm;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import android.cts.statsd.atom.AtomTestCase;
 
 import com.android.internal.os.StatsdConfigProto;
@@ -59,7 +61,7 @@
         String markTime = getCurrentLogcatDate();
         Thread.sleep(9_000);
 
-        if (INCIDENTD_TESTS_ENABLED) assertTrue("No incident", didIncidentdFireSince(markTime));
+        if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isTrue();
     }
 
 
diff --git a/hostsidetests/statsd/src/android/cts/statsd/alert/AnomalyDetectionTests.java b/hostsidetests/statsd/src/android/cts/statsd/alert/AnomalyDetectionTests.java
index 3109138..5e9eac6 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/alert/AnomalyDetectionTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/alert/AnomalyDetectionTests.java
@@ -15,6 +15,9 @@
  */
 package android.cts.statsd.alert;
 
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
 import android.cts.statsd.atom.AtomTestCase;
 
 import com.android.internal.os.StatsdConfigProto;
@@ -94,29 +97,28 @@
         // count(label=6) -> 1 (not an anomaly, since not "greater than 2")
         doAppBreadcrumbReportedStart(6);
         Thread.sleep(500);
-        assertEquals("Premature anomaly", 0, getEventMetricDataList().size());
-        if (INCIDENTD_TESTS_ENABLED) assertFalse("Incident", didIncidentdFireSince(markTime));
+        assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
+        if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isFalse();
 
         // count(label=6) -> 2 (not an anomaly, since not "greater than 2")
         doAppBreadcrumbReportedStart(6);
         Thread.sleep(500);
-        assertEquals("Premature anomaly", 0, getEventMetricDataList().size());
-        if (INCIDENTD_TESTS_ENABLED) assertFalse("Incident", didIncidentdFireSince(markTime));
+        assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
+        if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isFalse();
 
         // count(label=12) -> 1 (not an anomaly, since not "greater than 2")
         doAppBreadcrumbReportedStart(12);
         Thread.sleep(1000);
-        assertEquals("Premature anomaly", 0, getEventMetricDataList().size());
-        if (INCIDENTD_TESTS_ENABLED) assertFalse("Incident", didIncidentdFireSince(markTime));
+        assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
+        if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isFalse();
 
         doAppBreadcrumbReportedStart(6); // count(label=6) -> 3 (anomaly, since "greater than 2"!)
         Thread.sleep(WAIT_AFTER_BREADCRUMB_MS);
 
         List<EventMetricData> data = getEventMetricDataList();
-        assertEquals("Expected 1 anomaly", 1, data.size());
-        AnomalyDetected a = data.get(0).getAtom().getAnomalyDetected();
-        assertEquals("Wrong alert_id", ALERT_ID, a.getAlertId());
-        if (INCIDENTD_TESTS_ENABLED) assertTrue("No incident", didIncidentdFireSince(markTime));
+        assertWithMessage("Expected anomaly").that(data).hasSize(1);
+        assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID);
+        if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isTrue();
     }
 
     // Tests that anomaly detection for duration works.
@@ -149,18 +151,18 @@
         String markTime = getCurrentLogcatDate();
         doAppBreadcrumbReportedStart(1);
         Thread.sleep(6_000);  // Recorded duration at end: 6s
-        assertEquals("Premature anomaly,", 0, getEventMetricDataList().size());
+        assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
 
         doAppBreadcrumbReportedStop(1);
         Thread.sleep(4_000);  // Recorded duration at end: 6s
-        assertEquals("Premature anomaly,", 0, getEventMetricDataList().size());
+        assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
 
         // Test that alarm does fire when it is supposed to (after 4s, plus up to 5s alarm delay).
         doAppBreadcrumbReportedStart(1);
         Thread.sleep(9_000);  // Recorded duration at end: 13s
         List<EventMetricData> data = getEventMetricDataList();
-        assertEquals("Expected an anomaly,", 1, data.size());
-        assertEquals(ALERT_ID, data.get(0).getAtom().getAnomalyDetected().getAlertId());
+        assertWithMessage("Expected anomaly").that(data).hasSize(1);
+        assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID);
 
         // Now test that the refractory period is obeyed.
         markTime = getCurrentLogcatDate();
@@ -168,7 +170,7 @@
         doAppBreadcrumbReportedStart(1);
         Thread.sleep(3_000);  // Recorded duration at end: 13s
         // NB: the previous getEventMetricDataList also removes the report, so size is back to 0.
-        assertEquals("Expected only 1 anomaly,", 0, getEventMetricDataList().size());
+        assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
 
         // Test that detection works again after refractory period finishes.
         doAppBreadcrumbReportedStop(1);
@@ -177,9 +179,9 @@
         Thread.sleep(15_000);  // Recorded duration at end: 15s
         // We can do an incidentd test now that all the timing issues are done.
         data = getEventMetricDataList();
-        assertEquals("Expected another anomaly,", 1, data.size());
-        assertEquals(ALERT_ID, data.get(0).getAtom().getAnomalyDetected().getAlertId());
-        if (INCIDENTD_TESTS_ENABLED) assertTrue("No incident", didIncidentdFireSince(markTime));
+        assertWithMessage("Expected anomaly").that(data).hasSize(1);
+        assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID);
+        if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isTrue();
 
         doAppBreadcrumbReportedStop(1);
     }
@@ -212,7 +214,7 @@
         Thread.sleep(5_000);
         doAppBreadcrumbReportedStop(1);
         Thread.sleep(2_000);
-        assertEquals("Premature anomaly,", 0, getEventMetricDataList().size());
+        assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
 
         // Test that alarm does fire when it is supposed to.
         // The anomaly occurs in 1s, but alarms won't fire that quickly.
@@ -228,9 +230,8 @@
             // Although we expect that the alarm won't fire, we certainly cannot demand that.
             CLog.w(TAG, "The anomaly was detected twice. Presumably the alarm did manage to fire.");
         }
-        assertTrue("Expected 1 (or possibly 2) anomalies, instead of " + data.size(),
-                1 == data.size() || 2 == data.size());
-        assertEquals(ALERT_ID, data.get(0).getAtom().getAnomalyDetected().getAlertId());
+        assertThat(data.size()).isAnyOf(1, 2);
+        assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID);
     }
 
     // Tests that anomaly detection for value works.
@@ -256,17 +257,16 @@
         String markTime = getCurrentLogcatDate();
         doAppBreadcrumbReportedStart(6); // value = 6, which is NOT > trigger
         Thread.sleep(WAIT_AFTER_BREADCRUMB_MS);
-        assertEquals("Premature anomaly", 0, getEventMetricDataList().size());
-        if (INCIDENTD_TESTS_ENABLED) assertFalse("Incident", didIncidentdFireSince(markTime));
+        assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
+        if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isFalse();
 
         doAppBreadcrumbReportedStart(14); // value = 14 > trigger
         Thread.sleep(WAIT_AFTER_BREADCRUMB_MS);
 
         List<EventMetricData> data = getEventMetricDataList();
-        assertEquals("Expected 1 anomaly", 1, data.size());
-        AnomalyDetected a = data.get(0).getAtom().getAnomalyDetected();
-        assertEquals("Wrong alert_id", ALERT_ID, a.getAlertId());
-        if (INCIDENTD_TESTS_ENABLED) assertTrue("No incident", didIncidentdFireSince(markTime));
+        assertWithMessage("Expected anomaly").that(data).hasSize(1);
+        assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID);
+        if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isTrue();
     }
 
     // Test that anomaly detection integrates with perfetto properly.
@@ -306,17 +306,28 @@
         String markTime = getCurrentLogcatDate();
         doAppBreadcrumbReportedStart(6); // value = 6, which is NOT > trigger
         Thread.sleep(WAIT_AFTER_BREADCRUMB_MS);
-        assertEquals("Premature anomaly", 0, getEventMetricDataList().size());
-        if (PERFETTO_TESTS_ENABLED) assertFalse(isSystemTracingEnabled());
+        assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
+        if (PERFETTO_TESTS_ENABLED) assertThat(isSystemTracingEnabled()).isFalse();
 
         doAppBreadcrumbReportedStart(14); // value = 14 > trigger
         Thread.sleep(WAIT_AFTER_BREADCRUMB_MS);
 
         List<EventMetricData> data = getEventMetricDataList();
-        assertEquals("Expected 1 anomaly", 1, data.size());
-        AnomalyDetected a = data.get(0).getAtom().getAnomalyDetected();
-        assertEquals("Wrong alert_id", ALERT_ID, a.getAlertId());
-        if (PERFETTO_TESTS_ENABLED) assertTrue(isSystemTracingEnabled());
+        assertWithMessage("Expected anomaly").that(data).hasSize(1);
+        assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID);
+
+        // Pool a few times to allow for statsd <-> traced <-> traced_probes communication to happen.
+        if (PERFETTO_TESTS_ENABLED) {
+                boolean tracingEnabled = false;
+                for (int i = 0; i < 5; i++) {
+                        if (isSystemTracingEnabled()) {
+                                tracingEnabled = true;
+                                break;
+                        }
+                        Thread.sleep(1000);
+                }
+                assertThat(tracingEnabled).isTrue();
+        }
     }
 
     // Tests that anomaly detection for gauge works.
@@ -343,18 +354,17 @@
         String markTime = getCurrentLogcatDate();
         doAppBreadcrumbReportedStart(6); // gauge = 6, which is NOT > trigger
         Thread.sleep(Math.max(WAIT_AFTER_BREADCRUMB_MS, 1_100)); // Must be >1s to push next bucket.
-        assertEquals("Premature anomaly", 0, getEventMetricDataList().size());
-        if (INCIDENTD_TESTS_ENABLED) assertFalse("Incident", didIncidentdFireSince(markTime));
+        assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
+        if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isFalse();
 
         // We waited for >1s above, so we are now in the next bucket (which is essential).
         doAppBreadcrumbReportedStart(14); // gauge = 14 > trigger
         Thread.sleep(WAIT_AFTER_BREADCRUMB_MS);
 
         List<EventMetricData> data = getEventMetricDataList();
-        assertEquals("Expected 1 anomaly", 1, data.size());
-        AnomalyDetected a = data.get(0).getAtom().getAnomalyDetected();
-        assertEquals("Wrong alert_id", ALERT_ID, a.getAlertId());
-        if (INCIDENTD_TESTS_ENABLED) assertTrue("No incident", didIncidentdFireSince(markTime));
+        assertWithMessage("Expected anomaly").that(data).hasSize(1);
+        assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID);
+        if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isTrue();
     }
 
     // Test that anomaly detection for pulled metrics work.
@@ -399,9 +409,8 @@
 
         List<EventMetricData> data = getEventMetricDataList();
         // There will likely be many anomalies (one for each dimension). There must be at least one.
-        assertTrue("Expected >=1 anomaly", data.size() >= 1);
-        AnomalyDetected a = data.get(0).getAtom().getAnomalyDetected();
-        assertEquals("Wrong alert_id", ALERT_ID, a.getAlertId());
+        assertThat(data.size()).isAtLeast(1);
+        assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID);
     }
 
 
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
index 5b5711c..4ec6142 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
@@ -18,6 +18,9 @@
 import static android.cts.statsd.atom.DeviceAtomTestCase.DEVICE_SIDE_TEST_APK;
 import static android.cts.statsd.atom.DeviceAtomTestCase.DEVICE_SIDE_TEST_PACKAGE;
 
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
 import android.os.BatteryStatsProto;
 import android.os.StatsDataDumpProto;
 import android.service.battery.BatteryServiceDumpProto;
@@ -54,6 +57,7 @@
 import com.android.tradefed.util.CommandResult;
 import com.android.tradefed.util.CommandStatus;
 
+import com.google.common.collect.Range;
 import com.google.common.io.Files;
 import com.google.protobuf.ByteString;
 
@@ -255,7 +259,7 @@
      */
     protected List<EventMetricData> getEventMetricDataList(ConfigMetricsReportList reportList)
             throws Exception {
-        assertTrue("Expected one report", reportList.getReportsCount() == 1);
+        assertThat(reportList.getReportsCount()).isEqualTo(1);
         ConfigMetricsReport report = reportList.getReports(0);
 
         List<EventMetricData> data = new ArrayList<>();
@@ -273,15 +277,16 @@
 
     protected List<Atom> getGaugeMetricDataList() throws Exception {
         ConfigMetricsReportList reportList = getReportList();
-        assertTrue("Expected one report.", reportList.getReportsCount() == 1);
+        assertThat(reportList.getReportsCount()).isEqualTo(1);
+
         // only config
         ConfigMetricsReport report = reportList.getReports(0);
-        assertEquals("Expected one metric in the report.", 1, report.getMetricsCount());
+        assertThat(report.getMetricsCount()).isEqualTo(1);
 
         List<Atom> data = new ArrayList<>();
         for (GaugeMetricData gaugeMetricData :
                 report.getMetrics(0).getGaugeMetrics().getDataList()) {
-            assertTrue("Expected one bucket.", gaugeMetricData.getBucketInfoCount() == 1);
+            assertThat(gaugeMetricData.getBucketInfoCount()).isEqualTo(1);
             for (Atom atom : gaugeMetricData.getBucketInfo(0).getAtomList()) {
                 data.add(atom);
             }
@@ -300,7 +305,7 @@
      */
     protected List<DurationMetricData> getDurationMetricDataList() throws Exception {
         ConfigMetricsReportList reportList = getReportList();
-        assertTrue("Expected one report", reportList.getReportsCount() == 1);
+        assertThat(reportList.getReportsCount()).isEqualTo(1);
         ConfigMetricsReport report = reportList.getReports(0);
 
         List<DurationMetricData> data = new ArrayList<>();
@@ -321,7 +326,7 @@
      */
     protected List<CountMetricData> getCountMetricDataList() throws Exception {
         ConfigMetricsReportList reportList = getReportList();
-        assertTrue("Expected one report", reportList.getReportsCount() == 1);
+        assertThat(reportList.getReportsCount()).isEqualTo(1);
         ConfigMetricsReport report = reportList.getReports(0);
 
         List<CountMetricData> data = new ArrayList<>();
@@ -342,7 +347,7 @@
      */
     protected List<ValueMetricData> getValueMetricDataList() throws Exception {
         ConfigMetricsReportList reportList = getReportList();
-        assertTrue("Expected one report", reportList.getReportsCount() == 1);
+        assertThat(reportList.getReportsCount()).isEqualTo(1);
         ConfigMetricsReport report = reportList.getReports(0);
 
         List<ValueMetricData> data = new ArrayList<>();
@@ -359,14 +364,14 @@
 
     protected StatsLogReport getStatsLogReport() throws Exception {
         ConfigMetricsReport report = getConfigMetricsReport();
-        assertTrue(report.hasUidMap());
-        assertEquals(1, report.getMetricsCount());
+        assertThat(report.hasUidMap()).isTrue();
+        assertThat(report.getMetricsCount()).isEqualTo(1);
         return report.getMetrics(0);
     }
 
     protected ConfigMetricsReport getConfigMetricsReport() throws Exception {
         ConfigMetricsReportList reportList = getReportList();
-        assertEquals(1, reportList.getReportsCount());
+        assertThat(reportList.getReportsCount()).isEqualTo(1);
         return reportList.getReports(0);
     }
 
@@ -624,7 +629,7 @@
             int wait, Function<Atom, Integer> getStateFromAtom) {
         // Sometimes, there are more events than there are states.
         // Eg: When the screen turns off, it may go into OFF and then DOZE immediately.
-        assertTrue("Too few states found (" + data.size() + ")", data.size() >= stateSets.size());
+        assertWithMessage("Too few states found").that(data.size()).isAtLeast(stateSets.size());
         int stateSetIndex = 0; // Tracks which state set we expect the data to be in.
         for (int dataIndex = 0; dataIndex < data.size(); dataIndex++) {
             Atom atom = data.get(dataIndex).getAtom();
@@ -641,19 +646,18 @@
                 LogUtil.CLog.i("Assert that the following atom at dataIndex=" + dataIndex + " is"
                         + " in stateSetIndex " + stateSetIndex + ":\n"
                         + data.get(dataIndex).getAtom().toString());
-                assertTrue("Missed first state", dataIndex != 0); // should not be on first data
-                assertTrue("Too many states (" + (stateSetIndex + 1) + ")",
-                        stateSetIndex < stateSets.size());
-                assertTrue("Is in wrong state (" + state + ")",
-                        stateSets.get(stateSetIndex).contains(state));
+                assertWithMessage("Missed first state").that(dataIndex).isNotEqualTo(0); 
+                assertWithMessage("Too many states").that(stateSetIndex)
+                    .isLessThan(stateSets.size());
+                assertWithMessage(String.format("Is in wrong state (%d)", state))
+                    .that(stateSets.get(stateSetIndex)).contains(state);
                 if (wait > 0) {
                     assertTimeDiffBetween(data.get(dataIndex - 1), data.get(dataIndex),
                             wait / 2, wait * 5);
                 }
             }
         }
-        assertTrue("Too few states (" + (stateSetIndex + 1) + ")",
-                stateSetIndex == stateSets.size() - 1);
+        assertWithMessage("Too few states").that(stateSetIndex).isEqualTo(stateSets.size() - 1);
     }
 
     /**
@@ -899,8 +903,8 @@
     public static void assertTimeDiffBetween(EventMetricData d0, EventMetricData d1,
             int minDiffMs, int maxDiffMs) {
         long diffMs = (d1.getElapsedTimestampNanos() - d0.getElapsedTimestampNanos()) / 1_000_000;
-        assertTrue("Illegal time difference (" + diffMs + "ms)", minDiffMs <= diffMs);
-        assertTrue("Illegal time difference (" + diffMs + "ms)", diffMs <= maxDiffMs);
+        assertWithMessage("Illegal time difference")
+            .that(diffMs).isIn(Range.closed((long) minDiffMs, (long) maxDiffMs));
     }
 
     protected String getCurrentLogcatDate() throws Exception {
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/BaseTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/atom/BaseTestCase.java
index 94d5fdc..b110fff 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/BaseTestCase.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/BaseTestCase.java
@@ -16,6 +16,9 @@
 
 package android.cts.statsd.atom;
 
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
 import android.cts.statsd.validation.ValidationTestUtil;
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
@@ -52,7 +55,7 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        assertNotNull(mCtsBuild);
+        assertThat(mCtsBuild).isNotNull();
     }
 
     @Override
@@ -104,7 +107,8 @@
         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
         final String result = getDevice().installPackage(
                 buildHelper.getTestFile(appFileName), true, grantPermissions);
-        assertNull("Failed to install " + appFileName + ": " + result, result);
+        assertWithMessage(String.format("Failed to install %s: %s", appFileName, result))
+            .that(result).isNull();
     }
 
     protected CompatibilityBuildHelper getBuildHelper() {
@@ -135,7 +139,7 @@
         }
 
         CollectingTestListener listener = new CollectingTestListener();
-        assertTrue(getDevice().runInstrumentationTests(testRunner, listener));
+        assertThat(getDevice().runInstrumentationTests(testRunner, listener)).isTrue();
 
         final TestRunResult result = listener.getCurrentRunResults();
         if (result.isRunFailure()) {
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/DeviceAtomTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/atom/DeviceAtomTestCase.java
index ec6291d..8394006 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/DeviceAtomTestCase.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/DeviceAtomTestCase.java
@@ -15,6 +15,9 @@
  */
 package android.cts.statsd.atom;
 
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
 import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
 import com.android.internal.os.StatsdConfigProto.MessageMatcher;
 import com.android.internal.os.StatsdConfigProto.Position;
@@ -80,9 +83,9 @@
         List<EventMetricData> data = doDeviceMethod(methodName, conf);
 
         if (demandExactlyTwo) {
-            assertEquals(2, data.size());
+            assertThat(data).hasSize(2);
         } else {
-            assertTrue("data.size() [" + data.size() + "] should be >= 2", data.size() >= 2);
+            assertThat(data.size()).isAtLeast(2);
         }
         assertTimeDiffBetween(data.get(0), data.get(1), minTimeDiffMs, maxTimeDiffMs);
         return data;
@@ -165,9 +168,9 @@
                 + currentUser + " " + DEVICE_SIDE_TEST_PACKAGE);
         String[] uidLineParts = uidLine.split(":");
         // 3rd entry is package uid
-        assertTrue(uidLineParts.length > 2);
+        assertThat(uidLineParts.length).isGreaterThan(2);
         int uid = Integer.parseInt(uidLineParts[2].trim());
-        assertTrue(uid > 10000);
+        assertThat(uid).isGreaterThan(10000);
         return uid;
     }
 
@@ -287,8 +290,10 @@
     protected void rebootDeviceAndWaitUntilReady() throws Exception {
         rebootDevice();
         // Wait for 2 mins.
-        assertTrue("Device failed to boot", getDevice().waitForBootComplete(120_000));
-        assertTrue("Stats service failed to start", waitForStatsServiceStart(60_000));
+        assertWithMessage("Device failed to boot")
+            .that(getDevice().waitForBootComplete(120_000)).isTrue();
+        assertWithMessage("Stats service failed to start")
+            .that(waitForStatsServiceStart(60_000)).isTrue();
         Thread.sleep(2_000);
     }
 
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
index c22fd8b..1bdaf7c 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
@@ -16,6 +16,7 @@
 package android.cts.statsd.atom;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.os.BatteryPluggedStateEnum;
 import android.os.BatteryStatusEnum;
@@ -32,6 +33,8 @@
 import com.android.os.StatsLog.ConfigMetricsReportList;
 import com.android.os.StatsLog.EventMetricData;
 
+import com.google.common.collect.Range;
+
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
@@ -349,11 +352,12 @@
 
         List<Atom> data = getGaugeMetricDataList();
 
-        assertTrue(data.size() > 0);
+        assertThat(data).isNotEmpty();
         Atom atom = data.get(0);
-        assertTrue(atom.getRemainingBatteryCapacity().hasChargeMicroAmpereHour());
+        assertThat(atom.getRemainingBatteryCapacity().hasChargeMicroAmpereHour()).isTrue();
         if (hasBattery()) {
-            assertTrue(atom.getRemainingBatteryCapacity().getChargeMicroAmpereHour() > 0);
+            assertThat(atom.getRemainingBatteryCapacity().getChargeMicroAmpereHour())
+                .isGreaterThan(0);
         }
     }
 
@@ -375,11 +379,11 @@
 
         List<Atom> data = getGaugeMetricDataList();
 
-        assertTrue(data.size() > 0);
+        assertThat(data).isNotEmpty();
         Atom atom = data.get(0);
-        assertTrue(atom.getFullBatteryCapacity().hasCapacityMicroAmpereHour());
+        assertThat(atom.getFullBatteryCapacity().hasCapacityMicroAmpereHour()).isTrue();
         if (hasBattery()) {
-            assertTrue(atom.getFullBatteryCapacity().getCapacityMicroAmpereHour() > 0);
+            assertThat(atom.getFullBatteryCapacity().getCapacityMicroAmpereHour()).isGreaterThan(0);
         }
     }
 
@@ -399,11 +403,11 @@
 
         List<Atom> data = getGaugeMetricDataList();
 
-        assertTrue(data.size() > 0);
+        assertThat(data).isNotEmpty();
         Atom atom = data.get(0);
-        assertTrue(atom.getBatteryVoltage().hasVoltageMillivolt());
+        assertThat(atom.getBatteryVoltage().hasVoltageMillivolt()).isTrue();
         if (hasBattery()) {
-            assertTrue(atom.getBatteryVoltage().getVoltageMillivolt() > 0);
+            assertThat(atom.getBatteryVoltage().getVoltageMillivolt()).isGreaterThan(0);
         }
     }
 
@@ -424,12 +428,11 @@
 
         List<Atom> data = getGaugeMetricDataList();
 
-        assertTrue(data.size() > 0);
+        assertThat(data).isNotEmpty();
         Atom atom = data.get(0);
-        assertTrue(atom.getBatteryLevel().hasBatteryLevel());
+        assertThat(atom.getBatteryLevel().hasBatteryLevel()).isTrue();
         if (hasBattery()) {
-            assertTrue(atom.getBatteryLevel().getBatteryLevel() > 0);
-            assertTrue(atom.getBatteryLevel().getBatteryLevel() <= 100);
+            assertThat(atom.getBatteryLevel().getBatteryLevel()).isIn(Range.openClosed(0, 100));
         }
     }
 
@@ -450,11 +453,11 @@
 
         List<Atom> data = getGaugeMetricDataList();
 
-        assertTrue(data.size() > 0);
+        assertThat(data).isNotEmpty();
         Atom atom = data.get(0);
-        assertTrue(atom.getBatteryCycleCount().hasCycleCount());
+        assertThat(atom.getBatteryCycleCount().hasCycleCount()).isTrue();
         if (hasBattery()) {
-            assertTrue(atom.getBatteryCycleCount().getCycleCount() >= 0);
+            assertThat(atom.getBatteryCycleCount().getCycleCount()).isAtLeast(0);
         }
     }
 
@@ -474,11 +477,11 @@
         List<Atom> data = getGaugeMetricDataList();
 
         Atom atom = data.get(0);
-        assertTrue(!atom.getKernelWakelock().getName().equals(""));
-        assertTrue(atom.getKernelWakelock().hasCount());
-        assertTrue(atom.getKernelWakelock().hasVersion());
-        assertTrue(atom.getKernelWakelock().getVersion() > 0);
-        assertTrue(atom.getKernelWakelock().hasTimeMicros());
+        assertThat(atom.getKernelWakelock().getName()).isNotEmpty();
+        assertThat(atom.getKernelWakelock().hasCount()).isTrue();
+        assertThat(atom.getKernelWakelock().hasVersion()).isTrue();
+        assertThat(atom.getKernelWakelock().getVersion()).isGreaterThan(0);
+        assertThat(atom.getKernelWakelock().hasTimeMicros()).isTrue();
     }
 
     // Returns true iff either |WAKE_LOCK_FILE| or |WAKE_SOURCES_FILE| exists.
@@ -510,12 +513,12 @@
         List<Atom> dataList = getGaugeMetricDataList();
 
         for (Atom atom: dataList) {
-            assertTrue(atom.getWifiActivityInfo().getTimestampMillis() > 0);
-            assertTrue(atom.getWifiActivityInfo().getStackState() >= 0);
-            assertTrue(atom.getWifiActivityInfo().getControllerIdleTimeMillis() > 0);
-            assertTrue(atom.getWifiActivityInfo().getControllerTxTimeMillis() >= 0);
-            assertTrue(atom.getWifiActivityInfo().getControllerRxTimeMillis() >= 0);
-            assertTrue(atom.getWifiActivityInfo().getControllerEnergyUsed() >= 0);
+            assertThat(atom.getWifiActivityInfo().getTimestampMillis()).isGreaterThan(0L);
+            assertThat(atom.getWifiActivityInfo().getStackState()).isAtLeast(0);
+            assertThat(atom.getWifiActivityInfo().getControllerIdleTimeMillis()).isGreaterThan(0L);
+            assertThat(atom.getWifiActivityInfo().getControllerTxTimeMillis()).isAtLeast(0L);
+            assertThat(atom.getWifiActivityInfo().getControllerRxTimeMillis()).isAtLeast(0L);
+            assertThat(atom.getWifiActivityInfo().getControllerEnergyUsed()).isAtLeast(0L);
         }
     }
 
@@ -533,16 +536,17 @@
         Thread.sleep(WAIT_TIME_LONG);
 
         List<Atom> data = getGaugeMetricDataList();
-        assertTrue(data.size() > 0);
+        assertThat(data).isNotEmpty();
         BuildInformation atom = data.get(0).getBuildInformation();
-        assertEquals(getProperty("ro.product.brand"),             atom.getBrand());
-        assertEquals(getProperty("ro.product.name"),              atom.getProduct());
-        assertEquals(getProperty("ro.product.device"),            atom.getDevice());
-        assertEquals(getProperty("ro.build.version.release"),     atom.getVersionRelease());
-        assertEquals(getProperty("ro.build.id"),                  atom.getId());
-        assertEquals(getProperty("ro.build.version.incremental"), atom.getVersionIncremental());
-        assertEquals(getProperty("ro.build.type"),                atom.getType());
-        assertEquals(getProperty("ro.build.tags"),                atom.getTags());
+        assertThat(getProperty("ro.product.brand")).isEqualTo(atom.getBrand());
+        assertThat(getProperty("ro.product.name")).isEqualTo(atom.getProduct());
+        assertThat(getProperty("ro.product.device")).isEqualTo(atom.getDevice());
+        assertThat(getProperty("ro.build.version.release")).isEqualTo(atom.getVersionRelease());
+        assertThat(getProperty("ro.build.id")).isEqualTo(atom.getId());
+        assertThat(getProperty("ro.build.version.incremental"))
+            .isEqualTo(atom.getVersionIncremental());
+        assertThat(getProperty("ro.build.type")).isEqualTo(atom.getType());
+        assertThat(getProperty("ro.build.tags")).isEqualTo(atom.getTags());
     }
 
     public void testOnDevicePowerMeasurement() throws Exception {
@@ -563,8 +567,9 @@
         List<Atom> dataList = getGaugeMetricDataList();
 
         for (Atom atom: dataList) {
-            assertTrue(atom.getOnDevicePowerMeasurement().getMeasurementTimestampMillis() >= 0);
-            assertTrue(atom.getOnDevicePowerMeasurement().getEnergyMicrowattSecs() >= 0);
+            assertThat(atom.getOnDevicePowerMeasurement().getMeasurementTimestampMillis())
+                .isAtLeast(0L);
+            assertThat(atom.getOnDevicePowerMeasurement().getEnergyMicrowattSecs()).isAtLeast(0L);
         }
     }
 
@@ -582,8 +587,8 @@
 
         List<EventMetricData> data = getEventMetricDataList();
         AppBreadcrumbReported atom = data.get(0).getAtom().getAppBreadcrumbReported();
-        assertTrue(atom.getLabel() == 1);
-        assertTrue(atom.getState().getNumber() == AppBreadcrumbReported.State.START_VALUE);
+        assertThat(atom.getLabel()).isEqualTo(1);
+        assertThat(atom.getState().getNumber()).isEqualTo(AppBreadcrumbReported.State.START_VALUE);
     }
 
     // Test dumpsys stats --proto.
@@ -600,7 +605,7 @@
 
         // Get the stats incident section.
         List<ConfigMetricsReportList> listList = getReportsFromStatsDataDumpProto();
-        assertTrue(listList.size() > 0);
+        assertThat(listList).isNotEmpty();
 
         // Extract the relevent report from the incident section.
         ConfigMetricsReportList ourList = null;
@@ -612,14 +617,14 @@
                 break;
             }
         }
-        assertNotNull("Could not find list for uid=" + hostUid
-                + " id=" + CONFIG_ID, ourList);
+        assertWithMessage(String.format("Could not find list for uid=%d id=%d", hostUid, CONFIG_ID))
+            .that(ourList).isNotNull();
 
         // Make sure that the report is correct.
         List<EventMetricData> data = getEventMetricDataList(ourList);
         AppBreadcrumbReported atom = data.get(0).getAtom().getAppBreadcrumbReported();
-        assertTrue(atom.getLabel() == 1);
-        assertTrue(atom.getState().getNumber() == AppBreadcrumbReported.State.START_VALUE);
+        assertThat(atom.getLabel()).isEqualTo(1);
+        assertThat(atom.getState().getNumber()).isEqualTo(AppBreadcrumbReported.State.START_VALUE);
     }
 
     public void testConnectivityStateChange() throws Exception {
@@ -656,6 +661,7 @@
                 foundConnectEvent = true;
             }
         }
-        assertTrue(foundConnectEvent && foundDisconnectEvent);
+        assertThat(foundConnectEvent).isTrue();
+        assertThat(foundDisconnectEvent).isTrue();
     }
 }
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java
index 5fecde5..070d3f4 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java
@@ -15,6 +15,8 @@
  */
 package android.cts.statsd.atom;
 
+import static com.google.common.truth.Truth.assertWithMessage;
+
 import android.app.ProcessStateEnum; // From enums.proto for atoms.proto's UidProcessStateChanged.
 
 import com.android.os.AtomsProto.Atom;
@@ -248,8 +250,8 @@
         if (statsdDisabled()) {
             return;
         }
-        assertFalse("UNKNOWN_TO_PROTO should not be a valid state",
-                ALL_STATES.contains(ProcessStateEnum.PROCESS_STATE_UNKNOWN_TO_PROTO_VALUE));
+        assertWithMessage("UNKNOWN_TO_PROTO should not be a valid state")
+            .that(ALL_STATES).doesNotContain(ProcessStateEnum.PROCESS_STATE_UNKNOWN_TO_PROTO_VALUE);
     }
 
     /** Returns the a set containing elements of a that are not elements of b. */
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index 045b4a1..e6d8e43 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -15,6 +15,9 @@
  */
 package android.cts.statsd.atom;
 
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
 import android.net.wifi.WifiModeEnum;
 import android.os.WakeLockLevelEnum;
 import android.server.ErrorSource;
@@ -41,10 +44,10 @@
 import com.android.os.AtomsProto.LooperStats;
 import com.android.os.AtomsProto.LmkKillOccurred;
 import com.android.os.AtomsProto.MediaCodecStateChanged;
-import com.android.os.AtomsProto.NativeProcessMemoryState;
 import com.android.os.AtomsProto.OverlayStateChanged;
 import com.android.os.AtomsProto.PictureInPictureStateChanged;
 import com.android.os.AtomsProto.ProcessMemoryHighWaterMark;
+import com.android.os.AtomsProto.ProcessMemorySnapshot;
 import com.android.os.AtomsProto.ProcessMemoryState;
 import com.android.os.AtomsProto.ScheduledJobStateChanged;
 import com.android.os.AtomsProto.SyncStateChanged;
@@ -58,6 +61,8 @@
 import com.android.os.StatsLog.EventMetricData;
 import com.android.tradefed.log.LogUtil;
 
+import com.google.common.collect.Range;
+
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
@@ -102,12 +107,12 @@
         // Sorted list of events in order in which they occurred.
         List<EventMetricData> data = getEventMetricDataList();
 
-        assertEquals(1, data.size());
-        assertTrue(data.get(0).getAtom().hasLmkKillOccurred());
+        assertThat(data).hasSize(1);
+        assertThat(data.get(0).getAtom().hasLmkKillOccurred()).isTrue();
         LmkKillOccurred atom = data.get(0).getAtom().getLmkKillOccurred();
-        assertEquals(getUid(), atom.getUid());
-        assertEquals(DEVICE_SIDE_TEST_PACKAGE, atom.getProcessName());
-        assertTrue(500 <= atom.getOomAdjScore());
+        assertThat(atom.getUid()).isEqualTo(getUid());
+        assertThat(atom.getProcessName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
+        assertThat(atom.getOomAdjScore()).isAtLeast(500);
     }
 
     public void testAppCrashOccurred() throws Exception {
@@ -125,11 +130,12 @@
         List<EventMetricData> data = getEventMetricDataList();
 
         AppCrashOccurred atom = data.get(0).getAtom().getAppCrashOccurred();
-        assertEquals("crash", atom.getEventType());
-        assertEquals(AppCrashOccurred.InstantApp.FALSE_VALUE, atom.getIsInstantApp().getNumber());
-        assertEquals(AppCrashOccurred.ForegroundState.FOREGROUND_VALUE,
-                atom.getForegroundState().getNumber());
-        assertEquals("com.android.server.cts.device.statsd", atom.getPackageName());
+        assertThat(atom.getEventType()).isEqualTo("crash");
+        assertThat(atom.getIsInstantApp().getNumber())
+            .isEqualTo(AppCrashOccurred.InstantApp.FALSE_VALUE);
+        assertThat(atom.getForegroundState().getNumber())
+            .isEqualTo(AppCrashOccurred.ForegroundState.FOREGROUND_VALUE);
+        assertThat(atom.getPackageName()).isEqualTo("com.android.server.cts.device.statsd");
     }
 
     public void testAppStartOccurred() throws Exception {
@@ -147,12 +153,12 @@
         List<EventMetricData> data = getEventMetricDataList();
 
         AppStartOccurred atom = data.get(0).getAtom().getAppStartOccurred();
-        assertEquals("com.android.server.cts.device.statsd", atom.getPkgName());
-        assertEquals("com.android.server.cts.device.statsd.StatsdCtsForegroundActivity",
-                atom.getActivityName());
-        assertFalse(atom.getIsInstantApp());
-        assertTrue(atom.getActivityStartMillis() > 0);
-        assertTrue(atom.getTransitionDelayMillis() > 0);
+        assertThat(atom.getPkgName()).isEqualTo("com.android.server.cts.device.statsd");
+        assertThat(atom.getActivityName())
+            .isEqualTo("com.android.server.cts.device.statsd.StatsdCtsForegroundActivity");
+        assertThat(atom.getIsInstantApp()).isFalse();
+        assertThat(atom.getActivityStartMillis()).isGreaterThan(0L);
+        assertThat(atom.getTransitionDelayMillis()).isGreaterThan(0);
     }
 
     public void testAudioState() throws Exception {
@@ -204,8 +210,8 @@
 
         BleScanStateChanged a0 = data.get(0).getAtom().getBleScanStateChanged();
         BleScanStateChanged a1 = data.get(1).getAtom().getBleScanStateChanged();
-        assertTrue(a0.getState().getNumber() == stateOn);
-        assertTrue(a1.getState().getNumber() == stateOff);
+        assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
+        assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
     }
 
     public void testBleUnoptimizedScan() throws Exception {
@@ -225,15 +231,15 @@
                 stateOn, stateOff, minTimeDiffMillis, maxTimeDiffMillis, true);
 
         BleScanStateChanged a0 = data.get(0).getAtom().getBleScanStateChanged();
-        assertTrue(a0.getState().getNumber() == stateOn);
-        assertFalse(a0.getIsFiltered());
-        assertFalse(a0.getIsFirstMatch());
-        assertFalse(a0.getIsOpportunistic());
+        assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
+        assertThat(a0.getIsFiltered()).isFalse();
+        assertThat(a0.getIsFirstMatch()).isFalse();
+        assertThat(a0.getIsOpportunistic()).isFalse();
         BleScanStateChanged a1 = data.get(1).getAtom().getBleScanStateChanged();
-        assertTrue(a1.getState().getNumber() == stateOff);
-        assertFalse(a1.getIsFiltered());
-        assertFalse(a1.getIsFirstMatch());
-        assertFalse(a1.getIsOpportunistic());
+        assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
+        assertThat(a1.getIsFiltered()).isFalse();
+        assertThat(a1.getIsFirstMatch()).isFalse();
+        assertThat(a1.getIsOpportunistic()).isFalse();
 
 
         // Now repeat the test for opportunistic scanning and make sure it is reported correctly.
@@ -241,15 +247,15 @@
                 stateOn, stateOff, minTimeDiffMillis, maxTimeDiffMillis, true);
 
         a0 = data.get(0).getAtom().getBleScanStateChanged();
-        assertTrue(a0.getState().getNumber() == stateOn);
-        assertFalse(a0.getIsFiltered());
-        assertFalse(a0.getIsFirstMatch());
-        assertTrue(a0.getIsOpportunistic());  // This scan is opportunistic.
+        assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
+        assertThat(a0.getIsFiltered()).isFalse();
+        assertThat(a0.getIsFirstMatch()).isFalse();
+        assertThat(a0.getIsOpportunistic()).isTrue();  // This scan is opportunistic.
         a1 = data.get(1).getAtom().getBleScanStateChanged();
-        assertTrue(a1.getState().getNumber() == stateOff);
-        assertFalse(a1.getIsFiltered());
-        assertFalse(a1.getIsFirstMatch());
-        assertTrue(a1.getIsOpportunistic());
+        assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
+        assertThat(a1.getIsFiltered()).isFalse();
+        assertThat(a1.getIsFirstMatch()).isFalse();
+        assertThat(a1.getIsOpportunistic()).isTrue();
     }
 
     public void testBleScanResult() throws Exception {
@@ -265,9 +271,9 @@
         addAtomEvent(conf, atom, createFvm(field).setGteInt(0));
         List<EventMetricData> data = doDeviceMethod("testBleScanResult", conf);
 
-        assertTrue(data.size() >= 1);
+        assertThat(data.size()).isAtLeast(1);
         BleScanResultReceived a0 = data.get(0).getAtom().getBleScanResultReceived();
-        assertTrue(a0.getNumResults() >= 1);
+        assertThat(a0.getNumResults()).isAtLeast(1);
     }
 
     public void testHiddenApiUsed() throws Exception {
@@ -289,15 +295,15 @@
 
 
             List<EventMetricData> data = getEventMetricDataList();
-            assertTrue(data.size() == 1);
+            assertThat(data).hasSize(1);
 
             HiddenApiUsed atom = data.get(0).getAtom().getHiddenApiUsed();
 
             int uid = getUid();
-            assertEquals(uid, atom.getUid());
-            assertFalse(atom.getAccessDenied());
-            assertEquals("Landroid/app/Activity;->mWindow:Landroid/view/Window;",
-                    atom.getSignature());
+            assertThat(atom.getUid()).isEqualTo(uid);
+            assertThat(atom.getAccessDenied()).isFalse();
+            assertThat(atom.getSignature())
+                .isEqualTo("Landroid/app/Activity;->mWindow:Landroid/view/Window;");
         } finally {
             if (!oldRate.equals("null")) {
                 getDevice().executeShellCommand(
@@ -359,11 +365,11 @@
         for (Atom atom : atomList) {
             if (atom.getCpuTimePerUid().getUid() == uid) {
                 found = true;
-                assertTrue(atom.getCpuTimePerUid().getUserTimeMicros() > 0);
-                assertTrue(atom.getCpuTimePerUid().getSysTimeMicros() > 0);
+                assertThat(atom.getCpuTimePerUid().getUserTimeMicros()).isGreaterThan(0L);
+                assertThat(atom.getCpuTimePerUid().getSysTimeMicros()).isGreaterThan(0L);
             }
         }
-        assertTrue("found uid " + uid, found);
+        assertWithMessage(String.format("did not find uid %d", uid)).that(found).isTrue();
     }
 
     public void testDeviceCalculatedPowerUse() throws Exception {
@@ -384,7 +390,8 @@
         Thread.sleep(WAIT_TIME_LONG);
 
         Atom atom = getGaugeMetricDataList().get(0);
-        assertTrue(atom.getDeviceCalculatedPowerUse().getComputedPowerNanoAmpSecs() > 0);
+        assertThat(atom.getDeviceCalculatedPowerUse().getComputedPowerNanoAmpSecs())
+            .isGreaterThan(0L);
     }
 
 
@@ -413,13 +420,15 @@
         for (Atom atom : atomList) {
             DeviceCalculatedPowerBlameUid item = atom.getDeviceCalculatedPowerBlameUid();
                 if (item.getUid() == uid) {
-                assertFalse("Found multiple power values for uid " + uid, uidFound);
+                assertWithMessage(String.format("Found multiple power values for uid %d", uid))
+                    .that(uidFound).isFalse();
                 uidFound = true;
                 uidPower = item.getPowerNanoAmpSecs();
             }
         }
-        assertTrue("No power value for uid " + uid, uidFound);
-        assertTrue("Non-positive power value for uid " + uid, uidPower > 0);
+        assertWithMessage(String.format("No power value for uid %d", uid)).that(uidFound).isTrue();
+        assertWithMessage(String.format("Non-positive power value for uid %d", uid))
+            .that(uidPower).isGreaterThan(0L);
     }
 
     public void testDavey() throws Exception {
@@ -435,12 +444,10 @@
         runActivity("DaveyActivity", null, null);
 
         List<EventMetricData> data = getEventMetricDataList();
-        assertTrue(data.size() == 1);
+        assertThat(data).hasSize(1);
         long duration = data.get(0).getAtom().getDaveyOccurred().getJankDurationMillis();
-        assertTrue("Jank duration of " + duration + "ms was less than " + MIN_DURATION + "ms",
-                duration >= MIN_DURATION);
-        assertTrue("Jank duration of " + duration + "ms was longer than " + MAX_DURATION + "ms",
-                duration <= MAX_DURATION);
+        assertWithMessage("Incorrect jank duration")
+            .that(duration).isIn(Range.closed(MIN_DURATION, MAX_DURATION));
     }
 
     public void testFlashlightState() throws Exception {
@@ -526,8 +533,8 @@
 
             GpsScanStateChanged a0 = data.get(0).getAtom().getGpsScanStateChanged();
             GpsScanStateChanged a1 = data.get(1).getAtom().getGpsScanStateChanged();
-            assertTrue(a0.getState().getNumber() == stateOn);
-            assertTrue(a1.getState().getNumber() == stateOff);
+            assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
+            assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
         } finally {
             if ("null".equals(origWhitelist) || "".equals(origWhitelist)) {
                 getDevice().executeShellCommand(
@@ -657,7 +664,8 @@
                 atom -> atom.getScheduledJobStateChanged().getState().getNumber());
 
         for (EventMetricData e : data) {
-            assertTrue(e.getAtom().getScheduledJobStateChanged().getJobName().equals(expectedName));
+            assertThat(e.getAtom().getScheduledJobStateChanged().getJobName())
+                .isEqualTo(expectedName);
         }
     }
 
@@ -783,10 +791,8 @@
         for (EventMetricData event: data) {
             String tag = event.getAtom().getWakelockStateChanged().getTag();
             WakeLockLevelEnum type = event.getAtom().getWakelockStateChanged().getType();
-            assertTrue("Expected tag: " + EXPECTED_TAG + ", but got tag: " + tag,
-                    tag.equals(EXPECTED_TAG));
-            assertTrue("Expected wakelock type: " + EXPECTED_LEVEL  + ", but got level: " + type,
-                    type == EXPECTED_LEVEL);
+            assertThat(tag).isEqualTo(EXPECTED_TAG);
+            assertThat(type).isEqualTo(EXPECTED_LEVEL);
         }
     }
 
@@ -803,11 +809,11 @@
         addAtomEvent(config, atomTag, true);  // True: uses attribution.
 
         List<EventMetricData> data = doDeviceMethod("testWakeupAlarm", config);
-        assertTrue(data.size() >= 1);
+        assertThat(data.size()).isAtLeast(1);
         for (int i = 0; i < data.size(); i++) {
             WakeupAlarmOccurred wao = data.get(i).getAtom().getWakeupAlarmOccurred();
-            assertEquals("*walarm*:android.cts.statsd.testWakeupAlarm", wao.getTag());
-            assertEquals(DEVICE_SIDE_TEST_PACKAGE, wao.getPackageName());
+            assertThat(wao.getTag()).isEqualTo("*walarm*:android.cts.statsd.testWakeupAlarm");
+            assertThat(wao.getPackageName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
         }
     }
 
@@ -836,8 +842,8 @@
                 atom -> atom.getWifiLockStateChanged().getState().getNumber());
 
         for (EventMetricData event : data) {
-            assertEquals(WifiModeEnum.WIFI_MODE_FULL_HIGH_PERF,
-                         event.getAtom().getWifiLockStateChanged().getMode());
+            assertThat(event.getAtom().getWifiLockStateChanged().getMode())
+                .isEqualTo(WifiModeEnum.WIFI_MODE_FULL_HIGH_PERF);
         }
     }
 
@@ -866,8 +872,8 @@
                 atom -> atom.getWifiLockStateChanged().getState().getNumber());
 
         for (EventMetricData event : data) {
-            assertEquals(WifiModeEnum.WIFI_MODE_FULL_LOW_LATENCY,
-                         event.getAtom().getWifiLockStateChanged().getMode());
+            assertThat(event.getAtom().getWifiLockStateChanged().getMode())
+                .isEqualTo(WifiModeEnum.WIFI_MODE_FULL_LOW_LATENCY);
         }
     }
 
@@ -901,7 +907,7 @@
 
         for (EventMetricData event: data) {
             String tag = event.getAtom().getWifiMulticastLockStateChanged().getTag();
-            assertEquals("Wrong tag.", EXPECTED_TAG, tag);
+            assertThat(tag).isEqualTo(EXPECTED_TAG);
         }
     }
 
@@ -922,12 +928,11 @@
         List<EventMetricData> data = doDeviceMethodOnOff("testWifiScan", atom, key,
                 stateOn, stateOff, minTimeDiffMillis, maxTimeDiffMillis, demandExactlyTwo);
 
-        assertTrue(data.size() >= 2);
-        assertTrue(data.size() <= 4);
+        assertThat(data.size()).isIn(Range.closed(2, 4));
         WifiScanStateChanged a0 = data.get(0).getAtom().getWifiScanStateChanged();
         WifiScanStateChanged a1 = data.get(1).getAtom().getWifiScanStateChanged();
-        assertTrue(a0.getState().getNumber() == stateOn);
-        assertTrue(a1.getState().getNumber() == stateOff);
+        assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
+        assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
     }
 
     public void testBinderStats() throws Exception {
@@ -963,20 +968,16 @@
 
                 if (calls.getUid() == uid && classMatches && methodMatches) {
                     found = true;
-                    assertTrue("Call count should not be negative or equal to 0.",
-                            calls.getRecordedCallCount() > 0);
-                    assertTrue("Call count should not be negative or equal to 0.",
-                            calls.getCallCount() > 0);
-                    assertTrue("Wrong latency",
-                            calls.getRecordedTotalLatencyMicros() > 0
-                            && calls.getRecordedTotalLatencyMicros() < 1000000);
-                    assertTrue("Wrong cpu usage",
-                            calls.getRecordedTotalCpuMicros() > 0
-                            && calls.getRecordedTotalCpuMicros() < 1000000);
+                    assertThat(calls.getRecordedCallCount()).isGreaterThan(0L);
+                    assertThat(calls.getCallCount()).isGreaterThan(0L);
+                    assertThat(calls.getRecordedTotalLatencyMicros())
+                        .isIn(Range.open(0L, 1000000L));
+                    assertThat(calls.getRecordedTotalCpuMicros()).isIn(Range.open(0L, 1000000L));
                 }
             }
 
-            assertTrue("Did not find a matching atom for uid " + uid, found);
+            assertWithMessage(String.format("Did not find a matching atom for uid %d", uid))
+                .that(found).isTrue();
 
         } finally {
             disableBinderStats();
@@ -1017,34 +1018,21 @@
                                 notificationServiceFullName + "$EnqueueNotificationRunnable");
                 if (atom.getLooperStats().getUid() == uid && handlerMatches && messageMatches) {
                     found = true;
-                    assertTrue(stats.getMessageCount() > 0);
-                    assertTrue("Message count should be non-negative.",
-                            stats.getMessageCount() > 0);
-                    assertTrue("Recorded message count should be non-negative.",
-                            stats.getRecordedMessageCount() > 0);
-                    assertTrue("Wrong latency",
-                            stats.getRecordedTotalLatencyMicros() > 0
-                                    && stats.getRecordedTotalLatencyMicros() < 1000000);
-                    assertTrue("Wrong cpu usage",
-                            stats.getRecordedTotalCpuMicros() > 0
-                                    && stats.getRecordedTotalCpuMicros() < 1000000);
-                    assertTrue("Wrong max latency",
-                            stats.getRecordedMaxLatencyMicros() > 0
-                                    && stats.getRecordedMaxLatencyMicros() < 1000000);
-                    assertTrue("Wrong max cpu usage",
-                            stats.getRecordedMaxCpuMicros() > 0
-                                    && stats.getRecordedMaxCpuMicros() < 1000000);
-                    assertTrue("Recorded delay message count should be non-negative.",
-                            stats.getRecordedDelayMessageCount() > 0);
-                    assertTrue("Wrong delay",
-                            stats.getRecordedTotalDelayMillis() >= 0
-                                    && stats.getRecordedTotalDelayMillis() < 5000);
-                    assertTrue("Wrong max delay",
-                            stats.getRecordedMaxDelayMillis() >= 0
-                                    && stats.getRecordedMaxDelayMillis() < 5000);
+                    assertThat(stats.getMessageCount()).isGreaterThan(0L);
+                    assertThat(stats.getRecordedMessageCount()).isGreaterThan(0L);
+                    assertThat(stats.getRecordedTotalLatencyMicros())
+                        .isIn(Range.open(0L, 1000000L));
+                    assertThat(stats.getRecordedTotalCpuMicros()).isIn(Range.open(0L, 1000000L));
+                    assertThat(stats.getRecordedMaxLatencyMicros()).isIn(Range.open(0L, 1000000L));
+                    assertThat(stats.getRecordedMaxCpuMicros()).isIn(Range.open(0L, 1000000L));
+                    assertThat(stats.getRecordedDelayMessageCount()).isGreaterThan(0L);
+                    assertThat(stats.getRecordedTotalDelayMillis())
+                        .isIn(Range.closedOpen(0L, 5000L));
+                    assertThat(stats.getRecordedMaxDelayMillis()).isIn(Range.closedOpen(0L, 5000L));
                 }
             }
-            assertTrue("Did not find a matching atom for uid " + uid, found);
+            assertWithMessage(String.format("Did not find a matching atom for uid %d", uid))
+                .that(found).isTrue();
         } finally {
             cleanUpLooperStats();
             plugInAc();
@@ -1081,53 +1069,16 @@
                 continue;
             }
             found = true;
-            assertEquals(DEVICE_SIDE_TEST_PACKAGE, state.getProcessName());
-            assertTrue("oom_score should not be negative", state.getOomAdjScore() >= 0);
-            assertTrue("page_fault should not be negative", state.getPageFault() >= 0);
-            assertTrue("page_major_fault should not be negative", state.getPageMajorFault() >= 0);
-            assertTrue("rss_in_bytes should be positive", state.getRssInBytes() > 0);
-            assertTrue("cache_in_bytes should not be negative", state.getCacheInBytes() >= 0);
-            assertTrue("swap_in_bytes should not be negative", state.getSwapInBytes() >= 0);
-            assertTrue("start_time_nanos should be positive", state.getStartTimeNanos() > 0);
-            assertTrue("start_time_nanos should be in the past",
-                    state.getStartTimeNanos() < System.nanoTime());
+            assertThat(state.getProcessName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
+            assertThat(state.getOomAdjScore()).isAtLeast(0);
+            assertThat(state.getPageFault()).isAtLeast(0L);
+            assertThat(state.getPageMajorFault()).isAtLeast(0L);
+            assertThat(state.getRssInBytes()).isGreaterThan(0L);
+            assertThat(state.getCacheInBytes()).isAtLeast(0L);
+            assertThat(state.getSwapInBytes()).isAtLeast(0L);
         }
-        assertTrue("Did not find a matching atom for uid=" + uid, found);
-    }
-
-    public void testNativeProcessMemoryState() throws Exception {
-        if (statsdDisabled()) {
-            return;
-        }
-
-        // Get NativeProcessState as a simple gauge metric.
-        StatsdConfig.Builder config = getPulledConfig();
-        addGaugeAtomWithDimensions(config, Atom.NATIVE_PROCESS_MEMORY_STATE_FIELD_NUMBER, null);
-        uploadConfig(config);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Trigger new pull.
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_LONG);
-
-        // Assert about NativeProcessMemoryState for statsd.
-        List<Atom> atoms = getGaugeMetricDataList();
-        boolean found = false;
-        for (Atom atom : atoms) {
-            NativeProcessMemoryState state = atom.getNativeProcessMemoryState();
-            if (!state.getProcessName().contains("/statsd")) {
-                continue;
-            }
-            found = true;
-            assertTrue("uid is below 10000", state.getUid() < 10000);
-            assertTrue("page_fault should not be negative", state.getPageFault() >= 0);
-            assertTrue("page_major_fault should not be negative", state.getPageMajorFault() >= 0);
-            assertTrue("rss_in_bytes should be positive", state.getRssInBytes() > 0);
-            assertTrue("start_time_nanos should be positive", state.getStartTimeNanos() > 0);
-            assertTrue("start_time_nanos should be in the past",
-                    state.getStartTimeNanos() < System.nanoTime());
-        }
-        assertTrue("Did not find a matching atom for statsd", found);
+        assertWithMessage(String.format("Did not find a matching atom for uid %d", uid))
+            .that(found).isTrue();
     }
 
     public void testProcessMemoryHighWaterMark() throws Exception {
@@ -1135,13 +1086,13 @@
             return;
         }
 
-        // Get ProcessMemoryState as a simple gauge metric.
+        // Get ProcessMemoryHighWaterMark as a simple gauge metric.
         StatsdConfig.Builder config = getPulledConfig();
         addGaugeAtomWithDimensions(config, Atom.PROCESS_MEMORY_HIGH_WATER_MARK_FIELD_NUMBER, null);
         uploadConfig(config);
         Thread.sleep(WAIT_TIME_SHORT);
 
-        // Start test app and trigger a pull while its running.
+        // Start test app and trigger a pull while it is running.
         try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
                 "action.show_notification")) {
             setAppBreadcrumbPredicate();
@@ -1158,22 +1109,71 @@
             ProcessMemoryHighWaterMark state = atom.getProcessMemoryHighWaterMark();
             if (state.getUid() == uid) {
                 foundTestApp = true;
-                assertEquals(DEVICE_SIDE_TEST_PACKAGE, state.getProcessName());
-                assertTrue("rss_high_water_mark_in_bytes should be positive",
-                        state.getRssHighWaterMarkInBytes() > 0);
+                assertThat(state.getProcessName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
+                assertThat(state.getRssHighWaterMarkInBytes()).isGreaterThan(0L);
             } else if (state.getProcessName().contains("/statsd")) {
                 foundStatsd = true;
-                assertTrue("rss_high_water_mark_in_bytes should be positive",
-                        state.getRssHighWaterMarkInBytes() > 0);
+                assertThat(state.getRssHighWaterMarkInBytes()).isGreaterThan(0L);
             } else if (state.getProcessName().equals("system")) {
                 foundSystemServer = true;
-                assertTrue("rss_high_water_mark_in_bytes should be positive",
-                        state.getRssHighWaterMarkInBytes() > 0);
+                assertThat(state.getRssHighWaterMarkInBytes()).isGreaterThan(0L);
             }
         }
-        assertTrue("Did not find a matching atom for test app uid=" + uid, foundTestApp);
-        assertTrue("Did not find a matching atom for statsd", foundStatsd);
-        assertTrue("Did not find a matching atom for system server", foundSystemServer);
+        assertWithMessage(String.format("Did not find a matching atom for test app uid=%d",uid))
+            .that(foundTestApp).isTrue();
+        assertWithMessage("Did not find a matching atom for statsd").that(foundStatsd).isTrue();
+        assertWithMessage("Did not find a matching atom for system server")
+            .that(foundSystemServer).isTrue();
+    }
+
+    public void testProcessMemorySnapshot() throws Exception {
+        if (statsdDisabled()) {
+            return;
+        }
+
+        // Get ProcessMemorySnapshot as a simple gauge metric.
+        StatsdConfig.Builder config = getPulledConfig();
+        addGaugeAtomWithDimensions(config, Atom.PROCESS_MEMORY_SNAPSHOT_FIELD_NUMBER, null);
+        uploadConfig(config);
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        // Start test app and trigger a pull while it is running.
+        try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
+                "action.show_notification")) {
+            setAppBreadcrumbPredicate();
+            Thread.sleep(WAIT_TIME_LONG);
+        }
+
+        // Assert about ProcessMemorySnapshot for the test app, statsd and system server.
+        List<Atom> atoms = getGaugeMetricDataList();
+        int uid = getUid();
+        boolean foundTestApp = false;
+        boolean foundStatsd = false;
+        boolean foundSystemServer = false;
+        for (Atom atom : atoms) {
+          ProcessMemorySnapshot snapshot = atom.getProcessMemorySnapshot();
+          if (snapshot.getUid() == uid) {
+              foundTestApp = true;
+              assertThat(snapshot.getProcessName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
+          } else if (snapshot.getProcessName().contains("/statsd")) {
+              foundStatsd = true;
+          } else if (snapshot.getProcessName().equals("system")) {
+              foundSystemServer = true;
+          }
+
+          assertThat(snapshot.getPid()).isGreaterThan(0);
+          assertThat(snapshot.getAnonRssAndSwapInKilobytes()).isGreaterThan(0);
+          assertThat(snapshot.getAnonRssAndSwapInKilobytes()).isEqualTo(
+                  snapshot.getAnonRssInKilobytes() + snapshot.getSwapInKilobytes());
+          assertThat(snapshot.getRssInKilobytes()).isAtLeast(0);
+          assertThat(snapshot.getAnonRssInKilobytes()).isAtLeast(0);
+          assertThat(snapshot.getSwapInKilobytes()).isAtLeast(0);
+        }
+        assertWithMessage(String.format("Did not find a matching atom for test app uid=%d",uid))
+            .that(foundTestApp).isTrue();
+        assertWithMessage("Did not find a matching atom for statsd").that(foundStatsd).isTrue();
+        assertWithMessage("Did not find a matching atom for system server")
+            .that(foundSystemServer).isTrue();
     }
 
     /**
@@ -1216,20 +1216,20 @@
         for (Atom atom : getGaugeMetricDataList()) {
             AtomsProto.RoleHolder roleHolder = atom.getRoleHolder();
 
-            assertNotNull(roleHolder.getPackageName());
-            assertTrue(roleHolder.getUid() >= 0);
-            assertNotNull(roleHolder.getRole());
+            assertThat(roleHolder.getPackageName()).isNotNull();
+            assertThat(roleHolder.getUid()).isAtLeast(0);
+            assertThat(roleHolder.getRole()).isNotNull();
 
             if (roleHolder.getPackageName().equals(DEVICE_SIDE_TEST_PACKAGE)) {
-                assertEquals(testAppId, getAppId(roleHolder.getUid()));
-                assertEquals(DEVICE_SIDE_TEST_PACKAGE, roleHolder.getPackageName());
-                assertEquals(callScreenAppRole, roleHolder.getRole());
+                assertThat(getAppId(roleHolder.getUid())).isEqualTo(testAppId);
+                assertThat(roleHolder.getPackageName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
+                assertThat(roleHolder.getRole()).isEqualTo(callScreenAppRole);
 
                 verifiedKnowRoleState = true;
             }
         }
 
-        assertTrue(verifiedKnowRoleState);
+        assertThat(verifiedKnowRoleState).isTrue();
     }
 
     public void testDangerousPermissionState() throws Exception {
@@ -1257,26 +1257,27 @@
         for (Atom atom : getGaugeMetricDataList()) {
             DangerousPermissionState permissionState = atom.getDangerousPermissionState();
 
-            assertNotNull(permissionState.getPermissionName());
-            assertTrue(permissionState.getUid() >= 0);
-            assertNotNull(permissionState.getPackageName());
+            assertThat(permissionState.getPermissionName()).isNotNull();
+            assertThat(permissionState.getUid()).isAtLeast(0);
+            assertThat(permissionState.getPackageName()).isNotNull();
 
             if (permissionState.getPackageName().equals(DEVICE_SIDE_TEST_PACKAGE)) {
-                assertEquals(testAppId, getAppId(permissionState.getUid()));
+                assertThat(getAppId(permissionState.getUid())).isEqualTo(testAppId);
 
                 if (permissionState.getPermissionName().equals(
                         "android.permission.ACCESS_FINE_LOCATION")) {
-                    assertTrue(permissionState.getIsGranted());
-                    assertEquals(0, permissionState.getPermissionFlags() & (~(
+                    assertThat(permissionState.getIsGranted()).isTrue();
+                    assertThat(permissionState.getPermissionFlags() & (~(
                             FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED
-                                    | FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED)));
+                            | FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED)))
+                        .isEqualTo(0);
 
                     verifiedKnowPermissionState = true;
                 }
             }
         }
 
-        assertTrue(verifiedKnowPermissionState);
+        assertThat(verifiedKnowPermissionState).isTrue();
     }
 
     public void testANROccurred() throws Exception {
@@ -1297,14 +1298,15 @@
         // Sorted list of events in order in which they occurred.
         List<EventMetricData> data = getEventMetricDataList();
 
-        assertEquals(1, data.size());
-        assertTrue(data.get(0).getAtom().hasAnrOccurred());
+        assertThat(data).hasSize(1);
+        assertThat(data.get(0).getAtom().hasAnrOccurred()).isTrue();
         ANROccurred atom = data.get(0).getAtom().getAnrOccurred();
-        assertEquals(ANROccurred.InstantApp.FALSE_VALUE, atom.getIsInstantApp().getNumber());
-        assertEquals(ANROccurred.ForegroundState.FOREGROUND_VALUE,
-                atom.getForegroundState().getNumber());
-        assertEquals(ErrorSource.DATA_APP, atom.getErrorSource());
-        assertEquals(DEVICE_SIDE_TEST_PACKAGE, atom.getPackageName());
+        assertThat(atom.getIsInstantApp().getNumber())
+            .isEqualTo(ANROccurred.InstantApp.FALSE_VALUE);
+        assertThat(atom.getForegroundState().getNumber())
+            .isEqualTo(ANROccurred.ForegroundState.FOREGROUND_VALUE);
+        assertThat(atom.getErrorSource()).isEqualTo(ErrorSource.DATA_APP);
+        assertThat(atom.getPackageName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
     }
 
     public void testWriteRawTestAtom() throws Exception {
@@ -1320,80 +1322,71 @@
         Thread.sleep(WAIT_TIME_SHORT);
         // Sorted list of events in order in which they occurred.
         List<EventMetricData> data = getEventMetricDataList();
-        assertEquals(data.size(), 4);
+        assertThat(data).hasSize(4);
 
         TestAtomReported atom = data.get(0).getAtom().getTestAtomReported();
         List<AttributionNode> attrChain = atom.getAttributionNodeList();
-        assertEquals(2, attrChain.size());
-        assertEquals(1234, attrChain.get(0).getUid());
-        assertEquals("tag1", attrChain.get(0).getTag());
-        assertEquals(getUid(), attrChain.get(1).getUid());
-        assertEquals("tag2", attrChain.get(1).getTag());
+        assertThat(attrChain).hasSize(2);
+        assertThat(attrChain.get(0).getUid()).isEqualTo(1234);
+        assertThat(attrChain.get(0).getTag()).isEqualTo("tag1");
+        assertThat(attrChain.get(1).getUid()).isEqualTo(getUid());
+        assertThat(attrChain.get(1).getTag()).isEqualTo("tag2");
 
-        assertEquals(42, atom.getIntField());
-        assertEquals(Long.MAX_VALUE, atom.getLongField());
-        assertEquals(3.14f, atom.getFloatField());
-        assertEquals("This is a basic test!", atom.getStringField());
-        assertEquals(false, atom.getBooleanField());
-        assertEquals(TestAtomReported.State.ON_VALUE, atom.getState().getNumber());
-        List<Long> expIds = atom.getBytesField().getExperimentIdList();
-        assertEquals(3, expIds.size());
-        assertEquals(1L, (long) expIds.get(0));
-        assertEquals(2L, (long) expIds.get(1));
-        assertEquals(3L, (long) expIds.get(2));
+        assertThat(atom.getIntField()).isEqualTo(42);
+        assertThat(atom.getLongField()).isEqualTo(Long.MAX_VALUE);
+        assertThat(atom.getFloatField()).isEqualTo(3.14f);
+        assertThat(atom.getStringField()).isEqualTo("This is a basic test!");
+        assertThat(atom.getBooleanField()).isFalse();
+        assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.ON_VALUE);
+        assertThat(atom.getBytesField().getExperimentIdList())
+            .containsExactly(1L, 2L, 3L).inOrder();
+
 
         atom = data.get(1).getAtom().getTestAtomReported();
         attrChain = atom.getAttributionNodeList();
-        assertEquals(2, attrChain.size());
-        assertEquals(9999, attrChain.get(0).getUid());
-        assertEquals("tag9999", attrChain.get(0).getTag());
-        assertEquals(getUid(), attrChain.get(1).getUid());
-        assertEquals("", attrChain.get(1).getTag());
+        assertThat(attrChain).hasSize(2);
+        assertThat(attrChain.get(0).getUid()).isEqualTo(9999);
+        assertThat(attrChain.get(0).getTag()).isEqualTo("tag9999");
+        assertThat(attrChain.get(1).getUid()).isEqualTo(getUid());
+        assertThat(attrChain.get(1).getTag()).isEmpty();
 
-        assertEquals(100, atom.getIntField());
-        assertEquals(Long.MIN_VALUE, atom.getLongField());
-        assertEquals(-2.5f, atom.getFloatField());
-        assertEquals("Test null uid", atom.getStringField());
-        assertEquals(true, atom.getBooleanField());
-        assertEquals(TestAtomReported.State.UNKNOWN_VALUE, atom.getState().getNumber());
-        expIds = atom.getBytesField().getExperimentIdList();
-        assertEquals(3, expIds.size());
-        assertEquals(1L, (long) expIds.get(0));
-        assertEquals(2L, (long) expIds.get(1));
-        assertEquals(3L, (long) expIds.get(2));
+        assertThat(atom.getIntField()).isEqualTo(100);
+        assertThat(atom.getLongField()).isEqualTo(Long.MIN_VALUE);
+        assertThat(atom.getFloatField()).isEqualTo(-2.5f);
+        assertThat(atom.getStringField()).isEqualTo("Test null uid");
+        assertThat(atom.getBooleanField()).isTrue();
+        assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.UNKNOWN_VALUE);
+        assertThat(atom.getBytesField().getExperimentIdList())
+            .containsExactly(1L, 2L, 3L).inOrder();
 
         atom = data.get(2).getAtom().getTestAtomReported();
         attrChain = atom.getAttributionNodeList();
-        assertEquals(1, attrChain.size());
-        assertEquals(getUid(), attrChain.get(0).getUid());
-        assertEquals("tag1", attrChain.get(0).getTag());
+        assertThat(attrChain).hasSize(1);
+        assertThat(attrChain.get(0).getUid()).isEqualTo(getUid());
+        assertThat(attrChain.get(0).getTag()).isEqualTo("tag1");
 
-        assertEquals(-256, atom.getIntField());
-        assertEquals(-1234567890L, atom.getLongField());
-        assertEquals(42.01f, atom.getFloatField());
-        assertEquals("Test non chained", atom.getStringField());
-        assertEquals(true, atom.getBooleanField());
-        assertEquals(TestAtomReported.State.OFF_VALUE, atom.getState().getNumber());
-        expIds = atom.getBytesField().getExperimentIdList();
-        assertEquals(3, expIds.size());
-        assertEquals(1L, (long) expIds.get(0));
-        assertEquals(2L, (long) expIds.get(1));
-        assertEquals(3L, (long) expIds.get(2));
+        assertThat(atom.getIntField()).isEqualTo(-256);
+        assertThat(atom.getLongField()).isEqualTo(-1234567890L);
+        assertThat(atom.getFloatField()).isEqualTo(42.01f);
+        assertThat(atom.getStringField()).isEqualTo("Test non chained");
+        assertThat(atom.getBooleanField()).isTrue();
+        assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.OFF_VALUE);
+        assertThat(atom.getBytesField().getExperimentIdList())
+            .containsExactly(1L, 2L, 3L).inOrder();
 
         atom = data.get(3).getAtom().getTestAtomReported();
         attrChain = atom.getAttributionNodeList();
-        assertEquals(1, attrChain.size());
-        assertEquals(getUid(), attrChain.get(0).getUid());
-        assertEquals("", attrChain.get(0).getTag());
+        assertThat(attrChain).hasSize(1);
+        assertThat(attrChain.get(0).getUid()).isEqualTo(getUid());
+        assertThat(attrChain.get(0).getTag()).isEmpty();
 
-        assertEquals(0, atom.getIntField());
-        assertEquals(0L, atom.getLongField());
-        assertEquals(0f, atom.getFloatField());
-        assertEquals("", atom.getStringField());
-        assertEquals(true, atom.getBooleanField());
-        assertEquals(TestAtomReported.State.OFF_VALUE, atom.getState().getNumber());
-        expIds = atom.getBytesField().getExperimentIdList();
-        assertEquals(0, expIds.size());
+        assertThat(atom.getIntField()).isEqualTo(0);
+        assertThat(atom.getLongField()).isEqualTo(0L);
+        assertThat(atom.getFloatField()).isEqualTo(0f);
+        assertThat(atom.getStringField()).isEmpty();
+        assertThat(atom.getBooleanField()).isTrue();
+        assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.OFF_VALUE);
+        assertThat(atom.getBytesField().getExperimentIdList()).isEmpty();
     }
 
 }
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metadata/MetadataTests.java b/hostsidetests/statsd/src/android/cts/statsd/metadata/MetadataTests.java
index e92e7b2..4c0e4e6 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metadata/MetadataTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metadata/MetadataTests.java
@@ -15,6 +15,8 @@
  */
 package android.cts.statsd.metadata;
 
+import static com.google.common.truth.Truth.assertWithMessage;
+
 import android.cts.statsd.atom.AtomTestCase;
 
 import com.android.internal.os.StatsdConfigProto;
@@ -61,13 +63,14 @@
         for (ConfigStats stats: report.getConfigStatsList()) {
             if (stats.getId() == CONFIG_ID && stats.getUid() == getHostUid()) {
                 if(!stats.hasDeletionTimeSec()) {
-                    assertTrue("Found multiple active CTS configs!", foundActiveConfig == false);
+                    assertWithMessage("Found multiple active CTS configs!")
+                            .that(foundActiveConfig).isFalse();
                     foundActiveConfig = true;
                     creationTime = stats.getCreationTimeSec();
                 }
             }
         }
-        assertTrue("Did not find an active CTS config", foundActiveConfig);
+        assertWithMessage("Did not find an active CTS config").that(foundActiveConfig).isTrue();
 
         while(System.currentTimeMillis() - startTime < 8_000) {
             Thread.sleep(10);
@@ -81,26 +84,29 @@
             if (stats.getId() == CONFIG_ID && stats.getUid() == getHostUid()) {
                 // Original config should be TTL'd
                 if (stats.getCreationTimeSec() == creationTime) {
-                    assertTrue("Config should have TTL'd but is still active",
-                            stats.hasDeletionTimeSec());
-                    assertTrue("Config deletion time should be about " + TTL_TIME_SEC +
-                            " after creation",
-                            Math.abs(stats.getDeletionTimeSec() - expectedTime) <= 2);
+                    assertWithMessage("Config should have TTL'd but is still active")
+                            .that(stats.hasDeletionTimeSec()).isTrue();
+                    assertWithMessage(
+                            "Config deletion time should be about %s after creation", TTL_TIME_SEC
+                    ).that(Math.abs(stats.getDeletionTimeSec() - expectedTime)).isAtMost(2);
                 }
                 // There should still be one active config, that is marked as reset.
                 if(!stats.hasDeletionTimeSec()) {
-                    assertTrue("Found multiple active CTS configs!", foundActiveConfig == false);
+                    assertWithMessage("Found multiple active CTS configs!")
+                            .that(foundActiveConfig).isFalse();
                     foundActiveConfig = true;
                     creationTime = stats.getCreationTimeSec();
-                    assertTrue("Active config after TTL should be marked as reset",
-                            stats.hasResetTimeSec());
-                    assertEquals("Reset time and creation time should be equal for TTl'd configs",
-                            stats.getResetTimeSec(), stats.getCreationTimeSec());
-                    assertTrue("Reset config should be created when the original config TTL'd",
-                            Math.abs(stats.getCreationTimeSec() - expectedTime) <= 2);
+                    assertWithMessage("Active config after TTL should be marked as reset")
+                            .that(stats.hasResetTimeSec()).isTrue();
+                    assertWithMessage("Reset and creation time should be equal for TTl'd configs")
+                            .that(stats.getResetTimeSec()).isEqualTo(stats.getCreationTimeSec());
+                    assertWithMessage(
+                            "Reset config should be created when the original config TTL'd"
+                    ).that(Math.abs(stats.getCreationTimeSec() - expectedTime)).isAtMost(2);
                 }
             }
         }
-        assertTrue("Did not find an active CTS config after the TTL", foundActiveConfig);
+        assertWithMessage("Did not find an active CTS config after the TTL")
+                .that(foundActiveConfig).isTrue();
     }
 }
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java
index 70777ef..4eeb74c 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java
@@ -15,6 +15,9 @@
  */
 package android.cts.statsd.metric;
 
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
 import android.cts.statsd.atom.DeviceAtomTestCase;
 
 import com.android.internal.os.StatsdConfigProto;
@@ -53,13 +56,13 @@
 
         StatsLogReport metricReport = getStatsLogReport();
         LogUtil.CLog.d("Got the following stats log report: \n" + metricReport.toString());
-        assertEquals(MetricsUtils.COUNT_METRIC_ID, metricReport.getMetricId());
-        assertTrue(metricReport.hasCountMetrics());
+        assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID);
+        assertThat(metricReport.hasCountMetrics()).isTrue();
 
         StatsLogReport.CountMetricDataWrapper countData = metricReport.getCountMetrics();
 
-        assertTrue(countData.getDataCount() > 0);
-        assertEquals(2, countData.getData(0).getBucketInfo(0).getCount());
+        assertThat(countData.getDataCount()).isGreaterThan(0);
+        assertThat(countData.getData(0).getBucketInfo(0).getCount()).isEqualTo(2);
     }
     public void testEventCountWithCondition() throws Exception {
         if (statsdDisabled()) {
@@ -112,13 +115,13 @@
         Thread.sleep(2000);  // Wait for the metrics to propagate to statsd.
 
         StatsLogReport metricReport = getStatsLogReport();
-        assertEquals(MetricsUtils.COUNT_METRIC_ID, metricReport.getMetricId());
-        assertTrue(metricReport.hasCountMetrics());
+        assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID);
+        assertThat(metricReport.hasCountMetrics()).isTrue();
 
         StatsLogReport.CountMetricDataWrapper countData = metricReport.getCountMetrics();
 
-        assertTrue(countData.getDataCount() > 0);
-        assertEquals(1, countData.getData(0).getBucketInfo(0).getCount());
+        assertThat(countData.getDataCount()).isGreaterThan(0);
+        assertThat(countData.getData(0).getBucketInfo(0).getCount()).isEqualTo(1);
     }
 
     public void testEventCountWithConditionAndActivation() throws Exception {
@@ -236,16 +239,16 @@
         Thread.sleep(2000);
 
         StatsLogReport metricReport = getStatsLogReport();
-        assertEquals(MetricsUtils.COUNT_METRIC_ID, metricReport.getMetricId());
+        assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID);
         LogUtil.CLog.d("Received the following data: " + metricReport.toString());
-        assertTrue(metricReport.hasCountMetrics());
-        assertFalse(metricReport.getIsActive());
+        assertThat(metricReport.hasCountMetrics()).isTrue();
+        assertThat(metricReport.getIsActive()).isFalse();
 
         StatsLogReport.CountMetricDataWrapper countData = metricReport.getCountMetrics();
-        assertEquals(1, countData.getDataCount());
-        assertEquals(2, countData.getData(0).getBucketInfoCount());
-        assertEquals(2, countData.getData(0).getBucketInfo(0).getCount());
-        assertEquals(1, countData.getData(0).getBucketInfo(1).getCount());
+        assertThat(countData.getDataCount()).isEqualTo(1);
+        assertThat(countData.getData(0).getBucketInfoCount()).isEqualTo(2);
+        assertThat(countData.getData(0).getBucketInfo(0).getCount()).isEqualTo(2);
+        assertThat(countData.getData(0).getBucketInfo(1).getCount()).isEqualTo(1);
     }
 
     public void testPartialBucketCountMetric() throws Exception {
@@ -273,17 +276,14 @@
         ConfigMetricsReportList reports = getReportList();
         LogUtil.CLog.d("Got following report list: " + reports.toString());
 
-        assertEquals("Expected 2 reports, got " + reports.getReportsCount(),
-                2, reports.getReportsCount());
+        assertThat(reports.getReportsCount()).isEqualTo(2);
         boolean inOrder = reports.getReports(0).getCurrentReportWallClockNanos() <
                 reports.getReports(1).getCurrentReportWallClockNanos();
 
         // Only 1 metric, so there should only be 1 StatsLogReport.
         for (ConfigMetricsReport report : reports.getReportsList()) {
-            assertEquals("Expected 1 StatsLogReport in each ConfigMetricsReport",
-                    1, report.getMetricsCount());
-            assertEquals("Expected 1 CountMetricData in each report",
-                    1, report.getMetrics(0).getCountMetrics().getDataCount());
+            assertThat(report.getMetricsCount()).isEqualTo(1);
+            assertThat(report.getMetrics(0).getCountMetrics().getDataCount()).isEqualTo(1);
         }
         CountMetricData data1 =
                 reports.getReports(inOrder? 0 : 1).getMetrics(0).getCountMetrics().getData(0);
@@ -291,19 +291,20 @@
                 reports.getReports(inOrder? 1 : 0).getMetrics(0).getCountMetrics().getData(0);
         // Data1 should have only 1 bucket, and it should be a partial bucket.
         // The count should be 1.
-        assertEquals("First report should only have 1 bucket", 1, data1.getBucketInfoCount());
+        assertThat(data1.getBucketInfoCount()).isEqualTo(1);
         CountBucketInfo bucketInfo = data1.getBucketInfo(0);
-        assertEquals("First report should have a count of 1", 1, bucketInfo.getCount());
-        assertTrue("First report's bucket should be less than 1 day",
-                bucketInfo.getEndBucketElapsedNanos() <
-                (bucketInfo.getStartBucketElapsedNanos() + 1_000_000_000L * 60L * 60L * 24L));
+        assertThat(bucketInfo.getCount()).isEqualTo(1);
+        assertWithMessage("First report's bucket should be less than 1 day")
+                .that(bucketInfo.getEndBucketElapsedNanos())
+                .isLessThan(bucketInfo.getStartBucketElapsedNanos() + 
+                        1_000_000_000L * 60L * 60L * 24L);
 
         //Second report should have a count of 2.
-        assertTrue("Second report should have at most 2 buckets", data2.getBucketInfoCount() < 3);
+        assertThat(data2.getBucketInfoCount()).isAtMost(2);
         int totalCount = 0;
         for (CountBucketInfo bucket : data2.getBucketInfoList()) {
             totalCount += bucket.getCount();
         }
-        assertEquals("Second report should have a count of 2", 2, totalCount);
+        assertThat(totalCount).isEqualTo(2);
     }
 }
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/GaugeMetricsTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/GaugeMetricsTests.java
index 4bd5a2a..16c3baf 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/GaugeMetricsTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/GaugeMetricsTests.java
@@ -122,30 +122,30 @@
 
       StatsLogReport metricReport = getStatsLogReport();
       LogUtil.CLog.d("Got the following gauge metric data: " + metricReport.toString());
-      assertEquals(MetricsUtils.GAUGE_METRIC_ID, metricReport.getMetricId());
-      assertTrue(metricReport.hasGaugeMetrics());
+      assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.GAUGE_METRIC_ID);
+      assertThat(metricReport.hasGaugeMetrics()).isTrue();
       StatsLogReport.GaugeMetricDataWrapper gaugeData = metricReport.getGaugeMetrics();
-      assertEquals(gaugeData.getDataCount(), 1);
+      assertThat(gaugeData.getDataCount()).isEqualTo(1);
 
       int bucketCount = gaugeData.getData(0).getBucketInfoCount();
       GaugeMetricData data = gaugeData.getData(0);
-      assertTrue(bucketCount > 2);
+      assertThat(bucketCount).isGreaterThan(2);
       MetricsUtils.assertBucketTimePresent(data.getBucketInfo(0));
-      assertEquals(data.getBucketInfo(0).getAtomCount(), 1);
-      assertEquals(data.getBucketInfo(0).getAtom(0).getAppBreadcrumbReported().getLabel(), 0);
-      assertEquals(data.getBucketInfo(0).getAtom(0).getAppBreadcrumbReported().getState(),
-          AppBreadcrumbReported.State.START);
+      assertThat(data.getBucketInfo(0).getAtomCount()).isEqualTo(1);
+      assertThat(data.getBucketInfo(0).getAtom(0).getAppBreadcrumbReported().getLabel())
+              .isEqualTo(0);
+      assertThat(data.getBucketInfo(0).getAtom(0).getAppBreadcrumbReported().getState())
+              .isEqualTo(AppBreadcrumbReported.State.START);
 
       MetricsUtils.assertBucketTimePresent(data.getBucketInfo(1));
-      assertEquals(data.getBucketInfo(1).getAtomCount(), 1);
+      assertThat(data.getBucketInfo(1).getAtomCount()).isEqualTo(1);
 
       MetricsUtils.assertBucketTimePresent(data.getBucketInfo(bucketCount-1));
-      assertEquals(data.getBucketInfo(bucketCount - 1).getAtomCount(), 1);
-      assertEquals(
-          data.getBucketInfo(bucketCount - 1).getAtom(0).getAppBreadcrumbReported().getLabel(), 2);
-      assertEquals(
-          data.getBucketInfo(bucketCount - 1).getAtom(0).getAppBreadcrumbReported().getState(),
-          AppBreadcrumbReported.State.STOP);
+      assertThat(data.getBucketInfo(bucketCount-1).getAtomCount()).isEqualTo(1);
+      assertThat(data.getBucketInfo(bucketCount-1).getAtom(0).getAppBreadcrumbReported().getLabel())
+              .isEqualTo(2);
+      assertThat(data.getBucketInfo(bucketCount-1).getAtom(0).getAppBreadcrumbReported().getState())
+              .isEqualTo(AppBreadcrumbReported.State.STOP);
   }
 
   public void testPulledGaugeMetricWithActivation() throws Exception {
@@ -197,8 +197,8 @@
 
       StatsLogReport metricReport = getStatsLogReport();
       LogUtil.CLog.d("Got the following gauge metric data: " + metricReport.toString());
-      assertEquals(MetricsUtils.GAUGE_METRIC_ID, metricReport.getMetricId());
-      assertFalse(metricReport.hasGaugeMetrics());
+      assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.GAUGE_METRIC_ID);
+      assertThat(metricReport.hasGaugeMetrics()).isFalse();
   }
 
     public void testPulledGaugeMetricWithConditionAndActivation() throws Exception {
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/MetricActivationTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/MetricActivationTests.java
index 803dd2e..b976678 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/MetricActivationTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/MetricActivationTests.java
@@ -15,6 +15,8 @@
  */
 package android.cts.statsd.metric;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import android.cts.statsd.atom.DeviceAtomTestCase;
 
 import com.android.internal.os.StatsdConfigProto;
@@ -374,7 +376,7 @@
 
         ConfigMetricsReportList reportList = getReportList();
         List<ConfigMetricsReport> reports = getSortedConfigMetricsReports(reportList);
-        assertEquals(3, reports.size());
+        assertThat(reports).hasSize(3);
 
         // Report before restart.
         ConfigMetricsReport report = reports.get(0);
@@ -510,7 +512,7 @@
 
         ConfigMetricsReportList reportList = getReportList();
         List<ConfigMetricsReport> reports = getSortedConfigMetricsReports(reportList);
-        assertEquals(3, reports.size());
+        assertThat(reports).hasSize(3);
 
         // Report before restart.
         ConfigMetricsReport report = reports.get(0);
@@ -538,7 +540,7 @@
 
     private void verifyMetrics(ConfigMetricsReport report, int metric1Count, int metric2Count,
             int metric3Count) throws Exception {
-        assertEquals(3, report.getMetricsCount());
+        assertThat(report.getMetricsCount()).isEqualTo(3);
 
         verifyMetric(
                 report.getMetrics(0),   // StatsLogReport
@@ -563,17 +565,14 @@
     private void verifyMetric(StatsLogReport metricReport, long metricId, int metricMatcherLabel,
             int dataCount) {
         LogUtil.CLog.d("Got the following event metric data: " + metricReport.toString());
-        assertEquals(metricId, metricReport.getMetricId());
-        if (dataCount > 0) {
-            assertTrue(metricReport.hasEventMetrics());
-        } else {
-            assertFalse(metricReport.hasEventMetrics());
-        }
+        assertThat(metricReport.getMetricId()).isEqualTo(metricId);
+        assertThat(metricReport.hasEventMetrics()).isEqualTo(dataCount > 0);
+
         StatsLogReport.EventMetricDataWrapper eventData = metricReport.getEventMetrics();
-        assertEquals(dataCount, eventData.getDataCount());
+        assertThat(eventData.getDataCount()).isEqualTo(dataCount);
         for (int i = 0; i < eventData.getDataCount(); i++) {
             AppBreadcrumbReported atom = eventData.getData(i).getAtom().getAppBreadcrumbReported();
-            assertEquals(metricMatcherLabel, atom.getLabel());
+            assertThat(atom.getLabel()).isEqualTo(metricMatcherLabel);
         }
     }
 }
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/MetricsUtils.java b/hostsidetests/statsd/src/android/cts/statsd/metric/MetricsUtils.java
index 2677634..7097587 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/MetricsUtils.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/MetricsUtils.java
@@ -15,6 +15,8 @@
  */
 package android.cts.statsd.metric;
 
+import static com.google.common.truth.Truth.assertWithMessage;
+
 import com.android.internal.os.StatsdConfigProto;
 import com.android.internal.os.StatsdConfigProto.AtomMatcher;
 import com.android.internal.os.StatsdConfigProto.EventActivation;
@@ -26,8 +28,6 @@
 import com.google.protobuf.Descriptors.Descriptor;
 import com.google.protobuf.Descriptors.FieldDescriptor;
 
-import static org.junit.Assert.assertTrue;
-
 public class MetricsUtils {
     public static final long COUNT_METRIC_ID = 3333;
     public static final long DURATION_METRIC_ID = 4444;
@@ -156,7 +156,8 @@
                    endMillis != null && bucketInfo.hasField(endMillis)) {
             found = true;
         }
-        assertTrue("Bucket info did not have either bucket num or start and end elapsed millis",
-                found);
+        assertWithMessage(
+                "Bucket info did not have either bucket num or start and end elapsed millis"
+        ).that(found).isTrue();
     }
 }
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java
index 25c06e3..2b2eee7 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java
@@ -94,22 +94,22 @@
 
     StatsLogReport metricReport = getStatsLogReport();
     LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString());
-    assertEquals(MetricsUtils.VALUE_METRIC_ID, metricReport.getMetricId());
-    assertTrue(metricReport.hasValueMetrics());
+    assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID);
+    assertThat(metricReport.hasValueMetrics()).isTrue();
     StatsLogReport.ValueMetricDataWrapper valueData = metricReport.getValueMetrics();
-    assertEquals(1, valueData.getDataCount());
+    assertThat(valueData.getDataCount()).isEqualTo(1);
 
     int bucketCount = valueData.getData(0).getBucketInfoCount();
-    assertTrue(bucketCount > 1);
+    assertThat(bucketCount).isGreaterThan(1);
     ValueMetricData data = valueData.getData(0);
     int totalValue = 0;
     for (ValueBucketInfo bucketInfo : data.getBucketInfoList()) {
       MetricsUtils.assertBucketTimePresent(bucketInfo);
-      assertEquals(1, bucketInfo.getValuesCount());
-      assertEquals(0, bucketInfo.getValues(0).getIndex());
+      assertThat(bucketInfo.getValuesCount()).isEqualTo(1);
+      assertThat(bucketInfo.getValues(0).getIndex()).isEqualTo(0);
       totalValue += (int) bucketInfo.getValues(0).getValueLong();
     }
-    assertEquals(8, totalValue);
+    assertThat(totalValue).isEqualTo(8);
   }
 
   // Test value metric with pulled atoms and across multiple buckets
@@ -172,24 +172,24 @@
 
     StatsLogReport metricReport = getStatsLogReport();
     LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString());
-    assertEquals(MetricsUtils.VALUE_METRIC_ID, metricReport.getMetricId());
-    assertTrue(metricReport.hasValueMetrics());
+    assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID);
+    assertThat(metricReport.hasValueMetrics()).isTrue();
     StatsLogReport.ValueMetricDataWrapper valueData = metricReport.getValueMetrics();
-    assertEquals(valueData.getDataCount(), 1);
+    assertThat(valueData.getDataCount()).isEqualTo(1);
 
     int bucketCount = valueData.getData(0).getBucketInfoCount();
     // should have at least 2 buckets
-    assertTrue(bucketCount >= 2);
+    assertThat(bucketCount).isAtLeast(2);
     ValueMetricData data = valueData.getData(0);
     int totalValue = 0;
     for (ValueBucketInfo bucketInfo : data.getBucketInfoList()) {
       MetricsUtils.assertBucketTimePresent(bucketInfo);
-      assertEquals(1, bucketInfo.getValuesCount());
-      assertEquals(0, bucketInfo.getValues(0).getIndex());
+      assertThat(bucketInfo.getValuesCount()).isEqualTo(1);
+      assertThat(bucketInfo.getValues(0).getIndex()).isEqualTo(0);
       totalValue += (int) bucketInfo.getValues(0).getValueLong();
     }
     // At most we lose one full min bucket
-    assertTrue(totalValue > (130_000 - 60_000));
+    assertThat(totalValue).isGreaterThan(130_000 - 60_000);
   }
 
   // Test value metric with pulled atoms and across multiple buckets
@@ -256,24 +256,24 @@
 
     StatsLogReport metricReport = getStatsLogReport();
     LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString());
-    assertEquals(MetricsUtils.VALUE_METRIC_ID, metricReport.getMetricId());
-    assertTrue(metricReport.hasValueMetrics());
+    assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID);
+    assertThat(metricReport.hasValueMetrics()).isTrue();
     StatsLogReport.ValueMetricDataWrapper valueData = metricReport.getValueMetrics();
-    assertEquals(valueData.getDataCount(), 1);
+    assertThat(valueData.getDataCount()).isEqualTo(1);
 
     int bucketCount = valueData.getData(0).getBucketInfoCount();
     // should have at least 2 buckets
-    assertTrue(bucketCount >= 2);
+    assertThat(bucketCount).isAtLeast(2);
     ValueMetricData data = valueData.getData(0);
     int totalValue = 0;
     for (ValueBucketInfo bucketInfo : data.getBucketInfoList()) {
       MetricsUtils.assertBucketTimePresent(bucketInfo);
-      assertEquals(1, bucketInfo.getValuesCount());
-      assertEquals(0, bucketInfo.getValues(0).getIndex());
+      assertThat(bucketInfo.getValuesCount()).isEqualTo(1);
+      assertThat(bucketInfo.getValues(0).getIndex()).isEqualTo(0);
       totalValue += (int) bucketInfo.getValues(0).getValueLong();
     }
     // At most we lose one full min bucket
-    assertTrue(totalValue > (GAP_INTERVAL*NUM_EVENTS - 60_000));
+    assertThat((long) totalValue).isGreaterThan(GAP_INTERVAL * NUM_EVENTS - 60_000);
   }
 
   // Test value metric with pulled atoms and across multiple buckets
@@ -329,8 +329,8 @@
 
     StatsLogReport metricReport = getStatsLogReport();
     LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString());
-    assertEquals(MetricsUtils.VALUE_METRIC_ID, metricReport.getMetricId());
-    assertFalse(metricReport.hasValueMetrics());
+    assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID);
+    assertThat(metricReport.hasValueMetrics()).isFalse();
   }
 
     public void testValueMetricWithConditionAndActivation() throws Exception {
diff --git a/hostsidetests/statsd/src/android/cts/statsd/subscriber/ShellSubscriberTest.java b/hostsidetests/statsd/src/android/cts/statsd/subscriber/ShellSubscriberTest.java
index f8aa3b7..f239f0e 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/subscriber/ShellSubscriberTest.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/subscriber/ShellSubscriberTest.java
@@ -15,6 +15,8 @@
  */
 package android.cts.statsd.subscriber;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import com.android.compatibility.common.util.CpuFeatures;
 import com.android.internal.os.StatsdConfigProto;
 import com.android.os.AtomsProto;
@@ -79,7 +81,7 @@
         startSubscription(config, receiver, 10);
         byte[] output = receiver.getOutput();
         // There should be at lease some data returned.
-        assertTrue(output.length > sizetBytes);
+        assertThat(output.length).isGreaterThan(sizetBytes);
 
         int atomCount = 0;
         int i = 0;
@@ -98,8 +100,8 @@
                 ShellDataProto.ShellData data =
                         ShellDataProto.ShellData.parseFrom(
                                 Arrays.copyOfRange(output, i + sizetBytes, i + sizetBytes + len));
-                assertTrue(data.getAtomCount() > 0);
-                assertTrue(data.getAtom(0).hasSystemUptime());
+                assertThat(data.getAtomCount()).isGreaterThan(0);
+                assertThat(data.getAtom(0).hasSystemUptime()).isTrue();
                 atomCount++;
                 LogUtil.CLog.d("Received " + data.toString());
             } catch (InvalidProtocolBufferException e) {
@@ -108,7 +110,7 @@
             i += (sizetBytes + len);
         }
 
-        assertTrue(atomCount > 0);
+        assertThat(atomCount).isGreaterThan(0);
     }
 
     private void startSubscription(ShellConfig.ShellSubscription config,
diff --git a/hostsidetests/statsd/src/android/cts/statsd/uidmap/UidMapTests.java b/hostsidetests/statsd/src/android/cts/statsd/uidmap/UidMapTests.java
index bf599d3..dab7f0f 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/uidmap/UidMapTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/uidmap/UidMapTests.java
@@ -15,7 +15,7 @@
  */
 package android.cts.statsd.uidmap;
 
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
 
 import android.cts.statsd.atom.DeviceAtomTestCase;
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
@@ -40,15 +40,15 @@
         createAndUploadConfig(AtomsProto.Atom.UID_PROCESS_STATE_CHANGED_FIELD_NUMBER);
 
         ConfigMetricsReportList reports = getReportList();
-        assertTrue(reports.getReportsCount() > 0);
+        assertThat(reports.getReportsCount()).isGreaterThan(0);
 
         for (ConfigMetricsReport report : reports.getReportsList()) {
             UidMapping uidmap = report.getUidMap();
-            assertTrue(uidmap.getSnapshotsCount() > 0);
+            assertThat(uidmap.getSnapshotsCount()).isGreaterThan(0);
             for (PackageInfoSnapshot snapshot : uidmap.getSnapshotsList()) {
                 // There must be at least one element in each snapshot (at least one package is
                 // installed).
-                assertTrue(snapshot.getPackageInfoCount() > 0);
+                assertThat(snapshot.getPackageInfoCount()).isGreaterThan(0);
             }
         }
     }
@@ -81,7 +81,7 @@
         Thread.sleep(WAIT_TIME_SHORT);
 
         ConfigMetricsReportList reports = getReportList();
-        assertTrue(reports.getReportsCount() > 0);
+        assertThat(reports.getReportsCount()).isGreaterThan(0);
 
         boolean found = false;
         int uid = getUid();
@@ -91,7 +91,7 @@
                 found = true;
             }
         }
-        assertTrue(found);
+        assertThat(found).isTrue();
     }
 
     // We check that a re-installation gives a change event (similar to an app upgrade).
@@ -108,7 +108,7 @@
         Thread.sleep(WAIT_TIME_SHORT);
 
         ConfigMetricsReportList reports = getReportList();
-        assertTrue(reports.getReportsCount() > 0);
+        assertThat(reports.getReportsCount()).isGreaterThan(0);
 
         boolean found = false;
         int uid = getUid();
@@ -118,7 +118,7 @@
                 found = true;
             }
         }
-        assertTrue(found);
+        assertThat(found).isTrue();
     }
 
     public void testChangeFromUninstall() throws Exception {
@@ -134,7 +134,7 @@
         Thread.sleep(WAIT_TIME_SHORT);
 
         ConfigMetricsReportList reports = getReportList();
-        assertTrue(reports.getReportsCount() > 0);
+        assertThat(reports.getReportsCount()).isGreaterThan(0);
 
         boolean found = false;
         for (ConfigMetricsReport report : reports.getReportsList()) {
@@ -143,6 +143,6 @@
                 found = true;
             }
         }
-        assertTrue(found);
+        assertThat(found).isTrue();
     }
 }
diff --git a/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java b/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java
index 7b95933..dc3e23b 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java
@@ -15,8 +15,8 @@
  */
 package android.cts.statsd.validation;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.cts.statsd.atom.DeviceAtomTestCase;
 import android.os.BatteryStatsProto;
@@ -70,11 +70,11 @@
 
         BatteryStatsProto batterystatsProto = getBatteryStatsProto();
         List<CountMetricData> countMetricData = getCountMetricDataList();
-        assertEquals(1, countMetricData.size());
-        assertEquals(1, countMetricData.get(0).getBucketInfoCount());
-        assertTrue(countMetricData.get(0).getBucketInfo(0).getCount() >= 2);
-        assertEquals(batterystatsProto.getSystem().getMisc().getNumConnectivityChanges(),
-                countMetricData.get(0).getBucketInfo(0).getCount());
+        assertThat(countMetricData).hasSize(1);
+        assertThat(countMetricData.get(0).getBucketInfoCount()).isEqualTo(1);
+        assertThat(countMetricData.get(0).getBucketInfo(0).getCount()).isAtLeast(2L);
+        assertThat(countMetricData.get(0).getBucketInfo(0).getCount()).isEqualTo(
+                (long) batterystatsProto.getSystem().getMisc().getNumConnectivityChanges());
     }
 
     public void testPowerUse() throws Exception {
@@ -104,19 +104,16 @@
         // Extract statsd data
         Atom atom = atomList.get(0);
         long statsdPowerNas = atom.getDeviceCalculatedPowerUse().getComputedPowerNanoAmpSecs();
-        assertTrue("Statsd: Non-positive power value.", statsdPowerNas > 0);
+        assertThat(statsdPowerNas).isGreaterThan(0L);
 
         // Extract BatteryStats data
         double bsPowerNas = batterystatsProto.getSystem().getPowerUseSummary().getComputedPowerMah()
                 * 1_000_000L * 3600L; /* mAh to nAs */
-        assertTrue("BatteryStats: Non-positive power value.", bsPowerNas > 0);
+        assertThat(bsPowerNas).isGreaterThan(0d);
 
-        assertTrue(
-                String.format("Statsd (%d) < Batterystats (%f)", statsdPowerNas, bsPowerNas),
-                statsdPowerNas > ALLOWED_FRACTIONAL_DIFFERENCE * bsPowerNas);
-        assertTrue(
-                String.format("Batterystats (%f) < Statsd (%d)", bsPowerNas, statsdPowerNas),
-                bsPowerNas > ALLOWED_FRACTIONAL_DIFFERENCE * statsdPowerNas);
+        assertThat((double) statsdPowerNas)
+                .isGreaterThan(ALLOWED_FRACTIONAL_DIFFERENCE * bsPowerNas);
+        assertThat(bsPowerNas).isGreaterThan(ALLOWED_FRACTIONAL_DIFFERENCE * statsdPowerNas);
     }
 
     public void testPowerBlameUid() throws Exception {
@@ -151,13 +148,15 @@
         for (Atom atom : atomList) {
             DeviceCalculatedPowerBlameUid item = atom.getDeviceCalculatedPowerBlameUid();
             if (item.getUid() == uid) {
-                assertFalse("Found multiple power values for uid " + uid, uidFound);
+                assertWithMessage("Found multiple power values for uid %s", uid)
+                        .that(uidFound).isFalse();
                 uidFound = true;
                 statsdUidPowerNas = item.getPowerNanoAmpSecs();
             }
         }
-        assertTrue("Statsd: No power value for uid " + uid, uidFound);
-        assertTrue("Statsd: Non-positive power value for uid " + uid, statsdUidPowerNas > 0);
+        assertWithMessage("Statsd: No power value for uid %s", uid).that(uidFound).isTrue();
+        assertWithMessage("Statsd: Non-positive power value for uid %s", uid)
+                .that(statsdUidPowerNas).isGreaterThan(0L);
 
         // Extract batterystats data
         double bsUidPowerNas = -1;
@@ -169,15 +168,13 @@
                         * 1_000_000L * 3600L; /* mAh to nAs */;
             }
         }
-        assertTrue("Batterystats: No power value for uid " + uid, hadUid);
-        assertTrue("BatteryStats: Non-positive power value for uid " + uid, bsUidPowerNas > 0);
+        assertWithMessage("Batterystats: No power value for uid %s", uid).that(hadUid).isTrue();
+        assertWithMessage("BatteryStats: Non-positive power value for uid %s", uid)
+                .that(bsUidPowerNas).isGreaterThan(0d);
 
-        assertTrue(
-                String.format("Statsd (%d) < Batterystats (%f).", statsdUidPowerNas, bsUidPowerNas),
-                statsdUidPowerNas > ALLOWED_FRACTIONAL_DIFFERENCE * bsUidPowerNas);
-        assertTrue(
-                String.format("Batterystats (%f) < Statsd (%d).", bsUidPowerNas, statsdUidPowerNas),
-                bsUidPowerNas > ALLOWED_FRACTIONAL_DIFFERENCE * statsdUidPowerNas);
+        assertThat((double) statsdUidPowerNas)
+                .isGreaterThan(ALLOWED_FRACTIONAL_DIFFERENCE * bsUidPowerNas);
+        assertThat(bsUidPowerNas).isGreaterThan(ALLOWED_FRACTIONAL_DIFFERENCE * statsdUidPowerNas);
     }
 
     public void testServiceStartCount() throws Exception {
@@ -192,16 +189,17 @@
 
         BatteryStatsProto batterystatsProto = getBatteryStatsProto();
         List<CountMetricData> countMetricData = getCountMetricDataList();
-        assertTrue(countMetricData.size() > 0);
+        assertThat(countMetricData).isNotEmpty();
         int uid = getUid();
         long countFromStatsd = 0;
         for (CountMetricData data : countMetricData) {
             List<DimensionsValue> dims = data.getDimensionLeafValuesInWhatList();
             if (dims.get(0).getValueInt() == uid) {
-                assertEquals(DEVICE_SIDE_TEST_PACKAGE, dims.get(1).getValueStr());
-                assertEquals(dims.get(2).getValueStr(), DEVICE_SIDE_TEST_FOREGROUND_SERVICE_NAME);
+                assertThat(dims.get(1).getValueStr()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
+                assertThat(dims.get(2).getValueStr())
+                        .isEqualTo(DEVICE_SIDE_TEST_FOREGROUND_SERVICE_NAME);
                 countFromStatsd = data.getBucketInfo(0).getCount();
-                assertTrue(countFromStatsd > 0);
+                assertThat(countFromStatsd).isGreaterThan(0L);
             }
         }
         long countFromBS = 0;
@@ -212,16 +210,16 @@
                         for (Service svc : pkg.getServicesList()) {
                             if (svc.getName().equals(DEVICE_SIDE_TEST_FOREGROUND_SERVICE_NAME)) {
                                 countFromBS = svc.getStartCount();
-                                assertTrue(countFromBS > 0);
+                                assertThat(countFromBS).isGreaterThan(0L);
                             }
                         }
                     }
                 }
             }
         }
-        assertTrue(countFromStatsd > 0);
-        assertTrue(countFromBS > 0);
-        assertEquals(countFromBS, countFromStatsd);
+        assertThat(countFromStatsd).isGreaterThan(0L);
+        assertThat(countFromBS).isGreaterThan(0L);
+        assertThat(countFromBS).isEqualTo(countFromStatsd);
     }
 
     public void testServiceLaunchCount() throws Exception {
@@ -236,16 +234,17 @@
 
         BatteryStatsProto batterystatsProto = getBatteryStatsProto();
         List<CountMetricData> countMetricData = getCountMetricDataList();
-        assertTrue(countMetricData.size() > 0);
+        assertThat(countMetricData).isNotEmpty();
         int uid = getUid();
         long countFromStatsd = 0;
         for (CountMetricData data : countMetricData) {
             List<DimensionsValue> dims = data.getDimensionLeafValuesInWhatList();
             if (dims.get(0).getValueInt() == uid) {
-                assertEquals(DEVICE_SIDE_TEST_PACKAGE, dims.get(1).getValueStr());
-                assertEquals(DEVICE_SIDE_TEST_FOREGROUND_SERVICE_NAME, dims.get(2).getValueStr());
+                assertThat(dims.get(1).getValueStr()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
+                assertThat(dims.get(2).getValueStr())
+                        .isEqualTo(DEVICE_SIDE_TEST_FOREGROUND_SERVICE_NAME);
                 countFromStatsd = data.getBucketInfo(0).getCount();
-                assertTrue(countFromStatsd > 0);
+                assertThat(countFromStatsd).isGreaterThan(0L);
             }
         }
         long countFromBS = 0;
@@ -256,15 +255,15 @@
                         for (Service svc : pkg.getServicesList()) {
                             if (svc.getName().equals(DEVICE_SIDE_TEST_FOREGROUND_SERVICE_NAME)) {
                                 countFromBS = svc.getLaunchCount();
-                                assertTrue(countFromBS > 0);
+                                assertThat(countFromBS).isGreaterThan(0L);
                             }
                         }
                     }
                 }
             }
         }
-        assertTrue(countFromStatsd > 0);
-        assertTrue(countFromBS > 0);
-        assertEquals(countFromBS, countFromStatsd);
+        assertThat(countFromStatsd).isGreaterThan(0L);
+        assertThat(countFromBS).isGreaterThan(0L);
+        assertThat(countFromBS).isEqualTo(countFromStatsd);
     }
 }
diff --git a/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java b/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java
index cfa0a37..9cb198c 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java
@@ -15,7 +15,7 @@
  */
 package android.cts.statsd.validation;
 
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
 
 import android.cts.statsd.atom.ProcStateTestCase;
 import android.service.procstats.ProcessState;
@@ -113,8 +113,8 @@
             }
         }
         // Verify that duration is within 1% difference
-        assertTrue(Math.abs(durationInTopStatsd - durationInTopProcStats) / durationInTopProcStats
-                < 0.1);
+        assertThat(Math.abs(durationInTopStatsd - durationInTopProcStats) / durationInTopProcStats)
+                .isLessThan(0.01);
     }
 
     public void testProcessStateCachedEmptyDuration() throws Exception {
@@ -178,8 +178,8 @@
             }
         }
         // Verify that duration is within 1% difference
-        assertTrue(Math.abs(durationInStatsd - durationInProcStats) / durationInProcStats
-                < 0.1);
+        assertThat(Math.abs(durationInStatsd - durationInProcStats) / durationInProcStats)
+                .isLessThan(0.01);
     }
 
     public void testProcessStatePssValue() throws Exception {
@@ -240,8 +240,8 @@
                 }
             }
         }
-        assertTrue(valueInProcStats > 0);
-        assertTrue(valueInStatsd == valueInProcStats);
+        assertThat(valueInProcStats).isGreaterThan(0d);
+        assertThat(valueInStatsd).isWithin(1e-10).of(valueInProcStats);
     }
 
     public void testProcessStateByPulling() throws Exception {
@@ -342,11 +342,11 @@
         }
 
         LogUtil.CLog.d("avg pss from procstats is " + pssAvgProcstats);
-        assertTrue(durationStatsd > 0);
-        assertTrue(durationStatsd == durationProcstats);
-        assertTrue(pssAvgStatsd == pssAvgProcstats);
-        assertTrue(ussAvgStatsd == ussAvgProcstats);
-        assertTrue(rssAvgStatsd == rssAvgProcstats);
+        assertThat(durationStatsd).isGreaterThan(0L);
+        assertThat(durationStatsd).isEqualTo(durationProcstats);
+        assertThat(pssAvgStatsd).isEqualTo(pssAvgProcstats);
+        assertThat(ussAvgStatsd).isEqualTo(ussAvgProcstats);
+        assertThat(rssAvgStatsd).isEqualTo(rssAvgProcstats);
     }
 
     public void testProcStatsPkgProcStats() throws Exception {
@@ -390,8 +390,11 @@
         Thread.sleep(WAIT_TIME_SHORT);
 
         List<Atom> statsdData = getGaugeMetricDataList();
-        assertTrue(statsdData.size() > 0);
-        assertTrue(statsdData.get(0).getProcStatsPkgProc().getProcStatsSection().getAvailablePagesList().size() > 0);
+        assertThat(statsdData).isNotEmpty();
+        assertThat(
+                statsdData.get(0).getProcStatsPkgProc().getProcStatsSection()
+                        .getAvailablePagesList()
+        ).isNotEmpty();
 
         List<ProcessStatsPackageProto> processStatsPackageProtoList = getAllProcStatsProto();
 
@@ -421,8 +424,8 @@
                         }
                     }
                 }
-                assertTrue(pkg.getServiceStatsCount() == 0);
-                assertTrue(pkg.getAssociationStatsCount() == 0);
+                assertThat(pkg.getServiceStatsCount()).isEqualTo(0L);
+                assertThat(pkg.getAssociationStatsCount()).isEqualTo(0L);
             }
         }
 
@@ -453,14 +456,14 @@
             serviceStatsCount += pkg.getServiceStatsCount();
             associationStatsCount += pkg.getAssociationStatsCount();
         }
-        assertTrue(serviceStatsCount > 0);
-        assertTrue(associationStatsCount > 0);
+        assertThat(serviceStatsCount).isGreaterThan(0);
+        assertThat(associationStatsCount).isGreaterThan(0);
 
         LogUtil.CLog.d("avg pss from procstats is " + pssAvgProcstats);
-        assertTrue(durationStatsd > 0);
-        assertTrue(durationStatsd == durationProcstats);
-        assertTrue(pssAvgStatsd == pssAvgProcstats);
-        assertTrue(ussAvgStatsd == ussAvgProcstats);
-        assertTrue(rssAvgStatsd == rssAvgProcstats);
+        assertThat(durationStatsd).isGreaterThan(0L);
+        assertThat(durationStatsd).isEqualTo(durationProcstats);
+        assertThat(pssAvgStatsd).isEqualTo(pssAvgProcstats);
+        assertThat(ussAvgStatsd).isEqualTo(ussAvgProcstats);
+        assertThat(rssAvgStatsd).isEqualTo(rssAvgProcstats);
     }
 }
diff --git a/hostsidetests/statsd/src/android/cts/statsd/validation/ValidationTests.java b/hostsidetests/statsd/src/android/cts/statsd/validation/ValidationTests.java
index ae40111..d4815b4 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/validation/ValidationTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/validation/ValidationTests.java
@@ -15,7 +15,8 @@
  */
 package android.cts.statsd.validation;
 
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.cts.statsd.atom.DeviceAtomTestCase;
 import android.os.BatteryPluggedStateEnum;
@@ -48,6 +49,8 @@
 import com.android.os.StatsLog.StatsLogReport;
 import com.android.tradefed.log.LogUtil.CLog;
 
+import com.google.common.collect.Range;
+
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -118,10 +121,8 @@
         for (EventMetricData event : data) {
             String tag = event.getAtom().getWakelockStateChanged().getTag();
             WakeLockLevelEnum type = event.getAtom().getWakelockStateChanged().getType();
-            assertTrue("Expected tag: " + EXPECTED_TAG + ", but got tag: " + tag,
-                    tag.equals(EXPECTED_TAG));
-            assertTrue("Expected wakelock level: " + EXPECTED_LEVEL + ", but got level: " + type,
-                    type == EXPECTED_LEVEL);
+            assertThat(tag).isEqualTo(EXPECTED_TAG);
+            assertThat(type).isEqualTo(EXPECTED_LEVEL);
         }
 
         //=================== verify that batterystats is correct ===============//
@@ -129,12 +130,10 @@
         android.os.TimerProto wl =
                 getBatteryStatsPartialWakelock(batterystatsProto, uid, EXPECTED_TAG);
 
-        assertNotNull(wl);
-        assertTrue(wl.getDurationMs() > 0);
-        assertTrue(wl.getMaxDurationMs() >= 400);
-        assertTrue(wl.getMaxDurationMs() < 700);
-        assertTrue(wl.getTotalDurationMs() >= 400);
-        assertTrue(wl.getTotalDurationMs() < 700);
+        assertThat(wl).isNotNull();
+        assertThat(wl.getDurationMs()).isGreaterThan(0L);
+        assertThat(wl.getMaxDurationMs()).isIn(Range.closedOpen(400L, 700L));
+        assertThat(wl.getTotalDurationMs()).isIn(Range.closedOpen(400L, 700L));
 
         setAodState(aodState); // restores AOD to initial state.
     }
@@ -174,36 +173,34 @@
         // Get the batterystats wakelock time and make sure it's reasonable.
         android.os.TimerProto bsWakelock =
                 getBatteryStatsPartialWakelock(batterystatsProto, EXPECTED_UID, EXPECTED_TAG);
-        assertNotNull("Could not find any partial wakelocks with uid " + EXPECTED_UID +
-                " and tag " + EXPECTED_TAG + " in BatteryStats", bsWakelock);
+        assertWithMessage(
+                "No partial wakelocks with uid %s and tag %s in BatteryStats",
+                EXPECTED_UID, EXPECTED_TAG
+        ).that(bsWakelock).isNotNull();
         long bsDurationMs = bsWakelock.getTotalDurationMs();
-        assertTrue("Wakelock in batterystats with uid " + EXPECTED_UID + " and tag "
-                + EXPECTED_TAG + "was too short. Expected " + MIN_DURATION +
-                ", received " + bsDurationMs, bsDurationMs >= MIN_DURATION);
-        assertTrue("Wakelock in batterystats with uid " + EXPECTED_UID + " and tag "
-                + EXPECTED_TAG + "was too long. Expected " + MAX_DURATION +
-                ", received " + bsDurationMs, bsDurationMs <= MAX_DURATION);
+        assertWithMessage(
+                "Wakelock in batterystats with uid %s and tag %s was too short or too long",
+                EXPECTED_UID, EXPECTED_TAG
+        ).that(bsDurationMs).isIn(Range.closed((long) MIN_DURATION, (long) MAX_DURATION));
 
         // Get the statsd wakelock time and make sure it's reasonable.
-        assertTrue("Could not find any wakelocks with uid " + EXPECTED_UID + " in statsd",
-                statsdWakelockData.containsKey(EXPECTED_UID));
-        assertTrue("Did not find any wakelocks with tag " + EXPECTED_TAG + " in statsd",
-                statsdWakelockData.get(EXPECTED_UID).containsKey(EXPECTED_TAG_HASH));
+        assertWithMessage("No wakelocks with uid %s in statsd", EXPECTED_UID)
+                .that(statsdWakelockData).containsKey(EXPECTED_UID);
+        assertWithMessage("No wakelocks with tag %s in statsd", EXPECTED_TAG)
+                .that(statsdWakelockData.get(EXPECTED_UID)).containsKey(EXPECTED_TAG_HASH);
         long statsdDurationMs = statsdWakelockData.get(EXPECTED_UID)
                 .get(EXPECTED_TAG_HASH) / 1_000_000;
-        assertTrue("Wakelock in statsd with uid " + EXPECTED_UID + " and tag " + EXPECTED_TAG +
-                        "was too short. Expected " + MIN_DURATION + ", received " +
-                        statsdDurationMs,
-                statsdDurationMs >= MIN_DURATION);
-        assertTrue("Wakelock in statsd with uid " + EXPECTED_UID + " and tag " + EXPECTED_TAG +
-                        "was too long. Expected " + MAX_DURATION + ", received " + statsdDurationMs,
-                statsdDurationMs <= MAX_DURATION);
+        assertWithMessage(
+                "Wakelock in statsd with uid %s and tag %s was too short or too long", 
+                EXPECTED_UID, EXPECTED_TAG
+        ).that(statsdDurationMs).isIn(Range.closed((long) MIN_DURATION, (long) MAX_DURATION));
 
         // Compare batterystats with statsd.
         long difference = Math.abs(statsdDurationMs - bsDurationMs);
-        assertTrue("For uid=" + EXPECTED_UID + " tag=" + EXPECTED_TAG + " had " +
-                        "BatteryStats=" + bsDurationMs + "ms but statsd=" + statsdDurationMs + "ms",
-                difference <= Math.max(bsDurationMs / 10, 10L));
+        assertWithMessage(
+                "For uid=%s tag=%s had BatteryStats=%s ms but statsd=%s ms",
+                EXPECTED_UID, EXPECTED_TAG, bsDurationMs, statsdDurationMs
+        ).that(difference).isAtMost(Math.max(bsDurationMs / 10, 10L));
 
         setAodState(aodState); // restores AOD to initial state.
     }
@@ -304,7 +301,7 @@
         for (DurationMetricData data : report.getDurationMetrics().getDataList()) {
             // Gets tag and uid.
             List<DimensionsValue> dims = data.getDimensionLeafValuesInWhatList();
-            assertTrue("Expected 2 dimensions, received " + dims.size(), dims.size() == 2);
+            assertThat(dims).hasSize(2);
             boolean hasTag = false;
             long tag = 0;
             int uid = -1;
@@ -317,8 +314,8 @@
                     tag = dim.getValueStrHash();
                 }
             }
-            assertTrue("Did not receive a tag for the wakelock", hasTag);
-            assertTrue("Did not receive a uid for the wakelock", uid != -1);
+            assertWithMessage("Did not receive a tag for the wakelock").that(hasTag).isTrue();
+            assertWithMessage("Did not receive a uid for the wakelock").that(uid).isNotEqualTo(-1);
 
             // Gets duration.
             for (DurationBucketInfo bucketInfo : data.getBucketInfoList()) {
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/src/android/theme/cts/ThemeHostTest.java b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
index f31b081..2fcbb5b 100644
--- a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
+++ b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
@@ -295,7 +295,7 @@
 
     private static int getDensityForDevice(ITestDevice device) throws DeviceNotAvailableException {
         final String densityProp;
-        if (device.getSerialNumber().startsWith("emulator-")) {
+        if (isEmulator(device)) {
             densityProp = DENSITY_PROP_EMULATOR;
         } else {
             densityProp = DENSITY_PROP_DEVICE;
@@ -308,4 +308,9 @@
                 || hardwareTypeString.contains("android.hardware.type.television")
                 || hardwareTypeString.contains("android.hardware.type.automotive");
     }
+
+    private static boolean isEmulator(ITestDevice device) {
+        // Expecting something like "emulator-XXXX" or "EMULATORXXXX".
+        return device.getSerialNumber().toLowerCase().startsWith("emulator");
+    }
 }
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/hostsidetests/wifibroadcasts/src/android/wifibroadcasts/cts/WifiBroadcastsHostJUnit4Test.java b/hostsidetests/wifibroadcasts/src/android/wifibroadcasts/cts/WifiBroadcastsHostJUnit4Test.java
index e01d682..8dcc4d3 100644
--- a/hostsidetests/wifibroadcasts/src/android/wifibroadcasts/cts/WifiBroadcastsHostJUnit4Test.java
+++ b/hostsidetests/wifibroadcasts/src/android/wifibroadcasts/cts/WifiBroadcastsHostJUnit4Test.java
@@ -71,9 +71,14 @@
     private static final String PROHIBITED_STRING = "UNEXPECTED WIFI BROADCAST RECEIVED";
 
     /**
-     * The maximim number of times to attempt a ping
+     * The maximum total number of times to attempt a ping.
      */
-    private static final int MAXIMUM_PING_TRIES = 30;
+    private static final int MAXIMUM_PING_TRIES = 180;
+
+    /**
+     * The maximum number of times to attempt a ping before toggling wifi.
+     */
+    private static final int MAXIMUM_PING_TRIES_PER_CONNECTION = 60;
 
     /**
      * Name for wifi feature test
@@ -121,6 +126,10 @@
         CommandResult pingCommandResult = null;
         boolean pingSucceeded = false;
         for (int tries = 0; tries < MAXIMUM_PING_TRIES; tries++) {
+            if (tries > 0 && tries % MAXIMUM_PING_TRIES_PER_CONNECTION == 0) {
+                // if we have been trying for a while, toggle wifi off and then on.
+                device.executeShellCommand("svc wifi disable; sleep 1; svc wifi enable; sleep 3");
+            }
             // We don't require internet connectivity, just a configured address
             pingCommandResult = device.executeShellV2Command("ping -c 4 -W 2 -t 1 8.8.8.8");
             pingResult = String.join("/", pingCommandResult.getStdout(),
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..359e03f
--- /dev/null
+++ b/libs/install/testapp/src/com/android/cts/install/lib/testapp/ProcessUserData.java
@@ -0,0 +1,123 @@
+/*
+ * 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 android.os.Process;
+
+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
+ * and user handle 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, and confirm
+     * the user data is for the correct user.
+     *
+     * @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();
+
+        String userHandle = Process.myUserHandle().toString();
+
+        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. Also ensure that the user data is
+        // for the correct user.
+        File versionFile = new File(context.getFilesDir(), "userdata.txt");
+        try {
+            Scanner s = new Scanner(versionFile);
+            int userDataVersion = s.nextInt();
+            s.nextLine();
+
+            if (userDataVersion > appVersion) {
+                throw new UserDataException("User data is from version " + userDataVersion
+                        + ", which is not compatible with this version " + appVersion
+                        + " of the RollbackTestApp");
+            }
+
+            String readUserHandle = s.nextLine();
+            s.close();
+
+            if (!readUserHandle.equals(userHandle)) {
+                throw new UserDataException("User handle expected to be: " + userHandle
+                        + ", but was actually " + readUserHandle);
+            }
+        } 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.println(userHandle);
+            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/AccessibilityGestureEventTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityGestureEventTest.java
new file mode 100644
index 0000000..abf9be8
--- /dev/null
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityGestureEventTest.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.AccessibilityGestureEvent;
+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.AccessibilityGestureEvent}.
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityGestureEventTest {
+
+    @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.
+        AccessibilityGestureEvent sentGestureEvent = new AccessibilityGestureEvent(
+                SENT_GESTURE, TARGET_DISPLAY);
+
+        // Marshal and unmarshal the gesture info.
+        Parcel parcel = Parcel.obtain();
+        sentGestureEvent.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        AccessibilityGestureEvent receivedGestureEvent =
+                AccessibilityGestureEvent.CREATOR.createFromParcel(parcel);
+
+        // Make sure all fields properly marshaled.
+        assertEqualsGestureEvent(sentGestureEvent, receivedGestureEvent);
+
+        parcel.recycle();
+    }
+
+    /**
+     * Tests whether the value of Getter method is as same as the parameter of the constructor.
+     *
+     */
+    @SmallTest
+    @Test
+    public void testGetterMethods() {
+        AccessibilityGestureEvent actualGesture = new AccessibilityGestureEvent(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() {
+        AccessibilityGestureEvent event1 = new AccessibilityGestureEvent(SENT_GESTURE,TARGET_DISPLAY);
+        assertSame("accessibility gesture infos always return 0 for this method.", 0,
+                event1.describeContents());
+        AccessibilityGestureEvent event2 = new AccessibilityGestureEvent(
+                AccessibilityService.GESTURE_SWIPE_LEFT, TARGET_DISPLAY);
+        assertSame("accessibility gesture infos always return 0 for this method.", 0,
+                event2.describeContents());
+    }
+
+    private void assertEqualsGestureEvent(AccessibilityGestureEvent sentGestureEvent,
+            AccessibilityGestureEvent receivedGestureEvent) {
+        assertEquals("getDisplayId has incorrectValue", sentGestureEvent.getDisplayId(),
+                receivedGestureEvent.getDisplayId());
+        assertEquals("getGestureId has incorrectValue", sentGestureEvent.getGestureId(),
+                receivedGestureEvent.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..c48d402 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
@@ -16,15 +16,26 @@
 
 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.Bitmap;
 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.Spannable;
+import android.text.SpannableString;
 import android.text.TextUtils;
+import android.text.style.ImageSpan;
 import android.util.ArrayMap;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
@@ -35,6 +46,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 +61,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 +91,7 @@
      * Tests if {@link AccessibilityNodeInfo}s are properly reused.
      */
     @SmallTest
+    @Test
     public void testReuse() {
         AccessibilityEvent firstInfo = AccessibilityEvent.obtain();
         firstInfo.recycle();
@@ -78,6 +103,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 +121,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 +135,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 +161,7 @@
      * Tests whether we catch addition of an action with invalid id.
      */
     @SmallTest
+    @Test
     public void testCreateInvalidActionId() {
         try {
             new AccessibilityAction(3, null);
@@ -145,6 +174,7 @@
      * Tests whether accessibility actions are properly removed.
      */
     @SmallTest
+    @Test
     public void testRemoveActions() {
         AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
 
@@ -173,6 +203,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";
@@ -181,6 +212,7 @@
         info.setText(updatingString);
         info.setError(updatingString);
         info.setContentDescription(updatingString);
+        info.setStateDescription(updatingString);
 
         updatingString.delete(0, updatingString.length());
         updatingString.append(newText);
@@ -188,9 +220,31 @@
         assertTrue(TextUtils.equals(originalText, info.getText()));
         assertTrue(TextUtils.equals(originalText, info.getError()));
         assertTrue(TextUtils.equals(originalText, info.getContentDescription()));
+        assertTrue(TextUtils.equals(originalText, info.getStateDescription()));
     }
 
     @SmallTest
+    @Test
+    public void testSetTextWithImageSpan_shouldTextSetToInfo() {
+        final Bitmap bitmap = Bitmap.createBitmap(/* width= */10, /* height= */10,
+                Bitmap.Config.ARGB_8888);
+        final ImageSpan imageSpan = new ImageSpan(getContext(), bitmap);
+        final String testString = "test string";
+        final String replacementString = " ";
+        final SpannableString textWithImageSpan = new SpannableString(testString);
+        final int indexIconStart = testString.indexOf(replacementString);
+        final int indexIconEnd = indexIconStart + replacementString.length();
+        textWithImageSpan.setSpan(imageSpan, /* start= */indexIconStart,/* end= */indexIconEnd,
+                /* flags= */Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+        AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+        info.setText(textWithImageSpan);
+
+        assertEquals(testString, info.getText().toString());
+    }
+
+    @SmallTest
+    @Test
     public void testIsHeadingTakesOldApiIntoAccount() {
         final AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
         assertFalse(info.isHeading());
@@ -213,6 +267,7 @@
         info.setBoundsInScreen(new Rect(2,2,2,2));
         info.setClassName("foo.bar.baz.Class");
         info.setContentDescription("content description");
+        info.setStateDescription("state description");
         info.setTooltipText("tooltip");
         info.setPackageName("foo.bar.baz");
         info.setText("text");
@@ -346,6 +401,8 @@
                 receivedInfo.getClassName());
         assertEquals("contentDescription has incorrect value", expectedInfo.getContentDescription(),
                 receivedInfo.getContentDescription());
+        assertEquals("stateDescription has incorrect value", expectedInfo.getStateDescription(),
+                receivedInfo.getStateDescription());
         assertEquals("tooltip text has incorrect value", expectedInfo.getTooltipText(),
                 receivedInfo.getTooltipText());
         assertEquals("packageName has incorrect value", expectedInfo.getPackageName(),
@@ -399,11 +456,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());
         }
@@ -528,6 +585,7 @@
         assertTrue("boundsInScreen not properly recycled", bounds.isEmpty());
         assertNull("className not properly recycled", info.getClassName());
         assertNull("contentDescription not properly recycled", info.getContentDescription());
+        assertNull("stateDescription not properly recycled", info.getStateDescription());
         assertNull("tooltiptext not properly recycled", info.getTooltipText());
         assertNull("packageName not properly recycled", info.getPackageName());
         assertNull("text not properly recycled", info.getText());
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 b37f5cb..d96b545 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..4dd1981 100644
--- a/tests/accessibilityservice/res/values/strings.xml
+++ b/tests/accessibilityservice/res/values/strings.xml
@@ -139,6 +139,8 @@
 
     <string name="a_b">A B</string>
 
+    <string name="testContentDescription">testContentDescription</string>
+
     <string name="german_text_with_strong_s">ß</string>
 
     <string name="android_wiki_search">Android is a Linux-based</string>
@@ -147,7 +149,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 +174,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..9166356
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEmbeddedDisplayTest.java
@@ -0,0 +1,238 @@
+/*
+ * 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.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();
+
+    @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 activityBound = new Rect();
+        oldActivityWindow.getBoundsInScreen(activityBound);
+
+        final int width = activityBound.width() / 2;
+        final int height = activityBound.height() / 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);
+        newActivityWindow.getBoundsInScreen(activityBound);
+
+        assertEquals(height, activityBound.height());
+        assertEquals(width, activityBound.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);
+        }
+
+        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..28e8f67 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java
@@ -14,17 +14,20 @@
 
 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.AccessibilityGestureEvent;
 import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.GestureDescription;
 import android.accessibilityservice.GestureDescription.StrokeDescription;
@@ -36,15 +39,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 +64,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 +114,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 +151,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 +246,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));
+        }
+
+        AccessibilityGestureEvent expectedGestureEvent = new AccessibilityGestureEvent(gestureId,
+                displayId);
+        assertEquals(1, mService.getGestureInfoSize());
+        assertEquals(expectedGestureEvent.toString(), mService.getGestureInfo(0).toString());
     }
 
     /** Create a path from startPoint, moving by delta1, then delta2. (delta2 may be null.) */
@@ -196,25 +276,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 +359,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..3d013cb 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java
@@ -30,10 +30,11 @@
 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;
+import android.graphics.Bitmap;
 import android.graphics.RectF;
 import android.os.Bundle;
 import android.os.Message;
@@ -42,6 +43,8 @@
 import android.text.Spanned;
 import android.text.TextUtils;
 import android.text.style.ClickableSpan;
+import android.text.style.ImageSpan;
+import android.text.style.ReplacementSpan;
 import android.text.style.URLSpan;
 import android.view.View;
 import android.view.accessibility.AccessibilityManager;
@@ -60,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.Arrays;
@@ -76,14 +80,20 @@
     private static UiAutomation sUiAutomation;
     final Object mClickableSpanCallbackLock = new Object();
     final AtomicBoolean mClickableSpanCalled = new AtomicBoolean(false);
-    
 
     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();
@@ -228,6 +238,25 @@
     }
 
     @Test
+    public void testImageSpan_accessibilityServiceShouldSeeContentDescription() {
+        final TextView textView = (TextView) mActivity.findViewById(R.id.text);
+        final Bitmap bitmap = Bitmap.createBitmap(/* width= */10, /* height= */10,
+                Bitmap.Config.ARGB_8888);
+        final ImageSpan imageSpan = new ImageSpan(mActivity, bitmap);
+        final String contentDescription = mActivity.getString(R.string.contentDescription);
+        imageSpan.setContentDescription(contentDescription);
+        final SpannableString textWithImageSpan =
+                new SpannableString(mActivity.getString(R.string.a_b));
+        textWithImageSpan.setSpan(imageSpan, /* start= */0, /* end= */1, /* flags= */0);
+        makeTextViewVisibleAndSetText(textView, textWithImageSpan);
+
+        ReplacementSpan replacementSpanFromA11y = findSingleSpanInViewWithText(R.string.a_b,
+                ReplacementSpan.class);
+        
+        assertEquals(contentDescription, replacementSpanFromA11y.getContentDescription());
+    }
+
+    @Test
     public void testTextLocations_textViewShouldProvideWhenRequested() {
         final TextView textView = (TextView) mActivity.findViewById(R.id.text);
         // Use text with a strong s, since that gets replaced with a double s for all caps.
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..f32871b
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
@@ -19,8 +19,10 @@
 import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterForEventType;
 import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterWindowsChangedWithChangeTypes;
 import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen;
 import static android.accessibilityservice.cts.utils.AsyncUtils.DEFAULT_TIMEOUT_MS;
 import static android.accessibilityservice.cts.utils.DisplayUtils.getStatusBarHeight;
+import static android.accessibilityservice.cts.utils.DisplayUtils.VirtualDisplaySession;
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED;
 import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_CLICKED;
@@ -48,10 +50,11 @@
 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.Activity;
 import android.app.ActivityTaskManager;
 import android.app.Instrumentation;
 import android.app.UiAutomation;
@@ -59,8 +62,11 @@
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.platform.test.annotations.AppModeFull;
 import android.test.suitebuilder.annotation.MediumTest;
+import android.util.SparseArray;
+import android.view.Display;
 import android.view.Gravity;
 import android.view.View;
 import android.view.WindowManager;
@@ -81,6 +87,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;
@@ -108,14 +115,18 @@
     private static UiAutomation sUiAutomation;
 
     private AccessibilityWindowQueryActivity mActivity;
+    private Activity mActivityOnVirtualDisplay;
 
-    @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 {
@@ -313,7 +324,7 @@
         try {
             // Add two more windows.
             final View views[];
-            views = addTwoAppPanelWindows();
+            views = addTwoAppPanelWindows(mActivity);
 
             try {
                 // Put accessibility focus in the first app window.
@@ -611,7 +622,7 @@
 
     @Test
     public void testGetWindows_resultIsSortedByLayerDescending() throws TimeoutException {
-        addTwoAppPanelWindows();
+        addTwoAppPanelWindows(mActivity);
         List<AccessibilityWindowInfo> windows = sUiAutomation.getWindows();
 
         AccessibilityWindowInfo windowAddedFirst = findWindow(windows, R.string.button1);
@@ -621,11 +632,67 @@
         assertThat(windows, new IsSortedBy<>(w -> w.getLayer(), /* ascending */ false));
     }
 
+    @Test
+    public void testGetWindowsOnAllDisplays_resultIsSortedByLayerDescending() throws Exception {
+        addTwoAppPanelWindows(mActivity);
+        // Creates a virtual display.
+        try (final VirtualDisplaySession displaySession = new VirtualDisplaySession()) {
+            final int virtualDisplayId =
+                    displaySession.createDisplayWithDefaultDisplayMetricsAndWait(
+                            sInstrumentation.getContext(), false).getDisplayId();
+            // Launches an activity on virtual display.
+            mActivityOnVirtualDisplay = launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen(
+                    sInstrumentation, sUiAutomation,
+                    AccessibilityEmbeddedDisplayTest.EmbeddedDisplayActivity.class,
+                    virtualDisplayId);
+            // Adds two app panel windows on activity of virtual display.
+            addTwoAppPanelWindows(mActivityOnVirtualDisplay);
+
+            // Gets all windows.
+            SparseArray<List<AccessibilityWindowInfo>> allWindows =
+                    sUiAutomation.getWindowsOnAllDisplays();
+            assertNotNull(allWindows);
+            assertTrue(allWindows.size() == 2);
+
+            // Gets windows on default display.
+            List<AccessibilityWindowInfo> windowsOnDefaultDisplay =
+                    allWindows.get(Display.DEFAULT_DISPLAY);
+            assertNotNull(windowsOnDefaultDisplay);
+            assertTrue(windowsOnDefaultDisplay.size() > 0);
+
+            AccessibilityWindowInfo windowAddedFirst =
+                    findWindow(windowsOnDefaultDisplay, R.string.button1);
+            AccessibilityWindowInfo windowAddedSecond =
+                    findWindow(windowsOnDefaultDisplay, R.string.button2);
+            assertThat(windowAddedFirst.getLayer(), lessThan(windowAddedSecond.getLayer()));
+            assertThat(windowsOnDefaultDisplay,
+                    new IsSortedBy<>(w -> w.getLayer(), /* ascending */ false));
+
+            // Gets windows on virtual display.
+            List<AccessibilityWindowInfo> windowsOnVirtualDisplay =
+                    allWindows.get(virtualDisplayId);
+            assertNotNull(windowsOnVirtualDisplay);
+            assertTrue(windowsOnVirtualDisplay.size() > 0);
+
+            AccessibilityWindowInfo windowAddedFirstOnVirtualDisplay =
+                    findWindow(windowsOnVirtualDisplay, R.string.button1);
+            AccessibilityWindowInfo windowAddedSecondOnVirtualDisplay =
+                    findWindow(windowsOnVirtualDisplay, R.string.button2);
+            assertThat(windowAddedFirstOnVirtualDisplay.getLayer(),
+                    lessThan(windowAddedSecondOnVirtualDisplay.getLayer()));
+            assertThat(windowsOnVirtualDisplay,
+                    new IsSortedBy<>(w -> w.getLayer(), /* ascending */ false));
+
+            mActivityOnVirtualDisplay.finish();
+        }
+    }
+
     private AccessibilityWindowInfo findWindow(List<AccessibilityWindowInfo> windows,
             int btnTextRes) {
         return windows.stream()
                 .filter(w -> w.getRoot()
-                        .findAccessibilityNodeInfosByText(mActivity.getString(btnTextRes))
+                        .findAccessibilityNodeInfosByText(
+                                sInstrumentation.getTargetContext().getString(btnTextRes))
                         .size() == 1)
                 .findFirst()
                 .get();
@@ -714,7 +781,7 @@
         }
     }
 
-    private View[] addTwoAppPanelWindows() throws TimeoutException {
+    private View[] addTwoAppPanelWindows(Activity activity) throws TimeoutException {
         setAccessInteractiveWindowsFlag();
         sUiAutomation
                 .waitForIdle(TIMEOUT_WINDOW_STATE_IDLE, DEFAULT_TIMEOUT_MS);
@@ -722,16 +789,16 @@
         return new View[] {
                 addWindow(R.string.button1, params -> {
                     params.gravity = Gravity.TOP;
-                    params.y = getStatusBarHeight(mActivity);
-                }),
+                    params.y = getStatusBarHeight(activity);
+                }, activity),
                 addWindow(R.string.button2, params -> {
                     params.gravity = Gravity.BOTTOM;
-                })
+                }, activity)
         };
     }
 
-    private Button addWindow(int btnTextRes, Consumer<WindowManager.LayoutParams> configure)
-            throws TimeoutException {
+    private Button addWindow(int btnTextRes, Consumer<WindowManager.LayoutParams> configure,
+            Activity activity) throws TimeoutException {
         AtomicReference<Button> result = new AtomicReference<>();
         sUiAutomation.executeAndWaitForEvent(() -> {
             sInstrumentation.runOnMainSync(() -> {
@@ -743,13 +810,13 @@
                         | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
                 params.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
-                params.token = mActivity.getWindow().getDecorView().getWindowToken();
+                params.token = activity.getWindow().getDecorView().getWindowToken();
                 configure.accept(params);
 
-                final Button button = new Button(mActivity);
+                final Button button = new Button(activity);
                 button.setText(btnTextRes);
                 result.set(button);
-                mActivity.getWindowManager().addView(button, params);
+                activity.getWindowManager().addView(button, params);
             });
         }, filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_ADDED), DEFAULT_TIMEOUT_MS);
         return result.get();
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..049bae1 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.AccessibilityGestureEvent;
 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<AccessibilityGestureEvent> mCollectedGestureEvents = 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(AccessibilityGestureEvent gestureEvent) {
+        super.onGesture(gestureEvent);
+        synchronized (mCollectedGestureEvents) {
+            mCollectedGestureEvents.add(gestureEvent);
+            mCollectedGestureEvents.notifyAll(); // Stop waiting for gesture.
         }
         return true;
     }
@@ -48,6 +55,9 @@
         synchronized (mCollectedGestures) {
             mCollectedGestures.clear();
         }
+        synchronized (mCollectedGestureEvents) {
+            mCollectedGestureEvents.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(AccessibilityGestureEvent)} to collect next gesture. */
+    public void waitUntilGestureInfo() {
+        synchronized (mCollectedGestureEvents) {
+            //Assume the size of mCollectedGestures is changed before mCollectedGestureEvents.
+            if (mCollectedGestureEvents.size() > 0) {
                 return;
             }
             try {
-                mCollectedGestures.wait(GESTURE_RECOGNIZE_TIMEOUT_MS);
+                mCollectedGestureEvents.wait(GESTURE_RECOGNIZE_TIMEOUT_MS);
             } catch (InterruptedException e) {
                 throw new RuntimeException(e);
             }
         }
     }
 
+    public int getGestureInfoSize() {
+        synchronized (mCollectedGestureEvents) {
+            return mCollectedGestureEvents.size();
+        }
+    }
+
+    public AccessibilityGestureEvent getGestureInfo(int index) {
+        synchronized (mCollectedGestureEvents) {
+            return mCollectedGestureEvents.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..61a1cea
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorationStubAccessibilityService.java
@@ -0,0 +1,44 @@
+/**
+ * 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_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_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..4beae0b
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
@@ -0,0 +1,346 @@
+/*
+ * 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_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_initiatesTouchExploration() {
+        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_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_doesNotInitiateTouchExploration() {
+        if (!mHasTouchscreen || !mScreenBigEnough) return;
+        dispatch(swipe(mTapLocation, add(mTapLocation, mSwipeDistance, 0)));
+        mHoverListener.assertNonePropagated();
+        mTouchListener.assertNonePropagated();
+        mService.assertPropagated(
+                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_dispatchesEventsBetweenFingers() {
+        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_initiatesTouchExploration() {
+        if (!mHasTouchscreen || !mScreenBigEnough) return;
+        dispatch(click(mTapLocation));
+        mHoverListener.assertPropagated(ACTION_HOVER_ENTER, ACTION_HOVER_EXIT);
+        mTouchListener.assertNonePropagated();
+        mService.assertPropagated(
+                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_performsClick() {
+        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_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_doesNotPerformClick() {
+        if (!mHasTouchscreen || !mScreenBigEnough) return;
+        dispatch(doubleTap(mTapLocation));
+        mHoverListener.assertNonePropagated();
+        mTouchListener.assertNonePropagated();
+        mService.assertPropagated(
+                 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_performsLongClick() {
+        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_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_doesNotPerformLongClick() {
+        if (!mHasTouchscreen || !mScreenBigEnough) return;
+        dispatch(doubleTap(mTapLocation));
+        mHoverListener.assertNonePropagated();
+        mTouchListener.assertNonePropagated();
+        mService.assertPropagated(
+                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/ActivityLaunchUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
index 6902c8f..c3220ad 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
@@ -17,11 +17,13 @@
 import static android.accessibilityservice.cts.utils.AsyncUtils.DEFAULT_TIMEOUT_MS;
 
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.app.Activity;
+import android.app.ActivityOptions;
 import android.app.Instrumentation;
 import android.app.UiAutomation;
 import android.content.Context;
@@ -29,10 +31,13 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.SparseArray;
+import android.view.Display;
 import android.view.InputDevice;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
@@ -62,40 +67,37 @@
     public static <T extends Activity> T launchActivityAndWaitForItToBeOnscreen(
             Instrumentation instrumentation, UiAutomation uiAutomation,
             ActivityTestRule<T> rule) throws Exception {
-        final int[] location = new int[2];
-        final StringBuilder activityPackage = new StringBuilder();
-        final Rect bounds = new Rect();
-        final StringBuilder activityTitle = new StringBuilder();
-        // Make sure we get window events, so we'll know when the window appears
-        AccessibilityServiceInfo info = uiAutomation.getServiceInfo();
-        info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
-        uiAutomation.setServiceInfo(info);
-        homeScreenOrBust(instrumentation.getContext(), uiAutomation);
-        final AccessibilityEvent awaitedEvent = uiAutomation.executeAndWaitForEvent(
-                () -> {
-                    mTempActivity = rule.launchActivity(null);
-                    final StringBuilder builder = new StringBuilder();
-                    instrumentation.runOnMainSync(() -> {
-                        mTempActivity.getWindow().getDecorView().getLocationOnScreen(location);
-                        activityPackage.append(mTempActivity.getPackageName());
-                    });
-                    instrumentation.waitForIdleSync();
-                    activityTitle.append(getActivityTitle(instrumentation, mTempActivity));
-                },
-                (event) -> {
-                    final AccessibilityWindowInfo window =
-                            findWindowByTitle(uiAutomation, activityTitle);
-                    if (window == null) return false;
-                    window.getBoundsInScreen(bounds);
-                    mTempActivity.getWindow().getDecorView().getLocationOnScreen(location);
-                    if (bounds.isEmpty()) {
-                        return false;
-                    }
-                    return (!bounds.isEmpty())
-                            && (bounds.left == location[0]) && (bounds.top == location[1]);
-                }, DEFAULT_TIMEOUT_MS);
-        assertNotNull(awaitedEvent);
-        return (T) mTempActivity;
+        ActivityLauncher activityLauncher = new ActivityLauncher() {
+            @Override
+            Activity launchActivity() {
+                return rule.launchActivity(null);
+            }
+        };
+        return launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen(instrumentation,
+                uiAutomation, activityLauncher, Display.DEFAULT_DISPLAY);
+    }
+
+    /**
+     * If this activity would be launched at virtual display, please finishes this activity before
+     * this test ended. Otherwise it will be displayed on default display and impacts the next test.
+     */
+    public static <T extends Activity> T launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen(
+            Instrumentation instrumentation, UiAutomation uiAutomation, Class<T> clazz,
+            int displayId) throws Exception {
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(displayId);
+        final Intent intent = new Intent(instrumentation.getTargetContext(), clazz);
+        // Add clear task because this activity may on other display.
+        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK|Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        ActivityLauncher activityLauncher = new ActivityLauncher() {
+            @Override
+            Activity launchActivity() {
+                return instrumentation.startActivitySync(intent, options.toBundle());
+            }
+        };
+        return launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen(instrumentation,
+                uiAutomation, activityLauncher, displayId);
     }
 
     public static CharSequence getActivityTitle(
@@ -108,16 +110,15 @@
     public static AccessibilityWindowInfo findWindowByTitle(
             UiAutomation uiAutomation, CharSequence title) {
         final List<AccessibilityWindowInfo> windows = uiAutomation.getWindows();
-        AccessibilityWindowInfo returnValue = null;
-        for (int i = 0; i < windows.size(); i++) {
-            final AccessibilityWindowInfo window = windows.get(i);
-            if (TextUtils.equals(title, window.getTitle())) {
-                returnValue = window;
-            } else {
-                window.recycle();
-            }
-        }
-        return returnValue;
+        return findWindowByTitleWithList(title, windows);
+    }
+
+    public static AccessibilityWindowInfo findWindowByTitleAndDisplay(
+            UiAutomation uiAutomation, CharSequence title, int displayId) {
+        final SparseArray<List<AccessibilityWindowInfo>> allWindows =
+                uiAutomation.getWindowsOnAllDisplays();
+        final List<AccessibilityWindowInfo> windowsOfDisplay = allWindows.get(displayId);
+        return findWindowByTitleWithList(title, windowsOfDisplay);
     }
 
     public static void homeScreenOrBust(Context context, UiAutomation uiAutomation) {
@@ -230,4 +231,74 @@
             uiAutomation.setOnAccessibilityEventListener(null);
         }
     }
+
+    private static <T extends Activity> T launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen(
+            Instrumentation instrumentation, UiAutomation uiAutomation,
+            ActivityLauncher activityLauncher, int displayId) throws Exception {
+        final int[] location = new int[2];
+        final StringBuilder activityPackage = new StringBuilder();
+        final Rect bounds = new Rect();
+        final StringBuilder activityTitle = new StringBuilder();
+        // Make sure we get window events, so we'll know when the window appears
+        AccessibilityServiceInfo info = uiAutomation.getServiceInfo();
+        info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
+        uiAutomation.setServiceInfo(info);
+        // There is no any window on virtual display even doing GLOBAL_ACTION_HOME, so only
+        // checking the home screen for default display.
+        if (displayId == Display.DEFAULT_DISPLAY) {
+            homeScreenOrBust(instrumentation.getContext(), uiAutomation);
+        }
+
+        final AccessibilityEvent awaitedEvent = uiAutomation.executeAndWaitForEvent(
+                () -> {
+                    mTempActivity = activityLauncher.launchActivity();
+                    instrumentation.runOnMainSync(() -> {
+                        mTempActivity.getWindow().getDecorView().getLocationOnScreen(location);
+                        activityPackage.append(mTempActivity.getPackageName());
+                    });
+                    instrumentation.waitForIdleSync();
+                    activityTitle.append(getActivityTitle(instrumentation, mTempActivity));
+                },
+                (event) -> {
+                    AccessibilityNodeInfo node = event.getSource();
+                    if (node != null) {
+                        final AccessibilityWindowInfo window = node.getWindow();
+                        if(TextUtils.equals(activityTitle, window.getTitle())) {
+                            return  true;
+                        }
+                    }
+                    final AccessibilityWindowInfo window =
+                            findWindowByTitleAndDisplay(uiAutomation, activityTitle, displayId);
+                    if (window == null) return false;
+                    window.getBoundsInScreen(bounds);
+                    mTempActivity.getWindow().getDecorView().getLocationOnScreen(location);
+                    if (bounds.isEmpty()) {
+                        return false;
+                    }
+                    return (!bounds.isEmpty())
+                            && (bounds.left == location[0]) && (bounds.top == location[1]);
+                }, DEFAULT_TIMEOUT_MS);
+        assertNotNull(awaitedEvent);
+        return (T) mTempActivity;
+    }
+
+    private static AccessibilityWindowInfo findWindowByTitleWithList(CharSequence title,
+            List<AccessibilityWindowInfo> windows) {
+        AccessibilityWindowInfo returnValue = null;
+        if (windows != null && windows.size() > 0) {
+            for (int i = 0; i < windows.size(); i++) {
+                final AccessibilityWindowInfo window = windows.get(i);
+                if (TextUtils.equals(title, window.getTitle())) {
+                    returnValue = window;
+                } else {
+                    window.recycle();
+                }
+            }
+        }
+        return returnValue;
+    }
+
+    private static abstract class ActivityLauncher {
+        abstract Activity launchActivity();
+    }
 }
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..5c2e4cf 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"/>
@@ -28,6 +29,7 @@
         <option name="test-file-name" value="CtsAppTestStubsApp3.apk" />
         <option name="test-file-name" value="CtsAppTestStubsApp2.apk" />
         <option name="test-file-name" value="CtsAppTestCases.apk" />
+        <option name="test-file-name" value="CtsBadProviderStubs.apk" />
         <option name="test-file-name" value="CtsCantSaveState1.apk" />
         <option name="test-file-name" value="CtsCantSaveState2.apk" />
         <option name="test-file-name" value="NotificationDelegator.apk" />
diff --git a/tests/app/BadProviderStubs/Android.bp b/tests/app/BadProviderStubs/Android.bp
new file mode 100644
index 0000000..3180ef5
--- /dev/null
+++ b/tests/app/BadProviderStubs/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: "CtsBadProviderStubs",
+    defaults: ["cts_support_defaults"],
+    static_libs: ["android-support-annotations"],
+    srcs: ["src/**/*.java"],
+    sdk_version: "current",
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    optimize: {
+        enabled: false,
+    },
+    dex_preopt: {
+        enabled: false,
+    },
+}
diff --git a/libs/rollback/testapp/res_v1/values/values.xml b/tests/app/BadProviderStubs/AndroidManifest.xml
similarity index 66%
copy from libs/rollback/testapp/res_v1/values/values.xml
copy to tests/app/BadProviderStubs/AndroidManifest.xml
index 8e71917..55f2228 100644
--- a/libs/rollback/testapp/res_v1/values/values.xml
+++ b/tests/app/BadProviderStubs/AndroidManifest.xml
@@ -14,6 +14,14 @@
      limitations under the License.
 -->
 
-<resources>
-    <integer name="app_version">1</integer>
-</resources>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.stubbad">
+
+    <application>
+        <provider
+            android:name=".BadProviderStub"
+            android:authorities="com.android.cts.stubbad.badprovider"
+            android:exported="true" />
+    </application>
+
+</manifest>
diff --git a/tests/app/BadProviderStubs/src/com/android/cts/stubbad/BadProviderStub.java b/tests/app/BadProviderStubs/src/com/android/cts/stubbad/BadProviderStub.java
new file mode 100644
index 0000000..0103391
--- /dev/null
+++ b/tests/app/BadProviderStubs/src/com/android/cts/stubbad/BadProviderStub.java
@@ -0,0 +1,61 @@
+/*
+ * 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.stubbad;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+public class BadProviderStub extends ContentProvider {
+    @Override
+    public boolean onCreate() {
+        System.exit(0);
+        return false;
+    }
+
+    @Override
+    public Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+            @Nullable String selection, @Nullable String[] selectionArgs,
+            @Nullable String sortOrder) {
+        return null;
+    }
+
+    @Override
+    public String getType(@NonNull Uri uri) {
+        return null;
+    }
+
+    @Override
+    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
+        return null;
+    }
+
+    @Override
+    public int delete(@NonNull Uri uri, @Nullable String selection,
+            @Nullable String[] selectionArgs) {
+        return 0;
+    }
+
+    @Override
+    public int update(@NonNull Uri uri, @Nullable ContentValues values,
+            @Nullable String selection, @Nullable String[] selectionArgs) {
+        return 0;
+    }
+}
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..3b15c95
--- /dev/null
+++ b/tests/app/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 316234
+include platform/frameworks/base:/services/core/java/com/android/server/am/OWNERS
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..410ae34 100644
--- a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
@@ -82,10 +82,18 @@
     static final String SIMPLE_PACKAGE_NAME = "com.android.cts.launcherapps.simpleapp";
     static final String SIMPLE_SERVICE = ".SimpleService";
     static final String SIMPLE_SERVICE2 = ".SimpleService2";
+    static final String SIMPLE_SERVICE3 = ".SimpleService3";
     static final String SIMPLE_RECEIVER_START_SERVICE = ".SimpleReceiverStartService";
     static final String SIMPLE_ACTIVITY_START_SERVICE = ".SimpleActivityStartService";
+    static final String SIMPLE_ACTIVITY_START_FG_SERVICE = ".SimpleActivityStartFgService";
     public static String ACTION_SIMPLE_ACTIVITY_START_SERVICE_RESULT =
             "com.android.cts.launcherapps.simpleapp.SimpleActivityStartService.RESULT";
+    static final String ACTION_SIMPLE_ACTIVITY_START_FG =
+            "com.android.cts.launcherapps.simpleapp.SimpleActivityStartFgService.START_THEN_FG";
+    public static String ACTION_SIMPLE_ACTIVITY_START_FG_SERVICE_RESULT =
+            "com.android.cts.launcherapps.simpleapp.SimpleActivityStartFgService.NOW_FOREGROUND";
+    public static String ACTION_FINISH_EVERYTHING =
+            "com.android.cts.launcherapps.simpleapp.SimpleActivityStartFgService.FINISH_ALL";
 
     // APKs for testing heavy weight app interactions.
     static final String CANT_SAVE_STATE_1_PACKAGE_NAME = "com.android.test.cantsavestate1";
@@ -94,6 +102,8 @@
     // Actions
     static final String ACTION_START_FOREGROUND = "com.android.test.action.START_FOREGROUND";
     static final String ACTION_STOP_FOREGROUND = "com.android.test.action.STOP_FOREGROUND";
+    static final String ACTION_START_THEN_FG = "com.android.test.action.START_THEN_FG";
+    static final String ACTION_STOP_SERVICE = "com.android.test.action.STOP";
 
     private static final int TEMP_WHITELIST_DURATION_MS = 2000;
 
@@ -103,6 +113,8 @@
     private Intent mServiceStartForegroundIntent;
     private Intent mServiceStopForegroundIntent;
     private Intent mService2Intent;
+    private Intent mService3Intent;
+    private Intent mServiceStartForeground3Intent;
     private Intent mMainProcess[];
     private Intent mAllProcesses[];
 
@@ -122,8 +134,10 @@
         mServiceStartForegroundIntent.setAction(ACTION_START_FOREGROUND);
         mServiceStopForegroundIntent = new Intent(mServiceIntent);
         mServiceStopForegroundIntent.setAction(ACTION_STOP_FOREGROUND);
-        mService2Intent = new Intent();
-        mService2Intent.setClassName(SIMPLE_PACKAGE_NAME, SIMPLE_PACKAGE_NAME + SIMPLE_SERVICE2);
+        mService2Intent = new Intent()
+                .setClassName(SIMPLE_PACKAGE_NAME, SIMPLE_PACKAGE_NAME + SIMPLE_SERVICE2);
+        mService3Intent = new Intent()
+                .setClassName(SIMPLE_PACKAGE_NAME, SIMPLE_PACKAGE_NAME + SIMPLE_SERVICE3);
         mMainProcess = new Intent[1];
         mMainProcess[0] = mServiceIntent;
         mAllProcesses = new Intent[2];
@@ -131,6 +145,7 @@
         mAllProcesses[1] = mService2Intent;
         mContext.stopService(mServiceIntent);
         mContext.stopService(mService2Intent);
+        mContext.stopService(mService3Intent);
         turnScreenOn();
         removeTestAppFromWhitelists();
         mAppCount = 0;
@@ -160,17 +175,16 @@
         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();
-            });
+           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 {
@@ -912,7 +926,7 @@
             waiter.prepare(ACTION_SIMPLE_ACTIVITY_START_SERVICE_RESULT);
             activityIntent.putExtra("service", mServiceIntent);
             mContext.startActivity(activityIntent);
-            Intent resultIntent = waiter.doWait(WAIT_TIME);
+            Intent resultIntent = waiter.doWait(WAIT_TIME * 2);
             int brCode = resultIntent.getIntExtra("result", Activity.RESULT_CANCELED);
             if (brCode != Activity.RESULT_FIRST_USER) {
                 fail("Failed starting service, result=" + brCode);
@@ -1122,6 +1136,78 @@
         }
     }
 
+    /**
+     * Verify that an app under background restrictions has its foreground services
+     * demoted to ordinary service state when it is no longer the top app.
+     */
+    public void testBgRestrictedForegroundService() throws Exception {
+        final Intent activityIntent = new Intent()
+                .setClassName(SIMPLE_PACKAGE_NAME,
+                        SIMPLE_PACKAGE_NAME + SIMPLE_ACTIVITY_START_FG_SERVICE)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        final ServiceProcessController controller = new ServiceProcessController(mContext,
+                getInstrumentation(), STUB_PACKAGE_NAME, mAllProcesses, WAIT_TIME);
+        final WatchUidRunner uidWatcher = controller.getUidWatcher();
+
+        final Intent homeIntent = new Intent()
+                .setAction(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_HOME)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+        final Intent serviceStartIntent = new Intent(mService3Intent)
+                .setAction(ACTION_START_THEN_FG);
+        activityIntent.putExtra("service", serviceStartIntent);
+        boolean activityStarted = false;
+
+        try {
+            // First kill the process to start out in a stable state.
+            controller.ensureProcessGone();
+
+            // Do initial setup.
+            controller.denyAnyInBackgroundOp();
+            controller.makeUidIdle();
+            controller.removeFromWhitelist();
+            controller.setAppOpMode(AppOpsManager.OPSTR_START_FOREGROUND, "allow");
+
+            // Start the activity, which will start the fg service as well, and wait
+            // for the report that it's all up and running.
+            WaitForBroadcast waiter = new WaitForBroadcast(mInstrumentation.getTargetContext());
+            waiter.prepare(ACTION_SIMPLE_ACTIVITY_START_FG_SERVICE_RESULT);
+
+            activityIntent.setAction(ACTION_SIMPLE_ACTIVITY_START_FG);
+            mContext.startActivity(activityIntent);
+            activityStarted = true;
+
+            Intent resultIntent = waiter.doWait(WAIT_TIME);
+            int brCode = resultIntent.getIntExtra("result", Activity.RESULT_CANCELED);
+            if (brCode != Activity.RESULT_FIRST_USER) {
+                fail("Failed starting service, result=" + brCode);
+            }
+
+            // activity is in front, fg service is running.  make sure that we see
+            // the expected state at this point.
+            uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null);
+            uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null);
+            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP);
+
+            // Switch to the home app; make sure the test app drops all the way
+            // down to SERVICE, not FG_SERVICE
+            mContext.startActivity(homeIntent);
+            uidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_SERVICE);
+        } finally {
+            // tear down everything and we're done
+            if (activityStarted) {
+                activityIntent.setAction(ACTION_FINISH_EVERYTHING);
+                mContext.startActivity(activityIntent);
+            }
+
+            controller.cleanup();
+        }
+
+    }
+
     private boolean supportsCantSaveState() {
         if (mContext.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_CANT_SAVE_STATE)) {
@@ -1182,6 +1268,10 @@
                 appInfo.uid, ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE-1,
                 WAIT_TIME);
         uidBackgroundListener.register();
+        UidImportanceListener uidCachedListener = new UidImportanceListener(mContext,
+                appInfo.uid, ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE + 1,
+                WAIT_TIME);
+        uidCachedListener.register();
 
         WatchUidRunner uidWatcher = new WatchUidRunner(getInstrumentation(), appInfo.uid,
                 WAIT_TIME);
@@ -1250,14 +1340,14 @@
                     AccessibilityService.GLOBAL_ACTION_BACK);
 
             // Wait for process to become cached
-            uidBackgroundListener.waitForValue(
+            uidCachedListener.waitForValue(
                     IMPORTANCE_CACHED,
                     IMPORTANCE_CACHED);
             assertEquals(IMPORTANCE_CACHED,
                     am.getPackageImportance(CANT_SAVE_STATE_1_PACKAGE_NAME));
 
             uidWatcher.expect(WatchUidRunner.CMD_CACHED, null);
-            uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_RECENT);
+            uidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_RECENT);
 
             // While in background, should go in to normal idle state.
             // Force app to go idle now
@@ -1269,6 +1359,7 @@
             uidWatcher.finish();
             uidForegroundListener.unregister();
             uidBackgroundListener.unregister();
+            uidCachedListener.unregister();
         }
     }
 
@@ -1413,7 +1504,7 @@
             getInstrumentation().getUiAutomation().performGlobalAction(
                     AccessibilityService.GLOBAL_ACTION_BACK);
             uid1Watcher.expect(WatchUidRunner.CMD_CACHED, null);
-            uid1Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_RECENT);
+            uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_RECENT);
 
             // Make both apps idle for cleanliness.
             cmd = "am make-uid-idle " + CANT_SAVE_STATE_1_PACKAGE_NAME;
@@ -1477,9 +1568,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 +1649,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 +1725,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 +1848,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 +1885,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 +2012,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 +2028,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/BadProviderTest.java b/tests/app/src/android/app/cts/BadProviderTest.java
new file mode 100644
index 0000000..3c16fd3
--- /dev/null
+++ b/tests/app/src/android/app/cts/BadProviderTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.cts;
+
+import android.app.ActivityManager;
+import android.app.cts.android.app.cts.tools.WatchUidRunner;
+import android.content.ContentResolver;
+import android.content.pm.ApplicationInfo;
+import android.net.Uri;
+import android.os.HandlerThread;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.test.AndroidTestCase;
+
+import androidx.test.InstrumentationRegistry;
+
+/**
+ * Test system behavior of a bad provider.
+ */
+public class BadProviderTest extends AndroidTestCase {
+    private static final String AUTHORITY = "com.android.cts.stubbad.badprovider";
+    private static final String TEST_PACKAGE_NAME = "com.android.cts.stubbad";
+    private static final int WAIT_TIME = 2000;
+
+    public void testExitOnCreate() {
+        WatchUidRunner uidWatcher = null;
+        ContentResolver res = mContext.getContentResolver();
+        HandlerThread worker = new HandlerThread("work");
+        worker.start();
+        Handler handler = new Handler(worker.getLooper());
+        try {
+            ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo(
+                    TEST_PACKAGE_NAME, 0);
+            uidWatcher = new WatchUidRunner(InstrumentationRegistry.getInstrumentation(),
+                    appInfo.uid, WAIT_TIME);
+            long startTs = SystemClock.uptimeMillis();
+            handler.post(()->
+                res.query(Uri.parse("content://" + AUTHORITY), null, null, null, null)
+            );
+            // Ensure the system will try at least 3 times for a bad content provider.
+            uidWatcher.waitFor(WatchUidRunner.CMD_GONE, null);
+            uidWatcher.waitFor(WatchUidRunner.CMD_GONE, null);
+            uidWatcher.waitFor(WatchUidRunner.CMD_GONE, null);
+            // Finish the watcher
+            uidWatcher.finish();
+            // Sleep for 10 seconds and initialize the watcher again
+            // (content provider publish timeout is 10 seconds)
+            Thread.sleep(Math.max(0, 10000 - (SystemClock.uptimeMillis() - startTs)));
+            uidWatcher = new WatchUidRunner(InstrumentationRegistry.getInstrumentation(),
+                    appInfo.uid, WAIT_TIME);
+            // By now we shouldn't see it's retrying again.
+            try {
+                uidWatcher.waitFor(WatchUidRunner.CMD_GONE, null);
+                fail("Excessive attempts to bring up a provider");
+            } catch (IllegalStateException e) {
+            }
+        } catch (Exception e) {
+            fail("Unexpected exception while query provider: " + e.getMessage());
+        } finally {
+            if (uidWatcher != null) {
+                uidWatcher.finish();
+            }
+            worker.quitSafely();
+        }
+    }
+}
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..45725bc 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -18,7 +18,12 @@
 
 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.INTERRUPTION_FILTER_PRIORITY;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
@@ -68,6 +73,7 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.Icon;
 import android.media.AudioAttributes;
+import android.media.AudioManager;
 import android.media.session.MediaSession;
 import android.net.Uri;
 import android.os.Build;
@@ -91,9 +97,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 +114,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,10 +124,12 @@
     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;
 
     private PackageManager mPackageManager;
+    private AudioManager mAudioManager;
     private NotificationManager mNotificationManager;
     private ActivityManager mActivityManager;
     private String mId;
@@ -138,12 +145,22 @@
                 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();
+        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
         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);
@@ -184,8 +201,8 @@
 
     private void toggleBubbleSetting(boolean enabled) throws InterruptedException {
         SystemUtil.runWithShellPermissionIdentity(() ->
-                Settings.Secure.putInt(mContext.getContentResolver(),
-                        Settings.Secure.NOTIFICATION_BUBBLES, enabled ? 1 : 0));
+                Settings.Global.putInt(mContext.getContentResolver(),
+                        Settings.Global.NOTIFICATION_BUBBLES, enabled ? 1 : 0));
         Thread.sleep(500); // wait for ranking update
 
     }
@@ -257,14 +274,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 +298,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);
@@ -553,13 +577,17 @@
                 && Objects.equals(a.getConfigurationActivity(), b.getConfigurationActivity());
     }
 
-    private AutomaticZenRule createRule(String name) {
+    private AutomaticZenRule createRule(String name, int filter) {
         return new AutomaticZenRule(name, null,
                 new ComponentName(mContext, AutomaticZenRuleActivity.class),
                 new Uri.Builder().scheme("scheme")
                         .appendPath("path")
                         .appendQueryParameter("fake_rule", "fake_value")
-                        .build(), null, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+                        .build(), null, filter, true);
+    }
+
+    private AutomaticZenRule createRule(String name) {
+        return createRule(name, NotificationManager.INTERRUPTION_FILTER_PRIORITY);
     }
 
     private void assertExpectedDndState(int expectedState) {
@@ -1089,8 +1117,8 @@
         // turn on bubbles globally
         toggleBubbleSetting(true);
 
-        assertEquals(1, Settings.Secure.getInt(
-                mContext.getContentResolver(), Settings.Secure.NOTIFICATION_BUBBLES));
+        assertEquals(1, Settings.Global.getInt(
+                mContext.getContentResolver(), Settings.Global.NOTIFICATION_BUBBLES));
 
         toggleListenerAccess(TestNotificationListener.getId(),
                 InstrumentationRegistry.getInstrumentation(), true);
@@ -1292,7 +1320,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 +1495,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 +1527,7 @@
         }
     }
 
-    public void testInboxStyle() throws Exception {
+    public void testInboxStyle() {
         final int id = 100;
 
         final Notification notification =
@@ -1523,7 +1551,7 @@
         }
     }
 
-    public void testBigTextStyle() throws Exception {
+    public void testBigTextStyle() {
         final int id = 101;
 
         final Notification notification =
@@ -1549,7 +1577,7 @@
         }
     }
 
-    public void testBigPictureStyle() throws Exception {
+    public void testBigPictureStyle() {
         final int id = 102;
 
         final Notification notification =
@@ -1564,10 +1592,10 @@
                                 Icon.createWithResource(getContext(), R.drawable.icon_blue),
                                 "a2", getPendingIntent()).build())
                         .setStyle(new Notification.BigPictureStyle()
-                        .setBigContentTitle("title")
-                        .bigPicture(Bitmap.createBitmap(100, 100, Bitmap.Config.RGB_565))
-                        .bigLargeIcon(Icon.createWithResource(getContext(), R.drawable.icon_blue))
-                        .setSummaryText("summary"))
+                                .setBigContentTitle("title")
+                                .bigPicture(Bitmap.createBitmap(100, 100, Bitmap.Config.RGB_565))
+                                .bigLargeIcon(Icon.createWithResource(getContext(), R.drawable.icon_blue))
+                                .setSummaryText("summary"))
                         .build();
         mNotificationManager.notify(id, notification);
 
@@ -1577,77 +1605,77 @@
     }
 
     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);
     }
 
     public void testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled()
-        throws Exception {
+            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 +1695,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) {
@@ -1677,6 +1705,94 @@
         assertOnlySomeNotificationsAutogrouped(postedIds);
     }
 
+    public void testTotalSilenceOnlyMuteStreams() throws Exception {
+        if (mActivityManager.isLowRamDevice()) {
+            return;
+        }
+
+        final int originalFilter = mNotificationManager.getCurrentInterruptionFilter();
+        try {
+            toggleNotificationPolicyAccess(mContext.getPackageName(),
+                    InstrumentationRegistry.getInstrumentation(), true);
+
+            // ensure volume is not muted/0 to start test
+            mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_SYSTEM, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 1, 0);
+
+            mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(
+                    PRIORITY_CATEGORY_ALARMS | PRIORITY_CATEGORY_MEDIA, 0, 0));
+            AutomaticZenRule rule = createRule("test_total_silence",
+                    NotificationManager.INTERRUPTION_FILTER_NONE);
+            String id = mNotificationManager.addAutomaticZenRule(rule);
+            mRuleIds.add(id);
+            Condition condition =
+                    new Condition(rule.getConditionId(), "summary", Condition.STATE_TRUE);
+            mNotificationManager.setAutomaticZenRuleState(id, condition);
+            mNotificationManager.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY);
+
+            // delay for streams to get into correct mute states
+            Thread.sleep(50);
+            assertTrue("Music (media) stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC));
+            assertTrue("System stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_SYSTEM));
+            assertTrue("Alarm stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_ALARM));
+
+            // Test requires that the phone's default state has no channels that can bypass dnd
+            assertTrue("Ringer stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_RING));
+        } finally {
+            mNotificationManager.setInterruptionFilter(originalFilter);
+        }
+    }
+
+    public void testAlarmsOnlyMuteStreams() throws Exception {
+        if (mActivityManager.isLowRamDevice()) {
+            return;
+        }
+
+        final int originalFilter = mNotificationManager.getCurrentInterruptionFilter();
+        try {
+            toggleNotificationPolicyAccess(mContext.getPackageName(),
+                    InstrumentationRegistry.getInstrumentation(), true);
+
+            // ensure volume is not muted/0 to start test
+            mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_SYSTEM, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 1, 0);
+
+            mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(
+                    PRIORITY_CATEGORY_ALARMS | PRIORITY_CATEGORY_MEDIA, 0, 0));
+            AutomaticZenRule rule = createRule("test_alarms",
+                    NotificationManager.INTERRUPTION_FILTER_ALARMS);
+            String id = mNotificationManager.addAutomaticZenRule(rule);
+            mRuleIds.add(id);
+            Condition condition =
+                    new Condition(rule.getConditionId(), "summary", Condition.STATE_TRUE);
+            mNotificationManager.setAutomaticZenRuleState(id, condition);
+            mNotificationManager.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY);
+
+            // delay for streams to get into correct mute states
+            Thread.sleep(50);
+            assertFalse("Music (media) stream should not be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC));
+            assertTrue("System stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_SYSTEM));
+            assertFalse("Alarm stream should not be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_ALARM));
+
+            // Test requires that the phone's default state has no channels that can bypass dnd
+            assertTrue("Ringer stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_RING));
+        } finally {
+            mNotificationManager.setInterruptionFilter(originalFilter);
+        }
+    }
+
     public void testAddAutomaticZenRule_configActivity() throws Exception {
         if (mActivityManager.isLowRamDevice()) {
             return;
@@ -1813,7 +1929,6 @@
         assertExpectedDndState(INTERRUPTION_FILTER_ALL);
     }
 
-    @FlakyTest
     public void testSetAutomaticZenRuleState_multipleRules() throws Exception {
         if (mActivityManager.isLowRamDevice()) {
             return;
@@ -1932,7 +2047,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 +2101,81 @@
                 .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);
+
+        Thread.sleep(100);
+        
+        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);
+
+        Thread.sleep(1000);
+
+        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 +2294,7 @@
                         .build();
         mNotificationManager.notify(id, notification);
 
-        StatusBarNotification n = findPostedNotification(id);
+        StatusBarNotification n = findPostedNotification(id, false);
         assertNotNull(n);
     }
 
@@ -2192,15 +2381,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 +2477,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 +2527,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) {
@@ -2355,7 +2544,7 @@
             // Tested in LegacyNotificationManager20Test
             if (checkNotificationExistence(notificationId, /*shouldExist=*/ true)) {
                 fail("Notification should have been cancelled for targetSdk below L.  targetSdk="
-                    + mContext.getApplicationInfo().targetSdkVersion);
+                        + mContext.getApplicationInfo().targetSdkVersion);
             }
         }
 
@@ -2376,7 +2565,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 +2819,7 @@
             }
 
             // Should be foreground now
-            a.sendBubble(1);
+            a.sendBubble(4000);
 
             boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
 
@@ -2643,7 +2833,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 +2844,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 +2859,7 @@
         }
     }
 
-    public void testNotificationManagerBubblePolicy_noFlag_notEmbeddable() throws Exception {
+    public void testNotificationManagerBubblePolicy_noFlag_notEmbeddable() {
         Person person = new Person.Builder()
                 .setName("bubblebot")
                 .build();
@@ -2705,7 +2895,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 +2930,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/UserHandleTest.java b/tests/app/src/android/app/cts/UserHandleTest.java
new file mode 100644
index 0000000..342ee36
--- /dev/null
+++ b/tests/app/src/android/app/cts/UserHandleTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.cts;
+
+import android.os.Parcel;
+import android.os.UserHandle;
+import android.test.AndroidTestCase;
+
+public class UserHandleTest extends AndroidTestCase {
+    private static void assertSameUserHandle(int userId) {
+        assertSame(UserHandle.of(userId), UserHandle.of(userId));
+    }
+
+    public void testOf() {
+        for (int i = -1000; i < 100; i++) {
+            assertEquals(i, UserHandle.of(i).getIdentifier());
+        }
+
+        // Ensure common objects are cached.
+        assertSameUserHandle(UserHandle.USER_SYSTEM);
+        assertSameUserHandle(UserHandle.USER_ALL);
+        assertSameUserHandle(UserHandle.USER_NULL);
+        assertSameUserHandle(UserHandle.MIN_SECONDARY_USER_ID);
+        assertSameUserHandle(UserHandle.MIN_SECONDARY_USER_ID + 1);
+    }
+
+    private static void assertParcel(int userId) {
+        Parcel p = Parcel.obtain();
+        p.writeParcelable(UserHandle.of(userId), 0);
+        p.setDataPosition(0);
+
+        UserHandle read = p.readParcelable(UserHandleTest.class.getClassLoader());
+
+        assertEquals(userId, read.getIdentifier());
+
+        p.recycle();
+    }
+
+    public void testParcel() {
+        for (int i = -1000; i < 100; i++) {
+            assertParcel(i);
+        }
+    }
+}
diff --git a/tests/app/src/android/app/cts/WallpaperManagerTest.java b/tests/app/src/android/app/cts/WallpaperManagerTest.java
index fe5f183..7c284b0 100644
--- a/tests/app/src/android/app/cts/WallpaperManagerTest.java
+++ b/tests/app/src/android/app/cts/WallpaperManagerTest.java
@@ -16,11 +16,11 @@
 
 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;
 import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -46,6 +46,9 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -66,15 +69,36 @@
     private WallpaperManager mWallpaperManager;
     private Context mContext;
     private Handler mHandler;
+    private BroadcastReceiver mBroadcastReceiver;
+    private CountDownLatch mCountDownLatch;
 
     @Before
     public void setUp() throws Exception {
         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());
+        mCountDownLatch = new CountDownLatch(1);
+        mBroadcastReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                mCountDownLatch.countDown();
+                if (DEBUG) {
+                    Log.d(TAG, "broadcast state count down: " + mCountDownLatch.getCount());
+                }
+            }
+        };
+        mContext.registerReceiver(mBroadcastReceiver,
+                new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mWallpaperManager.clear();
+        mContext.unregisterReceiver(mBroadcastReceiver);
     }
 
     @Test
@@ -117,22 +141,12 @@
         Canvas canvas = new Canvas(tmpWallpaper);
         canvas.drawColor(Color.BLACK);
 
-        CountDownLatch latch = new CountDownLatch(1);
-        mContext.registerReceiver(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                latch.countDown();
-            }
-        }, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
-
         try {
             mWallpaperManager.setBitmap(tmpWallpaper);
 
             // Wait for up to 5 sec since this is an async call.
             // Should fail if Intent.ACTION_WALLPAPER_CHANGED isn't delivered.
-            if (!latch.await(5, TimeUnit.SECONDS)) {
-                throw new AssertionError("Intent.ACTION_WALLPAPER_CHANGED not received.");
-            }
+            Assert.assertTrue(mCountDownLatch.await(5, TimeUnit.SECONDS));
         } catch (InterruptedException | IOException e) {
             throw new AssertionError("Intent.ACTION_WALLPAPER_CHANGED not received.");
         } finally {
@@ -142,22 +156,12 @@
 
     @Test
     public void wallpaperClearBroadcastTest() {
-        CountDownLatch latch = new CountDownLatch(1);
-        mContext.registerReceiver(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                latch.countDown();
-            }
-        }, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
-
         try {
             mWallpaperManager.clear(WallpaperManager.FLAG_LOCK | WallpaperManager.FLAG_SYSTEM);
 
             // Wait for 5 sec since this is an async call.
             // Should fail if Intent.ACTION_WALLPAPER_CHANGED isn't delivered.
-            if (!latch.await(5, TimeUnit.SECONDS)) {
-                throw new AssertionError("Intent.ACTION_WALLPAPER_CHANGED not received.");
-            }
+            Assert.assertTrue(mCountDownLatch.await(5, TimeUnit.SECONDS));
         } catch (InterruptedException | IOException e) {
             throw new AssertionError(e);
         }
@@ -299,6 +303,63 @@
         }
     }
 
+    @Test
+    public void highRatioWallpaper_largeWidth() throws Exception {
+        final String sysuiPid = getSysuiPid();
+        Bitmap highRatioWallpaper = Bitmap.createBitmap(800, 8000, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(highRatioWallpaper);
+        canvas.drawColor(Color.RED);
+
+        try {
+            mWallpaperManager.setBitmap(highRatioWallpaper);
+
+            Assert.assertTrue(mCountDownLatch.await(5, TimeUnit.SECONDS));
+            Assert.assertTrue(sysuiPid.contentEquals(getSysuiPid()));
+        } finally {
+            highRatioWallpaper.recycle();
+        }
+    }
+
+    @Test
+    public void highRatioWallpaper_largeHeight() throws Exception {
+        final String sysuiPid = getSysuiPid();
+        Bitmap highRatioWallpaper = Bitmap.createBitmap(8000, 800, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(highRatioWallpaper);
+        canvas.drawColor(Color.RED);
+
+        try {
+            mWallpaperManager.setBitmap(highRatioWallpaper);
+
+            Assert.assertTrue(mCountDownLatch.await(5, TimeUnit.SECONDS));
+            Assert.assertTrue(sysuiPid.contentEquals(getSysuiPid()));
+        } finally {
+            highRatioWallpaper.recycle();
+        }
+    }
+
+    @Test
+    public void highResolutionWallpaper() throws Exception {
+        final String sysuiPid = getSysuiPid();
+        Bitmap highResolutionWallpaper = Bitmap.createBitmap(10000, 10000, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(highResolutionWallpaper);
+        canvas.drawColor(Color.BLUE);
+
+        try {
+            mWallpaperManager.setBitmap(highResolutionWallpaper);
+
+            Assert.assertTrue(mCountDownLatch.await(5, TimeUnit.SECONDS));
+            Assert.assertTrue(sysuiPid.contentEquals(getSysuiPid()));
+        } finally {
+            highResolutionWallpaper.recycle();
+        }
+    }
+
+    private static String getSysuiPid() {
+        final String sysuiPkgName = "com.android.systemui";
+        final String sysuiPid = "pidof " + sysuiPkgName;
+        return SystemUtil.runShellCommand(sysuiPid);
+    }
+
     private void assertDesiredDimension(Point suggestedSize, Point expectedSize) {
         mWallpaperManager.suggestDesiredDimensions(suggestedSize.x, suggestedSize.y);
         Point actualSize = new Point(mWallpaperManager.getDesiredMinimumWidth(),
@@ -422,31 +483,22 @@
         // • System colors are known
         // • Lock colors are known
         final int expectedEvents = 5;
-        CountDownLatch latch = new CountDownLatch(expectedEvents);
+        mCountDownLatch = new CountDownLatch(expectedEvents);
         if (DEBUG) {
-            Log.d("WP", "Started latch expecting: " + latch.getCount());
+            Log.d(TAG, "Started latch expecting: " + mCountDownLatch.getCount());
         }
-        BroadcastReceiver receiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                latch.countDown();
-                if (DEBUG) {
-                    Log.d("WP", "broadcast state count down: " + latch.getCount());
-                }
-            }
-        };
+
         WallpaperManager.OnColorsChangedListener callback = (colors, which) -> {
             if ((which & WallpaperManager.FLAG_LOCK) != 0) {
-                latch.countDown();
+                mCountDownLatch.countDown();
             }
             if ((which & WallpaperManager.FLAG_SYSTEM) != 0) {
-                latch.countDown();
+                mCountDownLatch.countDown();
             }
             if (DEBUG) {
-                Log.d("WP", "color state count down: " + which + " - " + colors);
+                Log.d(TAG, "color state count down: " + which + " - " + colors);
             }
         };
-        mContext.registerReceiver(receiver, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
         mWallpaperManager.addOnColorsChangedListener(callback, mHandler);
 
         try {
@@ -454,14 +506,11 @@
 
             // Wait for up to 10 sec since this is an async call.
             // Will pass as soon as the expected callbacks are executed.
-            latch.await(10, TimeUnit.SECONDS);
-            if (latch.getCount() != 0) {
-                Log.w(TAG, "Did not receive all events! This is probably a bug.");
-            }
+            Assert.assertTrue(mCountDownLatch.await(10, TimeUnit.SECONDS));
+            Assert.assertEquals(0, mCountDownLatch.getCount());
         } catch (InterruptedException | IOException e) {
             throw new RuntimeException("Can't ensure a clean state.");
         } finally {
-            mContext.unregisterReceiver(receiver);
             mWallpaperManager.removeOnColorsChangedListener(callback);
             bmp.recycle();
         }
@@ -477,4 +526,4 @@
         public void onColorsChanged(WallpaperColors colors, int which) {
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/app/src/android/app/cts/android/app/cts/tools/ServiceProcessController.java b/tests/app/src/android/app/cts/android/app/cts/tools/ServiceProcessController.java
index c372a38..01235ae 100644
--- a/tests/app/src/android/app/cts/android/app/cts/tools/ServiceProcessController.java
+++ b/tests/app/src/android/app/cts/android/app/cts/tools/ServiceProcessController.java
@@ -118,6 +118,17 @@
         String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
     }
 
+    /** The "battery restriction" forced app standby app-op */
+    public void denyAnyInBackgroundOp() throws IOException {
+        String cmd = "appops set " + mServicePackage + " RUN_ANY_IN_BACKGROUND deny";
+        String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
+    }
+
+    public void allowAnyInBackgroundOp() throws IOException {
+        String cmd = "appops set " + mServicePackage + " RUN_ANY_IN_BACKGROUND allow";
+        String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
+    }
+
     public void makeUidIdle() throws IOException {
         String cmd = "am make-uid-idle " + mServicePackage;
         String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
@@ -151,6 +162,7 @@
     public void cleanup() throws IOException {
         removeFromWhitelist();
         allowBackgroundOp();
+        allowAnyInBackgroundOp();
         mUidWatcher.finish();
         mUidGoneListener.unregister();
         mUidForegroundListener.unregister();
diff --git a/tests/app/src/android/app/cts/android/app/cts/tools/WatchUidRunner.java b/tests/app/src/android/app/cts/android/app/cts/tools/WatchUidRunner.java
index f3e91a1..e6e844f 100644
--- a/tests/app/src/android/app/cts/android/app/cts/tools/WatchUidRunner.java
+++ b/tests/app/src/android/app/cts/android/app/cts/tools/WatchUidRunner.java
@@ -211,6 +211,7 @@
                 if (res[0].startsWith("#")) {
                     Log.d(TAG, "Note: " + res[0]);
                 } else {
+                    Log.v(TAG, "LINE: " + Arrays.toString(res));
                     return res;
                 }
             }
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/OWNERS b/tests/autofillservice/OWNERS
index eac063b..18f17f3 100644
--- a/tests/autofillservice/OWNERS
+++ b/tests/autofillservice/OWNERS
@@ -1,4 +1,4 @@
 # Bug component: 351486
-felipeal@google.com
+adamhe@google.com
 svetoslavganov@google.com
-adamhe@google.com
\ No newline at end of file
+felipeal@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/FieldsClassificationTest.java b/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
index ff25596..99debab 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
@@ -28,6 +28,7 @@
 import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE;
 import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH;
 import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MIN_VALUE_LENGTH;
+import static android.service.autofill.AutofillFieldClassificationService.REQUIRED_ALGORITHM_CREDIT_CARD;
 import static android.service.autofill.AutofillFieldClassificationService.REQUIRED_ALGORITHM_EDIT_DISTANCE;
 import static android.service.autofill.AutofillFieldClassificationService.REQUIRED_ALGORITHM_EXACT_MATCH;
 
@@ -80,12 +81,17 @@
             new SettingsStateChangerRule(sContext, AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT, "42");
 
     private AutofillManager mAfm;
-    private Bundle mLast4Bundle = new Bundle();
+    private final Bundle mLast4Bundle = new Bundle();
+    private final Bundle mCreditCardBundle = new Bundle();
 
     @Override
     protected void postActivityLaunched() {
         mAfm = mActivity.getAutofillManager();
-        mLast4Bundle.putInt("suffix", 4);
+        mLast4Bundle.putInt("MATCH_SUFFIX", 4);
+
+        mCreditCardBundle.putInt("REQUIRED_ARG_MIN_CC_LENGTH", 13);
+        mCreditCardBundle.putInt("REQUIRED_ARG_MAX_CC_LENGTH", 19);
+        mCreditCardBundle.putInt("OPTIONAL_ARG_SUFFIX_LENGTH", 4);
     }
 
     @Test
@@ -140,6 +146,7 @@
         assertThat(availableAlgorithms).isNotNull();
         assertThat(availableAlgorithms.contains(REQUIRED_ALGORITHM_EDIT_DISTANCE)).isTrue();
         assertThat(availableAlgorithms.contains(REQUIRED_ALGORITHM_EXACT_MATCH)).isTrue();
+        assertThat(availableAlgorithms.contains(REQUIRED_ALGORITHM_CREDIT_CARD)).isTrue();
     }
 
     @Test
@@ -243,6 +250,43 @@
     }
 
     @Test
+    public void testHit_CreditCardAlgorithm() throws Exception {
+        enableService();
+
+        // Set expectations.
+        mAfm.setUserData(new UserData
+                .Builder("id", "1122334455667788", "card")
+                .setFieldClassificationAlgorithmForCategory("card",
+                        REQUIRED_ALGORITHM_CREDIT_CARD, mCreditCardBundle)
+                .build());
+        final MyAutofillCallback callback = mActivity.registerCallback();
+        final EditText field = mActivity.getCell(1, 1);
+        final AtomicReference<AutofillId> fieldId = new AtomicReference<>();
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setFieldClassificationIds(ID_L1C1)
+                .setVisitor((contexts, builder) -> fieldId
+                        .set(findAutofillIdByResourceId(contexts.get(0), ID_L1C1)))
+                .build());
+
+        // Trigger autofill
+        mActivity.focusCell(1, 1);
+        sReplier.getNextFillRequest();
+
+        mUiBot.assertNoDatasetsEver();
+        callback.assertUiUnavailableEvent(field);
+
+        // Simulate user input
+        mActivity.setText(1, 1, "7788");
+
+        // Finish context.
+        mAfm.commit();
+
+        // Assert results
+        final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+        assertFillEventForFieldsClassification(events.get(0), fieldId.get(), "card", 1);
+    }
+
+    @Test
     public void testHit_useDefaultAlgorithm() throws Exception {
         enableService();
 
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 64dee4d..9636026 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/Android.bp b/tests/backup/Android.bp
new file mode 100644
index 0000000..019c864
--- /dev/null
+++ b/tests/backup/Android.bp
@@ -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.
+
+android_test {
+    name: "CtsBackupTestCases",
+    defaults: ["cts_defaults"],
+    compile_multilib: "both",
+    libs: [
+        "android.test.runner.stubs",
+        "org.apache.http.legacy",
+        "android.test.base.stubs",
+    ],
+    static_libs: [
+        "compatibility-device-util-axt",
+        "ctstestrunner-axt",
+        "ctstestserver",
+        "mockito-target-minus-junit4",
+        "testng",
+    ],
+    host_required: ["CtsBackupHostTestCases"],
+    srcs: ["src/**/*.java"],
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    sdk_version: "test_current",
+}
diff --git a/tests/backup/Android.mk b/tests/backup/Android.mk
deleted file mode 100644
index 44fb751..0000000
--- a/tests/backup/Android.mk
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# don't include this package in any target
-LOCAL_MODULE_TAGS := optional
-# and when built explicitly put it in the data partition
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-LOCAL_JAVA_LIBRARIES := \
-    android.test.runner.stubs \
-    org.apache.http.legacy \
-    android.test.base.stubs \
-
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    compatibility-device-util-axt \
-    ctstestrunner-axt \
-    ctstestserver \
-    mockito-target-minus-junit4 \
-    testng
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-LOCAL_PACKAGE_NAME := CtsBackupTestCases
-
-LOCAL_SDK_VERSION := test_current
-
-include $(BUILD_CTS_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/backup/OWNERS b/tests/backup/OWNERS
index 3637e32..e0e5e22 100644
--- a/tests/backup/OWNERS
+++ b/tests/backup/OWNERS
@@ -1,6 +1,8 @@
+# Bug component: 41666
 # Use this reviewer by default.
 br-framework-team+reviews@google.com
 
+alsutton@google.com
 anniemeng@google.com
 brufino@google.com
 nathch@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/Android.bp b/tests/backup/app/Android.bp
new file mode 100644
index 0000000..10e66ed
--- /dev/null
+++ b/tests/backup/app/Android.bp
@@ -0,0 +1,92 @@
+// 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: "CtsFullBackupApp",
+    defaults: ["cts_support_defaults"],
+    srcs: [
+        "src/**/*.java",
+    ],
+    static_libs: [
+        "compatibility-device-util-axt",
+        "ctstestrunner-axt",
+    ],
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    platform_apis: true,
+    manifest: "fullbackup/AndroidManifest.xml"
+}
+
+android_test_helper_app {
+    name: "CtsKeyValueBackupApp",
+    defaults: ["cts_support_defaults"],
+    // Tag this module as a cts test artifact
+    srcs: [
+        "src/**/*.java",
+    ],
+    static_libs: [
+        "compatibility-device-util-axt",
+        "ctstestrunner-axt",
+    ],
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    platform_apis: true,
+    manifest: "keyvalue/AndroidManifest.xml"
+}
+
+android_test_helper_app {
+    name: "CtsPermissionBackupApp",
+    defaults: ["cts_support_defaults"],
+    // Tag this module as a cts test artifact
+    srcs: [
+        "src/**/*.java",
+    ],
+    static_libs: [
+        "compatibility-device-util-axt",
+        "ctstestrunner-axt",
+    ],
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    platform_apis: true,
+    manifest: "permission/AndroidManifest.xml"
+}
+
+android_test_helper_app {
+    name: "CtsPermissionBackupApp22",
+    defaults: ["cts_support_defaults"],
+    // Tag this module as a cts test artifact
+    srcs: [
+        "src/**/*.java",
+    ],
+    static_libs: [
+        "compatibility-device-util-axt",
+        "ctstestrunner-axt",
+    ],
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    platform_apis: true,
+    manifest: "permission22/AndroidManifest.xml"
+}
diff --git a/tests/backup/app/AndroidManifest.xml b/tests/backup/app/AndroidManifest.xml
new file mode 100644
index 0000000..d2a8a75
--- /dev/null
+++ b/tests/backup/app/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?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.backup.app">
+
+</manifest>
+
diff --git a/tests/backup/app/fullbackup/Android.mk b/tests/backup/app/fullbackup/Android.mk
deleted file mode 100644
index b53e38a..0000000
--- a/tests/backup/app/fullbackup/Android.mk
+++ /dev/null
@@ -1,35 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-
-LOCAL_PACKAGE_NAME := CtsFullBackupApp
-LOCAL_SDK_VERSION := current
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    compatibility-device-util-axt \
-    ctstestrunner-axt
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/backup/app/keyvalue/Android.mk b/tests/backup/app/keyvalue/Android.mk
deleted file mode 100644
index dc5f2e8..0000000
--- a/tests/backup/app/keyvalue/Android.mk
+++ /dev/null
@@ -1,35 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-
-LOCAL_PACKAGE_NAME := CtsKeyValueBackupApp
-LOCAL_SDK_VERSION := current
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    compatibility-device-util-axt \
-    ctstestrunner-axt
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/backup/app/permission/Android.mk b/tests/backup/app/permission/Android.mk
deleted file mode 100644
index f51a44d..0000000
--- a/tests/backup/app/permission/Android.mk
+++ /dev/null
@@ -1,35 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-
-LOCAL_PACKAGE_NAME := CtsPermissionBackupApp
-LOCAL_SDK_VERSION := current
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    compatibility-device-util-axt \
-    ctstestrunner-axt
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
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/Android.mk b/tests/camera/Android.mk
index b605b16..083359a 100644
--- a/tests/camera/Android.mk
+++ b/tests/camera/Android.mk
@@ -28,7 +28,7 @@
 
 LOCAL_MODULE := CtsCameraUtils
 
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := test_current
 -include cts/error_prone_rules_tests.mk
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
@@ -50,7 +50,7 @@
 	androidx.test.rules
 
 LOCAL_SRC_FILES := \
-	src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java \
+	src/android/hardware/camera2/cts/testcases/Camera2AndroidBasicTestCase.java \
 	src/android/hardware/camera2/cts/PerformanceTest.java \
 	src/android/hardware/cts/CameraTestCase.java \
 	src/android/hardware/cts/LegacyCameraPerformanceTest.java
diff --git a/tests/camera/libctscamera2jni/native-camera-jni.cpp b/tests/camera/libctscamera2jni/native-camera-jni.cpp
index b9fc256..fb78a90 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;
@@ -3420,6 +3422,8 @@
             goto exit;
         }
 
+        usleep(100000); // sleep to give some time for callbacks to happen
+
         if (testCase.isCameraAvailable(cameraId)) {
             LOG_ERROR(errorString, "Camera %s should be unavailable now", cameraId);
             goto exit;
diff --git a/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java b/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
index 92b171a..776879d 100644
--- a/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
@@ -21,10 +21,13 @@
 import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
 import static android.hardware.camera2.cts.CameraTestUtils.*;
 import static com.android.ex.camera2.blocking.BlockingStateCallback.*;
+import static junit.framework.Assert.*;
 
 import android.content.Context;
 import android.graphics.ImageFormat;
 import android.graphics.RectF;
+
+import android.hardware.camera2.cts.Camera2ParameterizedTestCase;
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
@@ -50,11 +53,12 @@
 import android.os.HandlerThread;
 import android.renderscript.Allocation;
 import android.renderscript.Script.LaunchOptions;
-import android.test.AndroidTestCase;
 import android.util.Log;
 import android.util.Rational;
 import android.view.Surface;
 
+import androidx.test.InstrumentationRegistry;
+
 import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException;
 import com.android.ex.camera2.blocking.BlockingStateCallback;
 import com.android.ex.camera2.blocking.BlockingSessionCallback;
@@ -63,6 +67,10 @@
 import java.util.Arrays;
 import java.util.List;
 
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.Test;
+
 /**
  * Suite of tests for camera2 -> RenderScript APIs.
  *
@@ -71,17 +79,17 @@
  *
  * <p>YUV_420_888: flexible YUV420, it is a mandatory format for camera.</p>
  */
-public class AllocationTest extends AndroidTestCase {
+
+@RunWith(Parameterized.class)
+public class AllocationTest extends Camera2ParameterizedTestCase {
     private static final String TAG = "AllocationTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
 
-    private CameraManager mCameraManager;
     private CameraDevice mCamera;
     private CameraCaptureSession mSession;
     private BlockingStateCallback mCameraListener;
     private BlockingSessionCallback mSessionListener;
 
-    private String[] mCameraIds;
 
     private Handler mHandler;
     private HandlerThread mHandlerThread;
@@ -91,16 +99,8 @@
     private ResultIterable mResultIterable;
 
     @Override
-    public synchronized void setContext(Context context) {
-        super.setContext(context);
-        mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
-        assertNotNull("Can't connect to camera manager!", mCameraManager);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
+    public void setUp() throws Exception {
         super.setUp();
-        mCameraIds = mCameraManager.getCameraIdList();
         mHandlerThread = new HandlerThread("AllocationTest");
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
@@ -110,11 +110,11 @@
         mSizeIterable = new SizeIterable();
         mResultIterable = new ResultIterable();
 
-        RenderScriptSingleton.setContext(getContext());
+        RenderScriptSingleton.setContext(mContext);
     }
 
     @Override
-    protected void tearDown() throws Exception {
+    public void tearDown() throws Exception {
         MaybeNull.close(mCamera);
         RenderScriptSingleton.clearContext();
         mHandlerThread.quitSafely();
@@ -475,6 +475,7 @@
         if (VERBOSE) Log.v(TAG, "validating Buffer , size = " + actualSize);
     }
 
+    @Test
     public void testAllocationFromCameraFlexibleYuv() throws Exception {
 
         /** number of frame (for streaming requests) to be verified. */
@@ -516,7 +517,7 @@
                             if (VERBOSE) Log.v(TAG, "Cleanup Renderscript cache");
                             scriptGraph.close();
                             RenderScriptSingleton.clearContext();
-                            RenderScriptSingleton.setContext(getContext());
+                            RenderScriptSingleton.setContext(mContext);
                         }
                     }
                 });
@@ -533,6 +534,7 @@
      *
      * @throws Exception
      */
+    @Test
     public void testBlackWhite() throws CameraAccessException {
 
         /** low iso + low exposure (first shot) */
@@ -610,6 +612,7 @@
     /**
      * Test that the android.sensitivity.parameter is applied.
      */
+    @Test
     public void testParamSensitivity() throws CameraAccessException {
         final float THRESHOLD_MAX_MIN_DIFF = 0.3f;
         final float THRESHOLD_MAX_MIN_RATIO = 2.0f;
@@ -761,33 +764,33 @@
         public void forEachCamera(boolean fullHwLevel, CameraBlock runnable)
                 throws CameraAccessException {
             assertNotNull("No camera manager", mCameraManager);
-            assertNotNull("No camera IDs", mCameraIds);
+            assertNotNull("No camera IDs", mCameraIdsUnderTest);
 
-            for (int i = 0; i < mCameraIds.length; i++) {
+            for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
                 // Don't execute the runnable against non-FULL cameras if FULL is required
                 CameraCharacteristics properties =
-                        mCameraManager.getCameraCharacteristics(mCameraIds[i]);
+                        mCameraManager.getCameraCharacteristics(mCameraIdsUnderTest[i]);
                 StaticMetadata staticInfo = new StaticMetadata(properties);
                 if (fullHwLevel && !staticInfo.isHardwareLevelAtLeastFull()) {
                     Log.i(TAG, String.format(
                             "Skipping this test for camera %s, needs FULL hw level",
-                            mCameraIds[i]));
+                            mCameraIdsUnderTest[i]));
                     continue;
                 }
                 if (!staticInfo.isColorOutputSupported()) {
                     Log.i(TAG, String.format(
                         "Skipping this test for camera %s, does not support regular outputs",
-                        mCameraIds[i]));
+                        mCameraIdsUnderTest[i]));
                     continue;
                 }
                 // Open camera and execute test
-                Log.i(TAG, "Testing Camera " + mCameraIds[i]);
+                Log.i(TAG, "Testing Camera " + mCameraIdsUnderTest[i]);
                 try {
-                    openDevice(mCameraIds[i]);
+                    openDevice(mCameraIdsUnderTest[i]);
 
                     runnable.run(mCamera);
                 } finally {
-                    closeDevice(mCameraIds[i]);
+                    closeDevice(mCameraIdsUnderTest[i]);
                 }
             }
         }
diff --git a/tests/camera/src/android/hardware/camera2/cts/BurstCaptureRawTest.java b/tests/camera/src/android/hardware/camera2/cts/BurstCaptureRawTest.java
index 7b23abf..afff41b 100644
--- a/tests/camera/src/android/hardware/camera2/cts/BurstCaptureRawTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/BurstCaptureRawTest.java
@@ -37,11 +37,14 @@
 
 import java.util.ArrayList;
 
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 import org.junit.Test;
 
 /**
  * Basic tests for burst capture in RAW formats.
  */
+@RunWith(Parameterized.class)
 public class BurstCaptureRawTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "BurstCaptureRawTest";
     private static final int RAW_FORMATS[] = {
@@ -71,7 +74,7 @@
     @Test
     public void testRawSensorSize() throws Exception {
         Log.i(TAG, "Begin testRawSensorSize");
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 ArrayList<Integer> supportedRawList = new ArrayList<Integer>(RAW_FORMATS.length);
                 if (!checkCapability(id, supportedRawList, RAW_FORMATS)) {
@@ -675,7 +678,7 @@
     private void performTestRoutine(TestRoutine routine, int[] testedFormats) throws Exception
     {
         final int PREPARE_TIMEOUT_MS = 10000;
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 ArrayList<Integer> supportedRawList = new ArrayList<Integer>(RAW_FORMATS.length);
                 if (!checkCapability(id, supportedRawList, testedFormats)) {
diff --git a/tests/camera/src/android/hardware/camera2/cts/BurstCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/BurstCaptureTest.java
index 03dbdeb..b86c511 100644
--- a/tests/camera/src/android/hardware/camera2/cts/BurstCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/BurstCaptureTest.java
@@ -34,8 +34,11 @@
 import java.util.List;
 import java.util.ArrayList;
 
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 import org.junit.Test;
 
+@RunWith(Parameterized.class)
 public class BurstCaptureTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "BurstCaptureTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -65,9 +68,9 @@
     }
 
     private void testBurst(int fmt, int burstSize, boolean checkFrameRate) throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                String id = mCameraIds[i];
+                String id = mCameraIdsUnderTest[i];
 
                 StaticMetadata staticInfo = mAllStaticInfo.get(id);
                 if (!staticInfo.isColorOutputSupported()) {
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
index a64cd50..73e3828 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
@@ -67,6 +67,9 @@
 import java.util.Set;
 import android.util.Size;
 
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
+import org.junit.Test;
 import org.mockito.ArgumentMatcher;
 
 import java.util.concurrent.Executor;
@@ -76,6 +79,8 @@
 /**
  * <p>Basic test for CameraDevice APIs.</p>
  */
+
+@RunWith(Parameterized.class)
 public class CameraDeviceTest extends Camera2AndroidTestCase {
     private static final String TAG = "CameraDeviceTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -114,16 +119,15 @@
     }
 
     @Override
-    public void setContext(Context context) {
-        super.setContext(context);
-
+    public void setUp() throws Exception {
+        super.setUp();
         /**
          * Workaround for mockito and JB-MR2 incompatibility
          *
          * Avoid java.lang.IllegalArgumentException: dexcache == null
          * https://code.google.com/p/dexmaker/issues/detail?id=2
          */
-        System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
+        System.setProperty("dexmaker.dexcache", mContext.getCacheDir().toString());
 
         /**
          * Create error listener in context scope, to catch asynchronous device error.
@@ -131,11 +135,6 @@
          * implementation (spy doesn't stub the functions unless we ask it to do so).
          */
         mCameraMockListener = spy(new BlockingStateCallback());
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
         /**
          * Due to the asynchronous nature of camera device error callback, we
          * have to make sure device doesn't run into error state before. If so,
@@ -154,7 +153,7 @@
     }
 
     @Override
-    protected void tearDown() throws Exception {
+    public void tearDown() throws Exception {
         super.tearDown();
     }
 
@@ -176,9 +175,10 @@
      * settings.</li>
      * </ul>
      */
+    @Test
     public void testCameraDevicePreviewTemplate() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
-            captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_PREVIEW);
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+            captureTemplateTestByCamera(mCameraIdsUnderTest[i], CameraDevice.TEMPLATE_PREVIEW);
         }
 
         // TODO: test the frame rate sustainability in preview use case test.
@@ -202,9 +202,10 @@
      * frame rate for the given settings.</li>
      * </ul>
      */
+    @Test
     public void testCameraDeviceStillTemplate() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
-            captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_STILL_CAPTURE);
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+            captureTemplateTestByCamera(mCameraIdsUnderTest[i], CameraDevice.TEMPLATE_STILL_CAPTURE);
         }
     }
 
@@ -222,9 +223,10 @@
      * <li>Frame rate should be stable, for example, wide fps range like [7, 30]
      * is a bad setting.</li>
      */
+    @Test
     public void testCameraDeviceRecordingTemplate() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
-            captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_RECORD);
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+            captureTemplateTestByCamera(mCameraIdsUnderTest[i], CameraDevice.TEMPLATE_RECORD);
         }
 
         // TODO: test the frame rate sustainability in recording use case test.
@@ -238,9 +240,10 @@
      * as recording, with an additional requirement: the settings should maximize image quality
      * without compromising stable frame rate.</p>
      */
+    @Test
     public void testCameraDeviceVideoSnapShotTemplate() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
-            captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_VIDEO_SNAPSHOT);
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+            captureTemplateTestByCamera(mCameraIdsUnderTest[i], CameraDevice.TEMPLATE_VIDEO_SNAPSHOT);
         }
 
         // TODO: test the frame rate sustainability in video snapshot use case test.
@@ -253,9 +256,10 @@
      * metadata keys, and their values must be set correctly. It has the similar requirement
      * as preview, with an additional requirement: </p>
      */
+    @Test
     public void testCameraDeviceZSLTemplate() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
-            captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+            captureTemplateTestByCamera(mCameraIdsUnderTest[i], CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);
         }
     }
 
@@ -276,16 +280,18 @@
      * set to reasonable defaults.</li>
      * </ul>
      */
+    @Test
     public void testCameraDeviceManualTemplate() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
-            captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_MANUAL);
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+            captureTemplateTestByCamera(mCameraIdsUnderTest[i], CameraDevice.TEMPLATE_MANUAL);
         }
     }
 
+    @Test
     public void testCameraDeviceCreateCaptureBuilder() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                openDevice(mCameraIds[i], mCameraMockListener);
+                openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
                 /**
                  * Test: that each template type is supported, and that its required fields are
                  * present.
@@ -331,16 +337,17 @@
                 try {
                     closeSession();
                 } finally {
-                    closeDevice(mCameraIds[i], mCameraMockListener);
+                    closeDevice(mCameraIdsUnderTest[i], mCameraMockListener);
                 }
             }
         }
     }
 
+    @Test
     public void testCameraDeviceSetErrorListener() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                openDevice(mCameraIds[i], mCameraMockListener);
+                openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
                 /**
                  * Test: that the error listener can be set without problems.
                  * Also, wait some time to check if device doesn't run into error.
@@ -355,27 +362,31 @@
                 try {
                     closeSession();
                 } finally {
-                    closeDevice(mCameraIds[i], mCameraMockListener);
+                    closeDevice(mCameraIdsUnderTest[i], mCameraMockListener);
                 }
             }
         }
     }
 
+    @Test
     public void testCameraDeviceCapture() throws Exception {
         runCaptureTest(/*burst*/false, /*repeating*/false, /*abort*/false, /*useExecutor*/false);
         runCaptureTest(/*burst*/false, /*repeating*/false, /*abort*/false, /*useExecutor*/true);
     }
 
+    @Test
     public void testCameraDeviceCaptureBurst() throws Exception {
         runCaptureTest(/*burst*/true, /*repeating*/false, /*abort*/false, /*useExecutor*/false);
         runCaptureTest(/*burst*/true, /*repeating*/false, /*abort*/false, /*useExecutor*/true);
     }
 
+    @Test
     public void testCameraDeviceRepeatingRequest() throws Exception {
         runCaptureTest(/*burst*/false, /*repeating*/true, /*abort*/false, /*useExecutor*/false);
         runCaptureTest(/*burst*/false, /*repeating*/true, /*abort*/false, /*useExecutor*/true);
     }
 
+    @Test
     public void testCameraDeviceRepeatingBurst() throws Exception {
         runCaptureTest(/*burst*/true, /*repeating*/true, /*abort*/false, /*useExecutor*/ false);
         runCaptureTest(/*burst*/true, /*repeating*/true, /*abort*/false, /*useExecutor*/ true);
@@ -389,6 +400,7 @@
      * discarding in-progress work. Once the abort is complete, the idle callback will be called.
      * </p>
      */
+    @Test
     public void testCameraDeviceAbort() throws Exception {
         runCaptureTest(/*burst*/false, /*repeating*/true, /*abort*/true, /*useExecutor*/false);
         runCaptureTest(/*burst*/false, /*repeating*/true, /*abort*/true, /*useExecutor*/true);
@@ -414,10 +426,11 @@
     /**
      * Test invalid capture (e.g. null or empty capture request).
      */
+    @Test
     public void testInvalidCapture() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                openDevice(mCameraIds[i], mCameraMockListener);
+                openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
                 waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
 
                 prepareCapture();
@@ -427,7 +440,7 @@
                 closeSession();
             }
             finally {
-                closeDevice(mCameraIds[i], mCameraMockListener);
+                closeDevice(mCameraIdsUnderTest[i], mCameraMockListener);
             }
         }
     }
@@ -442,6 +455,7 @@
      *  onCaptureCompleted -> createCaptureRequest, getDevice, abortCaptures,
      *     capture, setRepeatingRequest, stopRepeating, session+device.close
      */
+    @Test
     public void testChainedOperation() throws Throwable {
 
         final ArrayList<Surface> outputs = new ArrayList<>();
@@ -585,13 +599,13 @@
 
         // Actual test code
 
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
                 Throwable result;
 
-                if (!(new StaticMetadata(mCameraManager.getCameraCharacteristics(mCameraIds[i]))).
+                if (!(new StaticMetadata(mCameraManager.getCameraCharacteristics(mCameraIdsUnderTest[i]))).
                         isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
@@ -602,7 +616,7 @@
 
                 // Start chained cascade
                 ChainedCameraListener cameraListener = new ChainedCameraListener();
-                mCameraManager.openCamera(mCameraIds[i], cameraListener, mHandler);
+                mCameraManager.openCamera(mCameraIdsUnderTest[i], cameraListener, mHandler);
 
                 // Check if open succeeded
                 result = results.poll(CAMERA_OPEN_TIMEOUT_MS, TimeUnit.MILLISECONDS);
@@ -648,21 +662,22 @@
      * Verify basic semantics and error conditions of the prepare call.
      *
      */
+    @Test
     public void testPrepare() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
-                openDevice(mCameraIds[i], mCameraMockListener);
+                openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
                 waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
 
                 prepareTestByCamera();
             }
             finally {
-                closeDevice(mCameraIds[i], mCameraMockListener);
+                closeDevice(mCameraIdsUnderTest[i], mCameraMockListener);
             }
         }
     }
@@ -671,26 +686,27 @@
      * Verify prepare call behaves properly when sharing surfaces.
      *
      */
+    @Test
     public void testPrepareForSharedSurfaces() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIds[i]);
+                StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIdsUnderTest[i]);
                 if (staticInfo.isHardwareLevelLegacy()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] + " is legacy, skipping");
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] + " is legacy, skipping");
                     continue;
                 }
                 if (!staticInfo.isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
-                openDevice(mCameraIds[i], mCameraMockListener);
+                openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
                 waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
 
                 prepareTestForSharedSurfacesByCamera();
             }
             finally {
-                closeDevice(mCameraIds[i], mCameraMockListener);
+                closeDevice(mCameraIdsUnderTest[i], mCameraMockListener);
             }
         }
     }
@@ -698,21 +714,22 @@
     /**
      * Verify creating sessions back to back.
      */
+    @Test
     public void testCreateSessions() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
-                openDevice(mCameraIds[i], mCameraMockListener);
+                openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
                 waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
 
-                testCreateSessionsByCamera(mCameraIds[i]);
+                testCreateSessionsByCamera(mCameraIdsUnderTest[i]);
             }
             finally {
-                closeDevice(mCameraIds[i], mCameraMockListener);
+                closeDevice(mCameraIdsUnderTest[i], mCameraMockListener);
             }
         }
     }
@@ -720,21 +737,22 @@
     /**
      * Verify creating a custom session
      */
+    @Test
     public void testCreateCustomSession() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
-                openDevice(mCameraIds[i], mCameraMockListener);
+                openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
                 waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
 
-                testCreateCustomSessionByCamera(mCameraIds[i]);
+                testCreateCustomSessionByCamera(mCameraIdsUnderTest[i]);
             }
             finally {
-                closeDevice(mCameraIds[i], mCameraMockListener);
+                closeDevice(mCameraIdsUnderTest[i], mCameraMockListener);
             }
         }
     }
@@ -808,6 +826,7 @@
     /**
      * Test session configuration.
      */
+    @Test
     public void testSessionConfiguration() throws Exception {
         ArrayList<OutputConfiguration> outConfigs = new ArrayList<OutputConfiguration> ();
         outConfigs.add(new OutputConfiguration(new Size(1, 1), SurfaceTexture.class));
@@ -856,14 +875,14 @@
         assertEquals("Session configuration input doesn't match",
                 highspeedSessionConfig.getInputConfiguration(), null);
 
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
-                openDevice(mCameraIds[i], mCameraMockListener);
+                openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
                 waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
 
                 CaptureRequest.Builder builder =
@@ -881,7 +900,7 @@
                         highspeedSessionConfig.getSessionParameters());
             }
             finally {
-                closeDevice(mCameraIds[i], mCameraMockListener);
+                closeDevice(mCameraIdsUnderTest[i], mCameraMockListener);
             }
         }
     }
@@ -889,21 +908,22 @@
     /**
      * Check for any state leakage in case of internal re-configure
      */
+    @Test
     public void testSessionParametersStateLeak() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
-                openDevice(mCameraIds[i], mCameraMockListener);
+                openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
                 waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
 
-                testSessionParametersStateLeakByCamera(mCameraIds[i]);
+                testSessionParametersStateLeakByCamera(mCameraIdsUnderTest[i]);
             }
             finally {
-                closeDevice(mCameraIds[i], mCameraMockListener);
+                closeDevice(mCameraIdsUnderTest[i], mCameraMockListener);
             }
         }
     }
@@ -1059,22 +1079,23 @@
     /**
      * Verify creating a session with additional parameters.
      */
+    @Test
     public void testCreateSessionWithParameters() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
-                openDevice(mCameraIds[i], mCameraMockListener);
+                openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
                 waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
 
-                testCreateSessionWithParametersByCamera(mCameraIds[i], /*reprocessable*/false);
-                testCreateSessionWithParametersByCamera(mCameraIds[i], /*reprocessable*/true);
+                testCreateSessionWithParametersByCamera(mCameraIdsUnderTest[i], /*reprocessable*/false);
+                testCreateSessionWithParametersByCamera(mCameraIdsUnderTest[i], /*reprocessable*/true);
             }
             finally {
-                closeDevice(mCameraIds[i], mCameraMockListener);
+                closeDevice(mCameraIdsUnderTest[i], mCameraMockListener);
             }
         }
     }
@@ -1596,9 +1617,9 @@
      */
     private void runCaptureTest(boolean burst, boolean repeating, boolean abort,
             boolean useExecutor) throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                openDevice(mCameraIds[i], mCameraMockListener);
+                openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
                 waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
 
                 prepareCapture();
@@ -1617,13 +1638,13 @@
                             continue;
                         }
 
-                        captureSingleShot(mCameraIds[i], sTemplates[j], repeating, abort,
+                        captureSingleShot(mCameraIdsUnderTest[i], sTemplates[j], repeating, abort,
                                 useExecutor);
                     }
                 }
                 else {
                     // Test: burst of one shot
-                    captureBurstShot(mCameraIds[i], sTemplates, 1, repeating, abort, useExecutor);
+                    captureBurstShot(mCameraIdsUnderTest[i], sTemplates, 1, repeating, abort, useExecutor);
 
                     int template = mStaticInfo.isColorOutputSupported() ?
                         CameraDevice.TEMPLATE_STILL_CAPTURE :
@@ -1637,12 +1658,12 @@
                     };
 
                     // Test: burst of 5 shots of the same template type
-                    captureBurstShot(mCameraIds[i], templates, templates.length, repeating, abort,
+                    captureBurstShot(mCameraIdsUnderTest[i], templates, templates.length, repeating, abort,
                             useExecutor);
 
                     if (mStaticInfo.isColorOutputSupported()) {
                         // Test: burst of 6 shots of different template types
-                        captureBurstShot(mCameraIds[i], sTemplates, sTemplates.length, repeating,
+                        captureBurstShot(mCameraIdsUnderTest[i], sTemplates, sTemplates.length, repeating,
                                 abort, useExecutor);
                     }
                 }
@@ -1658,7 +1679,7 @@
                 } catch (Exception e) {
                     mCollector.addError(e);
                 }finally {
-                    closeDevice(mCameraIds[i], mCameraMockListener);
+                    closeDevice(mCameraIdsUnderTest[i], mCameraMockListener);
                 }
             }
         }
@@ -2698,4 +2719,178 @@
             }
         }
     }
+
+    /**
+     * Verify audio restrictions are set properly for single CameraDevice usage
+     */
+    @Test
+    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 < mCameraIdsUnderTest.length; i++) {
+            try {
+                openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
+                waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
+
+                for (int mode : testModes) {
+                    mCamera.setCameraAudioRestriction(mode);
+                    int retMode = mCamera.getCameraAudioRestriction();
+                    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(mCameraIdsUnderTest[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;
+            cam0.setCameraAudioRestriction(mode0);
+            int retMode = cam0.getCameraAudioRestriction();
+            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;
+                cam1.setCameraAudioRestriction(mode1);
+                retMode = cam1.getCameraAudioRestriction();
+                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;
+            cam1.setCameraAudioRestriction(mode1);
+            retMode = cam1.getCameraAudioRestriction();
+            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;
+            cam0.setCameraAudioRestriction(mode0);
+            retMode = cam0.getCameraAudioRestriction();
+            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;
+            cam1.setCameraAudioRestriction(mode1);
+            retMode = cam1.getCameraAudioRestriction();
+            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;
+            cam0.setCameraAudioRestriction(mode0);
+            retMode = cam0.getCameraAudioRestriction();
+            assertTrue("Audio restriction mode mismatch: expect: " + expectMode +
+                    ", output:" + retMode, retMode == expectMode);
+
+            mode1 = CameraDevice.AUDIO_RESTRICTION_VIBRATION;
+            expectMode = mode0 | mode1;
+            cam1.setCameraAudioRestriction(mode1);
+            retMode = cam1.getCameraAudioRestriction();
+            assertTrue("Audio restriction mode mismatch: expect: " + expectMode +
+                    ", output:" + retMode, retMode == expectMode);
+
+            mode1 = CameraDevice.AUDIO_RESTRICTION_NONE;
+            expectMode = mode0 | mode1;
+            cam1.setCameraAudioRestriction(mode1);
+            retMode = cam1.getCameraAudioRestriction();
+            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;
+            cam1.setCameraAudioRestriction(mode1);
+            retMode = cam1.getCameraAudioRestriction();
+            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;
+            }
+        }
+    }
+
+    @Test
+    public void testAudioRestrictionMultipleDevices() throws Exception {
+        if (mCameraIdsUnderTest.length < 2) {
+            Log.i(TAG, "device doesn't have multiple cameras, skipping");
+            return;
+        }
+
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+            for (int j = i+1; j < mCameraIdsUnderTest.length; j++) {
+                testTwoCameraDevicesAudioRestriction(mCameraIdsUnderTest[i], mCameraIdsUnderTest[j]);
+            }
+        }
+    }
 }
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
index 4457f95..922dc3c 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
@@ -19,6 +19,7 @@
 import static org.mockito.Mockito.*;
 import static org.mockito.AdditionalMatchers.not;
 import static org.mockito.AdditionalMatchers.and;
+import static junit.framework.Assert.*;
 
 import android.app.ActivityManager;
 import android.app.Instrumentation;
@@ -31,6 +32,7 @@
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CameraDevice.StateCallback;
 import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.cts.Camera2ParameterizedTestCase;
 import android.hardware.camera2.cts.CameraTestUtils.HandlerExecutor;
 import android.hardware.camera2.cts.CameraTestUtils.MockStateCallback;
 import android.hardware.camera2.cts.helpers.CameraErrorCollector;
@@ -45,6 +47,9 @@
 import com.android.compatibility.common.util.PropertyUtil;
 import com.android.ex.camera2.blocking.BlockingStateCallback;
 
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
+import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.InOrder;
 
@@ -61,13 +66,14 @@
 /**
  * <p>Basic test for CameraManager class.</p>
  */
-public class CameraManagerTest extends AndroidTestCase {
+
+@RunWith(Parameterized.class)
+public class CameraManagerTest extends Camera2ParameterizedTestCase {
     private static final String TAG = "CameraManagerTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
     private static final int NUM_CAMERA_REOPENS = 10;
 
     private PackageManager mPackageManager;
-    private CameraManager mCameraManager;
     private NoopCameraListener mListener;
     private HandlerThread mHandlerThread;
     private Handler mHandler;
@@ -75,18 +81,11 @@
     private CameraErrorCollector mCollector;
 
     @Override
-    public void setContext(Context context) {
-        super.setContext(context);
-        mCameraManager = (CameraManager)context.getSystemService(Context.CAMERA_SERVICE);
-        assertNotNull("Can't connect to camera manager", mCameraManager);
-        mPackageManager = context.getPackageManager();
+    public void setUp() throws Exception {
+        super.setUp();
+        mPackageManager = mContext.getPackageManager();
         assertNotNull("Can't get package manager", mPackageManager);
         mListener = new NoopCameraListener();
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
 
         /**
          * Workaround for mockito and JB-MR2 incompatibility
@@ -94,7 +93,7 @@
          * Avoid java.lang.IllegalArgumentException: dexcache == null
          * https://code.google.com/p/dexmaker/issues/detail?id=2
          */
-        System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
+        System.setProperty("dexmaker.dexcache", mContext.getCacheDir().toString());
 
         mCameraListener = spy(new BlockingStateCallback());
 
@@ -105,7 +104,7 @@
     }
 
     @Override
-    protected void tearDown() throws Exception {
+    public void tearDown() throws Exception {
         mHandlerThread.quitSafely();
         mHandler = null;
 
@@ -137,10 +136,10 @@
         return -1; // unreachable
     }
 
+    @Test
     public void testCameraManagerGetDeviceIdList() throws Exception {
 
-        // Test: that the getCameraIdList method runs without exceptions.
-        String[] ids = mCameraManager.getCameraIdList();
+        String[] ids = mCameraIdsUnderTest;
         if (VERBOSE) Log.v(TAG, "CameraManager ids: " + Arrays.toString(ids));
 
         /**
@@ -197,8 +196,9 @@
     }
 
     // Test: that properties can be queried from each device, without exceptions.
+    @Test
     public void testCameraManagerGetCameraCharacteristics() throws Exception {
-        String[] ids = mCameraManager.getCameraIdList();
+        String[] ids = mCameraIdsUnderTest;
         for (int i = 0; i < ids.length; i++) {
             CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
             assertNotNull(
@@ -207,8 +207,9 @@
     }
 
     // Test: that an exception is thrown if an invalid device id is passed down.
+    @Test
     public void testCameraManagerInvalidDevice() throws Exception {
-        String[] ids = mCameraManager.getCameraIdList();
+        String[] ids = mCameraIdsUnderTest;
         // Create an invalid id by concatenating all the valid ids together.
         StringBuilder invalidId = new StringBuilder();
         invalidId.append("INVALID");
@@ -226,6 +227,7 @@
     }
 
     // Test: that each camera device can be opened one at a time, several times.
+    @Test
     public void testCameraManagerOpenCamerasSerially() throws Exception {
         testCameraManagerOpenCamerasSerially(/*useExecutor*/ false);
         testCameraManagerOpenCamerasSerially(/*useExecutor*/ true);
@@ -233,7 +235,7 @@
 
     private void testCameraManagerOpenCamerasSerially(boolean useExecutor) throws Exception {
         final Executor executor = useExecutor ? new HandlerExecutor(mHandler) : null;
-        String[] ids = mCameraManager.getCameraIdList();
+        String[] ids = mCameraIdsUnderTest;
         for (int i = 0; i < ids.length; i++) {
             for (int j = 0; j < NUM_CAMERA_REOPENS; j++) {
                 CameraDevice camera = null;
@@ -268,13 +270,14 @@
      * Test: one or more camera devices can be open at the same time, or the right error state
      * is set if this can't be done.
      */
+    @Test
     public void testCameraManagerOpenAllCameras() throws Exception {
         testCameraManagerOpenAllCameras(/*useExecutor*/ false);
         testCameraManagerOpenAllCameras(/*useExecutor*/ true);
     }
 
     private void testCameraManagerOpenAllCameras(boolean useExecutor) throws Exception {
-        String[] ids = mCameraManager.getCameraIdList();
+        String[] ids = mCameraIdsUnderTest;
         assertNotNull("Camera ids shouldn't be null", ids);
 
         // Skip test if the device doesn't have multiple cameras.
@@ -451,13 +454,14 @@
      * Test: that opening the same device multiple times and make sure the right
      * error state is set.
      */
+    @Test
     public void testCameraManagerOpenCameraTwice() throws Exception {
         testCameraManagerOpenCameraTwice(/*useExecutor*/ false);
         testCameraManagerOpenCameraTwice(/*useExecutor*/ true);
     }
 
     private void testCameraManagerOpenCameraTwice(boolean useExecutor) throws Exception {
-        String[] ids = mCameraManager.getCameraIdList();
+        String[] ids = mCameraIdsUnderTest;
         final Executor executor = useExecutor ? new HandlerExecutor(mHandler) : null;
 
         // Test across every camera device.
@@ -523,6 +527,7 @@
      * Registering a listener multiple times should have no effect, and unregistering
      * a listener that isn't registered should have no effect.
      */
+    @Test
     public void testCameraManagerListener() throws Exception {
         mCameraManager.unregisterAvailabilityCallback(mListener);
         // Test Handler API
@@ -541,6 +546,7 @@
     /**
      * Test that the availability callbacks fire when expected
      */
+    @Test
     public void testCameraManagerListenerCallbacks() throws Exception {
         testCameraManagerListenerCallbacks(/*useExecutor*/ false);
         testCameraManagerListenerCallbacks(/*useExecutor*/ true);
@@ -556,6 +562,17 @@
         CameraManager.AvailabilityCallback ac = new CameraManager.AvailabilityCallback() {
             @Override
             public void onCameraAvailable(String cameraId) {
+                try {
+                    // When we're testing system cameras, we don't list non system cameras in the
+                    // camera id list as mentioned in Camera2ParameterizedTest.java
+                    if (mAdoptShellPerm &&
+                            !CameraTestUtils.isSystemCamera(mCameraManager, cameraId)) {
+                        return;
+                    }
+                } catch (CameraAccessException e) {
+                    fail("CameraAccessException thrown when attempting to access camera" +
+                         "characteristics" + cameraId);
+                }
                 availableEventQueue.offer(cameraId);
             }
 
@@ -570,7 +587,7 @@
         } else {
             mCameraManager.registerAvailabilityCallback(ac, mHandler);
         }
-        String[] cameras = mCameraManager.getCameraIdList();
+        String[] cameras = mCameraIdsUnderTest;
 
         if (cameras.length == 0) {
             Log.i(TAG, "No cameras present, skipping test");
@@ -683,12 +700,13 @@
     } // testCameraManagerListenerCallbacks
 
     // Verify no LEGACY-level devices appear on devices first launched in the Q release or newer
+    @Test
     public void testNoLegacyOnQ() throws Exception {
         if(PropertyUtil.getFirstApiLevel() < Build.VERSION_CODES.Q){
             // LEGACY still allowed for devices upgrading to Q
             return;
         }
-        String[] ids = mCameraManager.getCameraIdList();
+        String[] ids = mCameraIdsUnderTest;
         for (int i = 0; i < ids.length; i++) {
             CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
             assertNotNull(
@@ -703,14 +721,15 @@
         }
     }
 
+    @Test
     public void testCameraManagerWithDnD() throws Exception {
-        String[] cameras = mCameraManager.getCameraIdList();
+        String[] cameras = mCameraIdsUnderTest;
         if (cameras.length == 0) {
             Log.i(TAG, "No cameras present, skipping test");
             return;
         }
 
-        ActivityManager am = getContext().getSystemService(ActivityManager.class);
+        ActivityManager am = mContext.getSystemService(ActivityManager.class);
 
         // Go devices do not support all interrupt filtering functionality
         if (am.isLowRamDevice()) {
@@ -718,12 +737,12 @@
         }
 
         // Allow the test package to adjust notification policy
-        toggleNotificationPolicyAccess(getContext().getPackageName(),
+        toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), true);
 
         // Enable DnD filtering
 
-        NotificationManager nm = getContext().getSystemService(NotificationManager.class);
+        NotificationManager nm = mContext.getSystemService(NotificationManager.class);
         try {
             nm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_NONE);
 
@@ -755,7 +774,7 @@
 
         runCommand(command, instrumentation);
 
-        NotificationManager nm = getContext().getSystemService(NotificationManager.class);
+        NotificationManager nm = mContext.getSystemService(NotificationManager.class);
         assertEquals("Notification Policy Access Grant is " +
                 nm.isNotificationPolicyAccessGranted() + " not " + on, on,
                 nm.isNotificationPolicyAccessGranted());
@@ -764,17 +783,14 @@
     private void runCommand(String command, Instrumentation instrumentation) throws IOException {
         UiAutomation uiAutomation = instrumentation.getUiAutomation();
         // Execute command
-        try (ParcelFileDescriptor fd = uiAutomation.executeShellCommand(command)) {
-            assertNotNull("Failed to execute shell command: " + command, fd);
-            // Wait for the command to finish by reading until EOF
-            try (InputStream in = new FileInputStream(fd.getFileDescriptor())) {
-                byte[] buffer = new byte[4096];
-                while (in.read(buffer) > 0) {}
-            } catch (IOException e) {
-                throw new IOException("Could not read stdout of command: " + command, e);
-            }
-        } finally {
-            uiAutomation.destroy();
+        ParcelFileDescriptor fd = mUiAutomation.executeShellCommand(command);
+        assertNotNull("Failed to execute shell command: " + command, fd);
+        // Wait for the command to finish by reading until EOF
+        try (InputStream in = new FileInputStream(fd.getFileDescriptor())) {
+            byte[] buffer = new byte[4096];
+            while (in.read(buffer) > 0) {}
+        } catch (IOException e) {
+            throw new IOException("Could not read stdout of command: " + command, e);
         }
     }
 
diff --git a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
index 2a46cf7..b02f13d 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -52,6 +52,8 @@
 import java.util.Arrays;
 import java.util.List;
 
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
 import org.junit.Test;
 
 /**
@@ -63,6 +65,8 @@
  * manual ISP control and other per-frame control and synchronization.
  * </p>
  */
+
+@RunWith(Parameterized.class)
 public class CaptureRequestTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "CaptureRequestTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -147,9 +151,9 @@
         SurfaceTexture outputTexture = new SurfaceTexture(/* random texture ID */ 5);
         Surface surface = new Surface(outputTexture);
 
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                openDevice(mCameraIds[i]);
+                openDevice(mCameraIdsUnderTest[i]);
                 CaptureRequest.Builder requestBuilder =
                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                 requestBuilder.addTarget(surface);
@@ -236,14 +240,14 @@
      */
     @Test
     public void testBlackLevelLock() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                if (!mAllStaticInfo.get(mCameraIds[i]).isCapabilitySupported(
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isCapabilitySupported(
                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
                     continue;
                 }
 
-                openDevice(mCameraIds[i]);
+                openDevice(mCameraIdsUnderTest[i]);
                 SimpleCaptureCallback listener = new SimpleCaptureCallback();
                 CaptureRequest.Builder requestBuilder =
                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
@@ -290,7 +294,7 @@
      */
     @Test
     public void testDynamicBlackWhiteLevel() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 if (!mAllStaticInfo.get(id).isDynamicBlackLevelSupported()) {
                     continue;
@@ -318,11 +322,11 @@
      */
     @Test
     public void testLensShadingMap() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIds[i]);
+                StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIdsUnderTest[i]);
                 if (!staticInfo.isManualLensShadingMapSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " doesn't support lens shading controls, skipping test");
                     continue;
                 }
@@ -334,7 +338,7 @@
                     continue;
                 }
 
-                openDevice(mCameraIds[i]);
+                openDevice(mCameraIdsUnderTest[i]);
                 SimpleCaptureCallback listener = new SimpleCaptureCallback();
                 CaptureRequest.Builder requestBuilder =
                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
@@ -396,15 +400,15 @@
      */
     @Test
     public void testAntiBandingModes() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
                 // Without manual sensor control, exposure time cannot be verified
-                if (!mAllStaticInfo.get(mCameraIds[i]).isCapabilitySupported(
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isCapabilitySupported(
                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
                     continue;
                 }
 
-                openDevice(mCameraIds[i]);
+                openDevice(mCameraIdsUnderTest[i]);
                 int[] modes = mStaticInfo.getAeAvailableAntiBandingModesChecked();
 
                 Size previewSz =
@@ -432,15 +436,15 @@
      */
     @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
     public void testAeModeAndLock() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
 
-                openDevice(mCameraIds[i]);
+                openDevice(mCameraIdsUnderTest[i]);
                 Size maxPreviewSz = mOrderedPreviewSizes.get(0); // Max preview size.
 
                 // Update preview surface with given size for all sub-tests.
@@ -465,15 +469,15 @@
      */
     @Test
     public void testFlashControl() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
 
-                openDevice(mCameraIds[i]);
+                openDevice(mCameraIdsUnderTest[i]);
                 SimpleCaptureCallback listener = new SimpleCaptureCallback();
                 CaptureRequest.Builder requestBuilder =
                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
@@ -509,15 +513,15 @@
      */
     @Test
     public void testFlashTurnOff() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
 
-                openDevice(mCameraIds[i]);
+                openDevice(mCameraIdsUnderTest[i]);
                 SimpleCaptureCallback listener = new SimpleCaptureCallback();
                 CaptureRequest.Builder requestBuilder =
                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
@@ -550,14 +554,14 @@
      */
     @Test
     public void testFaceDetection() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
-                openDevice(mCameraIds[i]);
+                openDevice(mCameraIdsUnderTest[i]);
                 faceDetectionTestByCamera();
             } finally {
                 closeDevice();
@@ -570,7 +574,7 @@
      */
     @Test
     public void testToneMapControl() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 if (!mAllStaticInfo.get(id).isManualToneMapSupported()) {
                     Log.i(TAG, "Camera " + id +
@@ -590,7 +594,7 @@
      */
     @Test
     public void testColorCorrectionControl() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 if (!mAllStaticInfo.get(id).isColorCorrectionSupported()) {
                     Log.i(TAG, "Camera " + id +
@@ -610,7 +614,7 @@
      */
     @Test
     public void testEdgeModeControl() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 if (!mAllStaticInfo.get(id).isEdgeModeControlSupported()) {
                     Log.i(TAG, "Camera " + id +
@@ -632,7 +636,7 @@
      */
     @Test
     public void testEdgeModeControlFastFps() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 if (!mAllStaticInfo.get(id).isEdgeModeControlSupported()) {
                     Log.i(TAG, "Camera " + id +
@@ -655,7 +659,7 @@
      */
     @Test
     public void testFocusDistanceControl() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 StaticMetadata staticInfo = mAllStaticInfo.get(id);
                 if (!staticInfo.hasFocuser()) {
@@ -683,7 +687,7 @@
      */
     @Test
     public void testNoiseReductionModeControl() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 if (!mAllStaticInfo.get(id).isNoiseReductionModeControlSupported()) {
                     Log.i(TAG, "Camera " + id +
@@ -705,7 +709,7 @@
      */
     @Test
     public void testNoiseReductionModeControlFastFps() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 if (!mAllStaticInfo.get(id).isNoiseReductionModeControlSupported()) {
                     Log.i(TAG, "Camera " + id +
@@ -729,7 +733,7 @@
      */
     @Test
     public void testAwbModeAndLock() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
@@ -748,7 +752,7 @@
      */
     @Test
     public void testAfModes() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
@@ -767,7 +771,7 @@
      */
     @Test
     public void testCameraStabilizations() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 StaticMetadata staticInfo = mAllStaticInfo.get(id);
                 List<Key<?>> keys = staticInfo.getCharacteristics().getKeys();
@@ -796,7 +800,7 @@
      */
     @Test
     public void testDigitalZoom() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
@@ -817,7 +821,7 @@
      */
     @Test
     public void testDigitalZoomPreviewCombinations() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
@@ -836,7 +840,7 @@
      */
     @Test
     public void testSceneModes() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 if (mAllStaticInfo.get(id).isSceneModeSupported()) {
                     openDevice(id);
@@ -853,7 +857,7 @@
      */
     @Test
     public void testEffectModes() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
diff --git a/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java b/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
index ab65015..0e68bd4 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;
@@ -37,6 +38,7 @@
 
 import static android.hardware.camera2.cts.CameraTestUtils.*;
 import static android.hardware.camera2.cts.helpers.CameraSessionUtils.*;
+import static junit.framework.Assert.*;
 
 import android.util.Log;
 import android.view.Surface;
@@ -51,6 +53,11 @@
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(Parameterized.class)
 public class CaptureResultTest extends Camera2AndroidTestCase {
     private static final String TAG = "CaptureResultTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -58,29 +65,15 @@
     private static final int NUM_FRAMES_VERIFIED = 30;
     private static final long WAIT_FOR_RESULT_TIMEOUT_MS = 3000;
 
-
     // List tracking the failed test keys.
 
     @Override
-    public void setContext(Context context) {
-        super.setContext(context);
-
-        /**
-         * Workaround for mockito and JB-MR2 incompatibility
-         *
-         * Avoid java.lang.IllegalArgumentException: dexcache == null
-         * https://code.google.com/p/dexmaker/issues/detail?id=2
-         */
-        System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
-    }
-
-    @Override
-    protected void setUp() throws Exception {
+    public void setUp() throws Exception {
         super.setUp();
     }
 
     @Override
-    protected void tearDown() throws Exception {
+    public void tearDown() throws Exception {
         super.tearDown();
     }
 
@@ -95,8 +88,9 @@
      * a capture result.
      * </p>
      */
+    @Test
     public void testCameraCaptureResultAllKeys() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 openDevice(id);
                 if (mStaticInfo.isColorOutputSupported()) {
@@ -149,10 +143,11 @@
      * onCaptureProgressed callbacks.
      * </ul></p>
      */
+    @Test
     public void testPartialResult() throws Exception {
         final int NUM_FRAMES_TESTED = 30;
         final int WAIT_FOR_RESULT_TIMOUT_MS = 2000;
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 // Skip the test if partial result is not supported
                 int partialResultCount = mAllStaticInfo.get(id).getPartialResultCount();
@@ -264,8 +259,9 @@
      * Check that the timestamps passed in the results, buffers, and capture callbacks match for
      * a single request, and increase monotonically
      */
+    @Test
     public void testResultTimestamps() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             ImageReader previewReader = null;
             ImageReader jpegReader = null;
 
@@ -401,16 +397,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 +420,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/DngCreatorTest.java b/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
index 4af437c..b0c806f 100644
--- a/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
@@ -60,11 +60,18 @@
 import java.util.TimeZone;
 import java.text.SimpleDateFormat;
 
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
 import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
+import static junit.framework.Assert.*;
 
 /**
  * Tests for the DngCreator API.
  */
+
+@RunWith(Parameterized.class)
 public class DngCreatorTest extends Camera2AndroidTestCase {
     private static final String TAG = "DngCreatorTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -99,24 +106,17 @@
     }
 
     @Override
-    protected void setUp() throws Exception {
-        RenderScriptSingleton.setContext(getContext());
-
+    public void setUp() throws Exception {
         super.setUp();
+        RenderScriptSingleton.setContext(mContext);
     }
 
     @Override
-    protected void tearDown() throws Exception {
+    public void tearDown() throws Exception {
         RenderScriptSingleton.clearContext();
-
         super.tearDown();
     }
 
-    @Override
-    public synchronized void setContext(Context context) {
-        super.setContext(context);
-    }
-
     /**
      * Test basic raw capture and DNG saving functionality for each of the available cameras.
      *
@@ -131,16 +131,17 @@
      * raw image captured for the first reported camera device to be saved to an output file.
      * </p>
      */
+    @Test
     public void testSingleImageBasic() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
-            String deviceId = mCameraIds[i];
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+            String deviceId = mCameraIdsUnderTest[i];
             ImageReader captureReader = null;
             FileOutputStream fileStream = null;
             ByteArrayOutputStream outputStream = null;
             try {
                 if (!mAllStaticInfo.get(deviceId).isCapabilitySupported(
                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
-                    Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
+                    Log.i(TAG, "RAW capability is not supported in camera " + mCameraIdsUnderTest[i] +
                             ". Skip the test.");
                     continue;
                 }
@@ -205,9 +206,10 @@
      * raw image captured for the first reported camera device to be saved to an output file.
      * </p>
      */
+    @Test
     public void testSingleImageThumbnail() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
-            String deviceId = mCameraIds[i];
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+            String deviceId = mCameraIdsUnderTest[i];
             List<ImageReader> captureReaders = new ArrayList<ImageReader>();
             List<CameraTestUtils.SimpleImageReaderListener> captureListeners =
                     new ArrayList<CameraTestUtils.SimpleImageReaderListener>();
@@ -216,7 +218,7 @@
             try {
                 if (!mAllStaticInfo.get(deviceId).isCapabilitySupported(
                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
-                    Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
+                    Log.i(TAG, "RAW capability is not supported in camera " + mCameraIdsUnderTest[i] +
                             ". Skip the test.");
                     continue;
                 }
@@ -369,8 +371,9 @@
      * adb shell setprop log.tag.DngCreatorTest VERBOSE
      * </p>
      */
+    @Test
     public void testRaw16JpegConsistency() throws Exception {
-        for (String deviceId : mCameraIds) {
+        for (String deviceId : mCameraIdsUnderTest) {
             List<ImageReader> captureReaders = new ArrayList<>();
             FileOutputStream fileStream = null;
             ByteArrayOutputStream outputStream = null;
@@ -461,8 +464,9 @@
     /**
      * Test basic DNG creation, ensure that the DNG image can be rendered by BitmapFactory.
      */
+    @Test
     public void testDngRenderingByBitmapFactor() throws Exception {
-        for (String deviceId : mCameraIds) {
+        for (String deviceId : mCameraIdsUnderTest) {
             List<ImageReader> captureReaders = new ArrayList<>();
 
             CapturedData data = captureRawJpegImagePair(deviceId, captureReaders);
diff --git a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index a5ea222..d761c6f 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -59,14 +59,21 @@
 import java.util.regex.Pattern;
 import java.util.Set;
 
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
 import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
 import static android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
 
+import static junit.framework.Assert.*;
+
 import static org.mockito.Mockito.*;
 
 /**
  * Extended tests for static camera characteristics.
  */
+@RunWith(Parameterized.class)
 public class ExtendedCameraCharacteristicsTest extends Camera2AndroidTestCase {
     private static final String TAG = "ExChrsTest"; // must be short so next line doesn't throw
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -128,7 +135,7 @@
     private static final int HIGH_SPEED_FPS_UPPER_MIN = 120;
 
     @Override
-    protected void setUp() throws Exception {
+    public void setUp() throws Exception {
         super.setUp();
         mCharacteristics = new ArrayList<>();
         for (int i = 0; i < mAllCameraIds.length; i++) {
@@ -137,7 +144,7 @@
     }
 
     @Override
-    protected void tearDown() throws Exception {
+    public void tearDown() throws Exception {
         super.tearDown();
         mCharacteristics = null;
     }
@@ -146,6 +153,7 @@
      * Test that the available stream configurations contain a few required formats and sizes.
      */
     @CddTest(requirement="7.5.1/C-1-2")
+    @Test
     public void testAvailableStreamConfigs() throws Exception {
         boolean firstBackFacingCamera = true;
         for (int i = 0; i < mAllCameraIds.length; i++) {
@@ -169,7 +177,7 @@
 
             boolean isMonochromeWithY8 = arrayContains(actualCapabilities, MONOCHROME)
                     && arrayContains(outputFormats, ImageFormat.Y8);
-            boolean isHiddenPhysicalCamera = !arrayContains(mCameraIds, mAllCameraIds[i]);
+            boolean isHiddenPhysicalCamera = !arrayContains(mCameraIdsUnderTest, mAllCameraIds[i]);
             boolean supportHeic = arrayContains(outputFormats, ImageFormat.HEIC);
 
             assertArrayContains(
@@ -769,6 +777,7 @@
 
     }
 
+    @Test
     public void testRecommendedStreamConfigurations() throws Exception {
         for (int i = 0; i < mAllCameraIds.length; i++) {
             CameraCharacteristics c = mCharacteristics.get(i);
@@ -868,6 +877,7 @@
     /**
      * Test {@link CameraCharacteristics#getKeys}
      */
+    @Test
     public void testKeys() {
         for (int i = 0; i < mAllCameraIds.length; i++) {
             CameraCharacteristics c = mCharacteristics.get(i);
@@ -1036,6 +1046,7 @@
     /**
      * Test values for static metadata used by the RAW capability.
      */
+    @Test
     public void testStaticRawCharacteristics() {
         for (int i = 0; i < mAllCameraIds.length; i++) {
             CameraCharacteristics c = mCharacteristics.get(i);
@@ -1142,6 +1153,7 @@
     /**
      * Test values for the available session keys.
      */
+    @Test
     public void testStaticSessionKeys() throws Exception {
         for (CameraCharacteristics c : mCharacteristics) {
             List<CaptureRequest.Key<?>> availableSessionKeys = c.getAvailableSessionKeys();
@@ -1161,6 +1173,7 @@
     /**
      * Test values for static metadata used by the BURST capability.
      */
+    @Test
     public void testStaticBurstCharacteristics() throws Exception {
         for (int i = 0; i < mAllCameraIds.length; i++) {
             CameraCharacteristics c = mCharacteristics.get(i);
@@ -1325,6 +1338,7 @@
     /**
      * Check reprocessing capabilities.
      */
+    @Test
     public void testReprocessingCharacteristics() {
         for (int i = 0; i < mAllCameraIds.length; i++) {
             Log.i(TAG, "testReprocessingCharacteristics: Testing camera ID " + mAllCameraIds[i]);
@@ -1453,6 +1467,7 @@
     /**
      * Check depth output capability
      */
+    @Test
     public void testDepthOutputCharacteristics() {
         for (int i = 0; i < mAllCameraIds.length; i++) {
             Log.i(TAG, "testDepthOutputCharacteristics: Testing camera ID " + mAllCameraIds[i]);
@@ -1728,6 +1743,7 @@
     /**
      * Cross-check StreamConfigurationMap output
      */
+    @Test
     public void testStreamConfigurationMap() throws Exception {
         for (int i = 0; i < mAllCameraIds.length; i++) {
             Log.i(TAG, "testStreamConfigurationMap: Testing camera ID " + mAllCameraIds[i]);
@@ -1933,6 +1949,7 @@
      * Test high speed capability and cross-check the high speed sizes and fps ranges from
      * the StreamConfigurationMap.
      */
+    @Test
     public void testConstrainedHighSpeedCapability() throws Exception {
         for (int i = 0; i < mAllCameraIds.length; i++) {
             CameraCharacteristics c = mCharacteristics.get(i);
@@ -2013,6 +2030,7 @@
     /**
      * Sanity check of optical black regions.
      */
+    @Test
     public void testOpticalBlackRegions() {
         for (int i = 0; i < mAllCameraIds.length; i++) {
             CameraCharacteristics c = mCharacteristics.get(i);
@@ -2089,6 +2107,7 @@
     /**
      * Check Logical camera capability
      */
+    @Test
     public void testLogicalCameraCharacteristics() throws Exception {
         for (int i = 0; i < mAllCameraIds.length; i++) {
             CameraCharacteristics c = mCharacteristics.get(i);
@@ -2163,6 +2182,7 @@
     /**
      * Check monochrome camera capability
      */
+    @Test
     public void testMonochromeCharacteristics() {
         for (int i = 0; i < mAllCameraIds.length; i++) {
             Log.i(TAG, "testMonochromeCharacteristics: Testing camera ID " + mAllCameraIds[i]);
@@ -2297,7 +2317,13 @@
      * accessible via Camera2.
      */
     @CddTest(requirement="7.5.4/C-0-11")
+    @Test
     public void testLegacyCameraDeviceParity() {
+        if (mAdoptShellPerm) {
+            // There is no current way to determine in camera1 api if a device is a system camera
+            // Skip test, http://b/141496896
+            return;
+        }
         int legacyDeviceCount = Camera.getNumberOfCameras();
         assertTrue("More legacy devices: " + legacyDeviceCount + " compared to Camera2 devices: " +
                 mCharacteristics.size(), legacyDeviceCount <= mCharacteristics.size());
@@ -2339,6 +2365,7 @@
      * Check camera orientation against device orientation
      */
     @CddTest(requirement="7.5.5/C-1-1")
+    @Test
     public void testCameraOrientationAlignedWithDevice() {
         WindowManager windowManager =
                 (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
diff --git a/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java b/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
index 0716c24..fc09e51 100644
--- a/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
@@ -41,6 +41,8 @@
 import android.hardware.camera2.cts.CameraTestUtils.SimpleImageReaderListener;
 import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
 
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
 import org.junit.Test;
 
 /**
@@ -50,6 +52,8 @@
  * May not take more than a few seconds to run, to be suitable for quick
  * testing.
  */
+
+@RunWith(Parameterized.class)
 public class FastBasicsTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "FastBasicsTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -62,17 +66,17 @@
     @Presubmit
     @Test
     public void testCamera2() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                Log.i(TAG, "Testing camera2 API for camera device " + mCameraIds[i]);
+                Log.i(TAG, "Testing camera2 API for camera device " + mCameraIdsUnderTest[i]);
 
-                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
 
-                openDevice(mCameraIds[i]);
+                openDevice(mCameraIdsUnderTest[i]);
                 camera2TestByCamera();
             } finally {
                 closeDevice();
diff --git a/tests/camera/src/android/hardware/camera2/cts/FlashlightTest.java b/tests/camera/src/android/hardware/camera2/cts/FlashlightTest.java
index 1aa6a17..fb1bd6c 100644
--- a/tests/camera/src/android/hardware/camera2/cts/FlashlightTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/FlashlightTest.java
@@ -31,10 +31,17 @@
 import java.util.concurrent.TimeUnit;
 
 import static org.mockito.Mockito.*;
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+import static junit.framework.Assert.*;
 
 /**
  * <p>Tests for flashlight API.</p>
  */
+
+@RunWith(Parameterized.class)
 public class FlashlightTest extends Camera2AndroidTestCase {
     private static final String TAG = "FlashlightTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -45,13 +52,13 @@
     private ArrayList<String> mFlashCameraIdList;
 
     @Override
-    protected void setUp() throws Exception {
+    public void setUp() throws Exception {
         super.setUp();
 
         // initialize the list of cameras that have a flash unit so it won't interfere with
         // flash tests.
         mFlashCameraIdList = new ArrayList<String>();
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             StaticMetadata info =
                     new StaticMetadata(mCameraManager.getCameraCharacteristics(id),
                                        CheckLevel.ASSERT, /*collector*/ null);
@@ -61,6 +68,7 @@
         }
     }
 
+    @Test
     public void testSetTorchModeOnOff() throws Exception {
         if (mFlashCameraIdList.size() == 0)
             return;
@@ -124,6 +132,7 @@
         }
     }
 
+    @Test
     public void testTorchCallback() throws Exception {
         testTorchCallback(/*useExecutor*/ false);
         testTorchCallback(/*useExecutor*/ true);
@@ -169,12 +178,13 @@
         }
     }
 
+    @Test
     public void testCameraDeviceOpenAfterTorchOn() throws Exception {
         if (mFlashCameraIdList.size() == 0)
             return;
 
         for (String id : mFlashCameraIdList) {
-            for (String idToOpen : mCameraIds) {
+            for (String idToOpen : mCameraIdsUnderTest) {
                 resetTorchModeStatus(id);
 
                 CameraManager.TorchCallback torchListener =
@@ -225,15 +235,16 @@
         }
     }
 
+    @Test
     public void testTorchModeExceptions() throws Exception {
         // cameraIdsToTestTorch = all available camera ID + non-existing camera id +
         //                        non-existing numeric camera id + null
-        String[] cameraIdsToTestTorch = new String[mCameraIds.length + 3];
-        System.arraycopy(mCameraIds, 0, cameraIdsToTestTorch, 0, mCameraIds.length);
-        cameraIdsToTestTorch[mCameraIds.length] = generateNonexistingCameraId();
-        cameraIdsToTestTorch[mCameraIds.length + 1] = generateNonexistingNumericCameraId();
+        String[] cameraIdsToTestTorch = new String[mCameraIdsUnderTest.length + 3];
+        System.arraycopy(mCameraIdsUnderTest, 0, cameraIdsToTestTorch, 0, mCameraIdsUnderTest.length);
+        cameraIdsToTestTorch[mCameraIdsUnderTest.length] = generateNonexistingCameraId();
+        cameraIdsToTestTorch[mCameraIdsUnderTest.length + 1] = generateNonexistingNumericCameraId();
 
-        for (String idToOpen : mCameraIds) {
+        for (String idToOpen : mCameraIdsUnderTest) {
             openDevice(idToOpen);
             try {
                 for (String id : cameraIdsToTestTorch) {
@@ -288,8 +299,8 @@
 
     private String generateNonexistingCameraId() {
         String nonExisting = "none_existing_camera";
-        for (String id : mCameraIds) {
-            if (Arrays.asList(mCameraIds).contains(nonExisting)) {
+        for (String id : mCameraIdsUnderTest) {
+            if (Arrays.asList(mCameraIdsUnderTest).contains(nonExisting)) {
                 nonExisting += id;
             } else {
                 break;
@@ -299,11 +310,15 @@
     }
 
     // return a non-existing and non-negative numeric camera id.
-    private String generateNonexistingNumericCameraId() {
-        int[] numericCameraIds = new int[mCameraIds.length];
+    private String generateNonexistingNumericCameraId() throws Exception {
+        // We don't rely on mCameraIdsUnderTest to generate a non existing camera id since
+        // mCameraIdsUnderTest doesn't give us an accurate reflection of which camera ids actually
+        // exist. It just tells us the ones we're testing right now.
+        String[] allCameraIds = mCameraManager.getCameraIdListNoLazy();
+        int[] numericCameraIds = new int[allCameraIds.length];
         int size = 0;
 
-        for (String cameraId : mCameraIds) {
+        for (String cameraId : allCameraIds) {
             try {
                 int value = Integer.parseInt(cameraId);
                 if (value >= 0) {
diff --git a/tests/camera/src/android/hardware/camera2/cts/HeifWriterTest.java b/tests/camera/src/android/hardware/camera2/cts/HeifWriterTest.java
index 0cb5c1b..f729cc7 100644
--- a/tests/camera/src/android/hardware/camera2/cts/HeifWriterTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/HeifWriterTest.java
@@ -55,6 +55,11 @@
 import java.util.Arrays;
 import java.util.List;
 
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(Parameterized.class)
 public class HeifWriterTest extends Camera2AndroidTestCase {
     private static final String TAG = HeifWriterTest.class.getSimpleName();
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -79,6 +84,7 @@
         super.tearDown();
     }
 
+    @Test
     public void testHeif() throws Exception {
         final int NUM_SINGLE_CAPTURE_TESTED = 3;
         final int NUM_HEIC_CAPTURE_TESTED = 2;
@@ -93,7 +99,7 @@
         boolean sessionFailure = false;
         Integer[] sessionStates = {BlockingSessionCallback.SESSION_READY,
                 BlockingSessionCallback.SESSION_CONFIGURE_FAILED};
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.v(TAG, "Testing HEIF capture for Camera " + id);
                 openDevice(id);
diff --git a/tests/camera/src/android/hardware/camera2/cts/IdleUidTest.java b/tests/camera/src/android/hardware/camera2/cts/IdleUidTest.java
index 34d0947..9caf365 100644
--- a/tests/camera/src/android/hardware/camera2/cts/IdleUidTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/IdleUidTest.java
@@ -26,9 +26,12 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
+import android.hardware.camera2.cts.Camera2ParameterizedTestCase;
+import android.hardware.camera2.cts.CameraTestUtils;
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CameraManager;
+
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Process;
@@ -40,6 +43,8 @@
 
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -52,8 +57,9 @@
  * get an error callback losing the camera handle. Similarly if the UID is
  * already idle it cannot obtain a camera handle.
  */
-@RunWith(AndroidJUnit4.class)
-public final class IdleUidTest {
+
+@RunWith(Parameterized.class)
+public final class IdleUidTest extends Camera2ParameterizedTestCase {
     private static final long CAMERA_OPERATION_TIMEOUT_MILLIS = 5000; // 5 sec
 
     private static final HandlerThread sCallbackThread = new HandlerThread("Callback thread");
@@ -73,10 +79,8 @@
      */
     @Test
     public void testCameraAccessForIdleUid() throws Exception {
-        final CameraManager cameraManager = InstrumentationRegistry.getTargetContext()
-                .getSystemService(CameraManager.class);
-        for (String cameraId : cameraManager.getCameraIdList()) {
-            testCameraAccessForIdleUidByCamera(cameraManager, cameraId,
+        for (String cameraId : mCameraIdsUnderTest) {
+            testCameraAccessForIdleUidByCamera(cameraId,
                     new Handler(sCallbackThread.getLooper()));
         }
     }
@@ -86,32 +90,29 @@
      */
     @Test
     public void testCameraAccessBecomingInactiveUid() throws Exception {
-        final CameraManager cameraManager = InstrumentationRegistry.getTargetContext()
-                .getSystemService(CameraManager.class);
-        for (String cameraId : cameraManager.getCameraIdList()) {
-            testCameraAccessBecomingInactiveUidByCamera(cameraManager, cameraId,
+        for (String cameraId : mCameraIdsUnderTest) {
+            testCameraAccessBecomingInactiveUidByCamera(cameraId,
                     new Handler(sCallbackThread.getLooper()));
         }
-
     }
 
-    private void testCameraAccessForIdleUidByCamera(CameraManager cameraManager,
-            String cameraId, Handler handler) throws Exception {
+    private void testCameraAccessForIdleUidByCamera(String cameraId, Handler handler)
+            throws Exception {
         // Can access camera from an active UID.
-        assertCameraAccess(cameraManager, cameraId, true, handler);
+        assertCameraAccess(mCameraManager, cameraId, true, handler);
 
         // Make our UID idle
         makeMyPackageIdle();
         try {
             // Can not access camera from an idle UID.
-            assertCameraAccess(cameraManager, cameraId, false, handler);
+            assertCameraAccess(mCameraManager, cameraId, false, handler);
         } finally {
             // Restore our UID as active
             makeMyPackageActive();
         }
 
         // Can access camera from an active UID.
-        assertCameraAccess(cameraManager, cameraId, true, handler);
+        assertCameraAccess(mCameraManager, cameraId, true, handler);
     }
 
     private static void assertCameraAccess(CameraManager cameraManager,
@@ -152,14 +153,14 @@
         }
     }
 
-    private void testCameraAccessBecomingInactiveUidByCamera(CameraManager cameraManager,
-            String cameraId, Handler handler) throws Exception {
+    private void testCameraAccessBecomingInactiveUidByCamera(String cameraId, Handler handler)
+            throws Exception {
         // Mock the callback used to observe camera state.
         final CameraDevice.StateCallback callback = mock(CameraDevice.StateCallback.class);
 
         // Open the camera
         try {
-            cameraManager.openCamera(cameraId, callback, handler);
+            mCameraManager.openCamera(cameraId, callback, handler);
         } catch (CameraAccessException e) {
             fail("Unexpected exception" + e);
         }
diff --git a/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java b/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
index abd6bb6..971b51c 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
@@ -52,11 +52,17 @@
 import java.util.Arrays;
 import java.util.List;
 
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.Test;
+
 import static android.hardware.camera2.cts.CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS;
+import static android.hardware.camera2.cts.CameraTestUtils.SESSION_READY_TIMEOUT_MS;
 import static android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
 import static android.hardware.camera2.cts.CameraTestUtils.SimpleImageReaderListener;
 import static android.hardware.camera2.cts.CameraTestUtils.dumpFile;
 import static android.hardware.camera2.cts.CameraTestUtils.getValueNotNull;
+import static junit.framework.Assert.*;
 
 /**
  * <p>Basic test for ImageReader APIs. It uses CameraDevice as producer, camera
@@ -68,6 +74,7 @@
  * <p>Some invalid access test. </p>
  * <p>TODO: Add more format tests? </p>
  */
+@RunWith(Parameterized.class)
 public class ImageReaderTest extends Camera2AndroidTestCase {
     private static final String TAG = "ImageReaderTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -92,22 +99,18 @@
     private SimpleImageListener mListener;
 
     @Override
-    public void setContext(Context context) {
-        super.setContext(context);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
+    public void setUp() throws Exception {
         super.setUp();
     }
 
     @Override
-    protected void tearDown() throws Exception {
+    public void tearDown() throws Exception {
         super.tearDown();
     }
 
+    @Test
     public void testFlexibleYuv() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing Camera " + id);
                 openDevice(id);
@@ -118,8 +121,9 @@
         }
     }
 
+    @Test
     public void testDepth16() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing Camera " + id);
                 openDevice(id);
@@ -130,8 +134,9 @@
         }
     }
 
+    @Test
     public void testDepthPointCloud() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing Camera " + id);
                 openDevice(id);
@@ -142,8 +147,9 @@
         }
     }
 
+    @Test
     public void testDynamicDepth() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 openDevice(id);
                 bufferFormatTestByCamera(ImageFormat.DEPTH_JPEG, /*repeating*/true,
@@ -154,8 +160,9 @@
         }
     }
 
+    @Test
     public void testY8() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing Camera " + id);
                 openDevice(id);
@@ -166,8 +173,9 @@
         }
     }
 
+    @Test
     public void testJpeg() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.v(TAG, "Testing jpeg capture for Camera " + id);
                 openDevice(id);
@@ -178,8 +186,9 @@
         }
     }
 
+    @Test
     public void testRaw() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.v(TAG, "Testing raw capture for camera " + id);
                 openDevice(id);
@@ -191,8 +200,9 @@
         }
     }
 
+    @Test
     public void testRawPrivate() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.v(TAG, "Testing raw capture for camera " + id);
                 openDevice(id);
@@ -204,8 +214,9 @@
         }
     }
 
+    @Test
     public void testHeic() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.v(TAG, "Testing heic capture for Camera " + id);
                 openDevice(id);
@@ -216,8 +227,9 @@
         }
     }
 
+    @Test
     public void testRepeatingJpeg() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.v(TAG, "Testing repeating jpeg capture for Camera " + id);
                 openDevice(id);
@@ -228,8 +240,9 @@
         }
     }
 
+    @Test
     public void testRepeatingRaw() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.v(TAG, "Testing repeating raw capture for camera " + id);
                 openDevice(id);
@@ -241,8 +254,9 @@
         }
     }
 
+    @Test
     public void testRepeatingRawPrivate() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.v(TAG, "Testing repeating raw capture for camera " + id);
                 openDevice(id);
@@ -254,8 +268,9 @@
         }
     }
 
+    @Test
     public void testRepeatingHeic() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.v(TAG, "Testing repeating heic capture for Camera " + id);
                 openDevice(id);
@@ -266,8 +281,9 @@
         }
     }
 
+    @Test
     public void testLongProcessingRepeatingRaw() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.v(TAG, "Testing long processing on repeating raw for camera " + id);
 
@@ -284,8 +300,9 @@
         }
     }
 
+    @Test
     public void testLongProcessingRepeatingFlexibleYuv() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.v(TAG, "Testing long processing on repeating YUV for camera " + id);
 
@@ -309,9 +326,10 @@
      * for camera case. For if the produced image byte buffer is not direct byte buffer, there
      * is no guarantee to get an ISE for this invalid access case.
      */
+    @Test
     public void testInvalidAccessTest() throws Exception {
         // Test byte buffer access after an image is released, it should throw ISE.
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.v(TAG, "Testing invalid image access for Camera " + id);
                 openDevice(id);
@@ -328,8 +346,9 @@
      *
      * <p>Both stream formats are mandatory for Camera2 API</p>
      */
+    @Test
     public void testYuvAndJpeg() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.v(TAG, "YUV and JPEG testing for camera " + id);
                 if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
@@ -351,8 +370,9 @@
      *
      * <p>Both stream formats are mandatory for Camera2 API</p>
      */
+    @Test
     public void testYuvAndJpegWithUsageFlag() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.v(TAG, "YUV and JPEG testing for camera " + id);
                 if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
@@ -372,8 +392,9 @@
      * Test two image stream (YUV420_888 and RAW_SENSOR) capture by using ImageReader.
      *
      */
+    @Test
     public void testImageReaderYuvAndRaw() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.v(TAG, "YUV and RAW testing for camera " + id);
                 if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
@@ -394,8 +415,9 @@
      * ImageFormat.PRIVATE + PROTECTED usage capture by using ImageReader with the
      * ImageReader factory method that has usage flag argument, and uses a custom usage flag.
      */
+    @Test
     public void testImageReaderPrivateWithProtectedUsageFlag() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.v(TAG, "Private format and protected usage testing for camera " + id);
                 if (!mAllStaticInfo.get(id).isCapabilitySupported(
@@ -420,8 +442,9 @@
      * ImageReader factory method that has usage flag argument.
      *
      */
+    @Test
     public void testImageReaderYuvAndRawWithUsageFlag() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.v(TAG, "YUV and RAW testing for camera " + id);
                 if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
@@ -441,10 +464,11 @@
      * Check that the center patches for YUV and JPEG outputs for the same frame match for each YUV
      * resolution and format supported.
      */
+    @Test
     public void testAllOutputYUVResolutions() throws Exception {
         Integer[] sessionStates = {BlockingSessionCallback.SESSION_READY,
                 BlockingSessionCallback.SESSION_CONFIGURE_FAILED};
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.v(TAG, "Testing all YUV image resolutions for camera " + id);
 
@@ -685,10 +709,11 @@
     /**
      * Test that images captured after discarding free buffers are valid.
      */
+    @Test
     public void testDiscardFreeBuffers() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
-                Log.v(TAG, "Testing jpeg capture for Camera " + id);
+                Log.v(TAG, "Testing discardFreeBuffers for Camera " + id);
                 openDevice(id);
                 discardFreeBuffersTestByCamera();
             } finally {
@@ -698,6 +723,7 @@
     }
 
     /** Tests that usage bits are preserved */
+    @Test
     public void testUsageRespected() throws Exception {
         ImageReader reader = ImageReader.newInstance(1, 1, PixelFormat.RGBA_8888, 1,
                 HardwareBuffer.USAGE_GPU_COLOR_OUTPUT | HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
@@ -964,13 +990,13 @@
 
         final Size SIZE = mStaticInfo.getAvailableSizesForFormatChecked(FORMAT,
                 StaticMetadata.StreamDirection.Output)[0];
-        Image img = null;
         // Create ImageReader.
         mListener = new SimpleImageListener();
         createDefaultImageReader(SIZE, FORMAT, MAX_NUM_IMAGES, mListener);
 
         // Start capture.
         final boolean REPEATING = true;
+        final boolean SINGLE = false;
         CaptureRequest request = prepareCaptureRequest();
         SimpleCaptureCallback listener = new SimpleCaptureCallback();
         startCapture(request, REPEATING, listener, mHandler);
@@ -985,6 +1011,23 @@
         // Validate images and capture resulst again.
         validateImage(SIZE, FORMAT, NUM_FRAME_VERIFIED, REPEATING);
         validateCaptureResult(FORMAT, SIZE, listener, NUM_FRAME_VERIFIED);
+
+        // Stop repeating request in preparation for discardFreeBuffers
+        mCameraSession.stopRepeating();
+        mCameraSessionListener.getStateWaiter().waitForState(
+                BlockingSessionCallback.SESSION_READY, SESSION_READY_TIMEOUT_MS);
+
+        // Drain the reader queue and discard free buffers from the reader.
+        Image img = mReader.acquireLatestImage();
+        if (img != null) {
+            img.close();
+        }
+        mReader.discardFreeBuffers();
+
+        // Do a single capture for camera device to reallocate buffers
+        mListener.reset();
+        startCapture(request, SINGLE, listener, mHandler);
+        validateImage(SIZE, FORMAT, /*captureCount*/1, SINGLE);
     }
 
     private void bufferFormatTestByCamera(int format, boolean repeating) throws Exception {
@@ -1212,6 +1255,10 @@
                 image.close();
             }
         }
+
+        public void reset() {
+            imageAvailable.close();
+        }
     }
 
     private void validateImage(Size sz, int format, int captureCount,  boolean repeating)
diff --git a/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java b/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java
index 38f8816..e5cbf5e 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java
@@ -17,8 +17,10 @@
 package android.hardware.camera2.cts;
 
 import static android.hardware.camera2.cts.CameraTestUtils.*;
+import static junit.framework.Assert.*;
 
 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;
@@ -38,6 +40,10 @@
 import java.util.Arrays;
 import java.util.List;
 
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.Test;
+
 /**
  * <p>
  * Basic test for ImageWriter APIs. ImageWriter takes the images produced by
@@ -45,6 +51,7 @@
  * interface or ImageReader.
  * </p>
  */
+@RunWith(Parameterized.class)
 public class ImageWriterTest extends Camera2AndroidTestCase {
     private static final String TAG = "ImageWriterTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -56,7 +63,7 @@
     private ImageWriter mWriter;
 
     @Override
-    protected void tearDown() throws Exception {
+    public void tearDown() throws Exception {
         try {
             closeImageReader(mReaderForWriter);
         } finally {
@@ -88,8 +95,9 @@
      * interface.</li>
      * </p>
      */
+    @Test
     public void testYuvImageWriterReaderOperation() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing Camera " + id);
                 if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
@@ -110,8 +118,9 @@
      * factory method of ImageReader and ImageWriter.
      * </p>
      */
+    @Test
     public void testYuvImageWriterReaderOperationAlt() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing Camera " + id);
                 if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
@@ -126,6 +135,7 @@
         }
     }
 
+    @Test
     public void testAbandonedSurfaceExceptions() throws Exception {
         final int READER_WIDTH = 1920;
         final int READER_HEIGHT = 1080;
@@ -170,6 +180,24 @@
         }
     }
 
+    @Test
+    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..27e6492 100644
--- a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
@@ -66,6 +66,8 @@
 import java.util.Map;
 import java.util.Set;
 
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
 import org.junit.Test;
 
 import static org.mockito.Mockito.*;
@@ -73,6 +75,7 @@
 /**
  * Tests exercising logical camera setup, configuration, and usage.
  */
+@RunWith(Parameterized.class)
 public final class LogicalCameraDeviceTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "LogicalCameraDeviceTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -107,7 +110,7 @@
      */
     @Test
     public void testInvalidPhysicalCameraIdInOutputConfiguration() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing Camera " + id);
                 if (mAllStaticInfo.get(id).isHardwareLevelLegacy()) {
@@ -167,7 +170,7 @@
     @Test
     public void testBasicPhysicalStreaming() throws Exception {
 
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing Camera " + id);
 
@@ -209,7 +212,7 @@
     @Test
     public void testBasicLogicalPhysicalStreamCombination() throws Exception {
 
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing Camera " + id);
 
@@ -331,7 +334,7 @@
     @Test
     public void testBasicPhysicalRequests() throws Exception {
 
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing Camera " + id);
 
@@ -462,7 +465,7 @@
     @Test
     public void testInvalidPhysicalCameraRequests() throws Exception {
 
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing Camera " + id);
 
@@ -569,7 +572,7 @@
     @Test
     public void testLogicalCameraZoomSwitch() throws Exception {
 
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing Camera " + id);
 
@@ -708,7 +711,7 @@
      */
     @Test
     public void testActivePhysicalId() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing Camera " + id);
 
@@ -792,7 +795,7 @@
         if (!isHandheldDevice()) {
             return;
         }
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing Camera " + id);
 
@@ -1109,8 +1112,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 +1136,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/camera/src/android/hardware/camera2/cts/MultiViewTest.java b/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
index 5c1f11e..fd081b3 100644
--- a/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
@@ -50,11 +50,15 @@
 import java.util.Arrays;
 import java.util.List;
 
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 import org.junit.Test;
 
 /**
  * CameraDevice test by using combination of SurfaceView, TextureView and ImageReader
  */
+
+@RunWith(Parameterized.class)
 public class MultiViewTest extends Camera2MultiViewTestCase {
     private static final String TAG = "MultiViewTest";
     private final static long WAIT_FOR_COMMAND_TO_COMPLETE = 5000; //ms
@@ -67,7 +71,7 @@
 
     @Test
     public void testTextureViewPreview() throws Exception {
-        for (String cameraId : mCameraIds) {
+        for (String cameraId : mCameraIdsUnderTest) {
             Exception prior = null;
 
             try {
@@ -96,7 +100,7 @@
 
     @Test
     public void testTextureViewPreviewWithImageReader() throws Exception {
-        for (String cameraId : mCameraIds) {
+        for (String cameraId : mCameraIdsUnderTest) {
             Exception prior = null;
 
             ImageVerifierListener yuvListener;
@@ -144,7 +148,7 @@
 
     @Test
     public void testDualTextureViewPreview() throws Exception {
-        for (String cameraId : mCameraIds) {
+        for (String cameraId : mCameraIdsUnderTest) {
             Exception prior = null;
             try {
                 openCamera(cameraId);
@@ -177,7 +181,7 @@
 
     @Test
     public void testDualTextureViewAndImageReaderPreview() throws Exception {
-        for (String cameraId : mCameraIds) {
+        for (String cameraId : mCameraIdsUnderTest) {
             Exception prior = null;
 
             ImageVerifierListener yuvListener;
@@ -223,31 +227,31 @@
     @Test
     public void testDualCameraPreview() throws Exception {
         final int NUM_CAMERAS_TESTED = 2;
-        if (mCameraIds.length < NUM_CAMERAS_TESTED) {
+        if (mCameraIdsUnderTest.length < NUM_CAMERAS_TESTED) {
             return;
         }
 
         try {
             for (int i = 0; i < NUM_CAMERAS_TESTED; i++) {
-                openCamera(mCameraIds[i]);
-                if (!getStaticInfo(mCameraIds[i]).isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                openCamera(mCameraIdsUnderTest[i]);
+                if (!getStaticInfo(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
                 List<TextureView> views = Arrays.asList(mTextureView[i]);
 
-                startTextureViewPreview(mCameraIds[i], views, /*ImageReader*/null);
+                startTextureViewPreview(mCameraIdsUnderTest[i], views, /*ImageReader*/null);
             }
             // TODO: check the framerate is correct
             SystemClock.sleep(PREVIEW_TIME_MS);
             for (int i = 0; i < NUM_CAMERAS_TESTED; i++) {
-                if (!getStaticInfo(mCameraIds[i]).isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                if (!getStaticInfo(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
-                stopPreview(mCameraIds[i]);
+                stopPreview(mCameraIdsUnderTest[i]);
             }
         } catch (BlockingOpenException e) {
             // The only error accepted is ERROR_MAX_CAMERAS_IN_USE, which means HAL doesn't support
@@ -257,7 +261,7 @@
             Log.i(TAG, "Camera HAL does not support dual camera preview. Skip the test");
         } finally {
             for (int i = 0; i < NUM_CAMERAS_TESTED; i++) {
-                closeCamera(mCameraIds[i]);
+                closeCamera(mCameraIdsUnderTest[i]);
             }
         }
     }
@@ -267,7 +271,7 @@
      */
     @Test
     public void testSharedSurfaceBasic() throws Exception {
-        for (String cameraId : mCameraIds) {
+        for (String cameraId : mCameraIdsUnderTest) {
             try {
                 openCamera(cameraId);
                 if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
@@ -409,7 +413,7 @@
      */
     @Test
     public void testSharedSurfaceImageReaderSwitch() throws Exception {
-        for (String cameraId : mCameraIds) {
+        for (String cameraId : mCameraIdsUnderTest) {
             try {
                 openCamera(cameraId);
                 if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
@@ -501,7 +505,7 @@
         int YUVFormats[] = {ImageFormat.YUV_420_888, ImageFormat.YUV_422_888,
             ImageFormat.YUV_444_888, ImageFormat.YUY2, ImageFormat.YV12,
             ImageFormat.NV16, ImageFormat.NV21};
-        for (String cameraId : mCameraIds) {
+        for (String cameraId : mCameraIdsUnderTest) {
             try {
                 openCamera(cameraId);
                 if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
@@ -629,7 +633,7 @@
      */
     @Test
     public void testSharedSurfaceLimit() throws Exception {
-        for (String cameraId : mCameraIds) {
+        for (String cameraId : mCameraIdsUnderTest) {
             try {
                 openCamera(cameraId);
                 if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
@@ -747,7 +751,7 @@
      */
     @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
     public void testSharedSurfaceSwitch() throws Exception {
-        for (String cameraId : mCameraIds) {
+        for (String cameraId : mCameraIdsUnderTest) {
             try {
                 openCamera(cameraId);
                 if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
@@ -848,7 +852,7 @@
      */
     @Test
     public void testTextureImageWriterReaderOperation() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             ImageReader reader = null;
             ImageWriter writer = null;
             Surface writerOutput = null;
@@ -1037,7 +1041,7 @@
      */
     @Test
     public void testSharedSurfaces() throws Exception {
-        for (String cameraId : mCameraIds) {
+        for (String cameraId : mCameraIdsUnderTest) {
             try {
                 openCamera(cameraId);
                 if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
index 291d998..207c3e3 100644
--- a/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
@@ -22,14 +22,17 @@
 import android.util.Size;
 import android.view.Surface;
 
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 import org.junit.Test;
 
 import static org.junit.Assert.assertTrue;
 
-
 /**
  * <p>Basic test for CameraManager class.</p>
  */
+
+@RunWith(Parameterized.class)
 public class NativeCameraDeviceTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "NativeCameraDeviceTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeCameraManagerTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeCameraManagerTest.java
index 08e0363..0ed5f0e 100644
--- a/tests/camera/src/android/hardware/camera2/cts/NativeCameraManagerTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeCameraManagerTest.java
@@ -16,13 +16,21 @@
 
 package android.hardware.camera2.cts;
 
-import android.test.AndroidTestCase;
+import android.hardware.cts.helpers.CameraParameterizedTestCase;
 import android.util.Log;
 
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+import static junit.framework.Assert.*;
+
 /**
  * <p>Basic test for CameraManager class.</p>
  */
-public class NativeCameraManagerTest extends AndroidTestCase {
+
+@RunWith(Parameterized.class)
+public class NativeCameraManagerTest extends CameraParameterizedTestCase {
     private static final String TAG = "NativeCameraManagerTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
 
@@ -33,21 +41,25 @@
         Log.i("NativeCameraManagerTest", "after loadlibrary");
     }
 
+    @Test
     public void testCameraManagerGetAndClose() {
         assertTrue("testCameraManagerGetAndClose fail, see log for details",
                 testCameraManagerGetAndCloseNative());
     }
 
+    @Test
     public void testCameraManagerGetCameraIds() {
         assertTrue("testCameraManagerGetCameraIds fail, see log for details",
                 testCameraManagerGetCameraIdsNative());
     }
 
+    @Test
     public void testCameraManagerAvailabilityCallback() {
         assertTrue("testCameraManagerAvailabilityCallback fail, see log for details",
                 testCameraManagerAvailabilityCallbackNative());
     }
 
+    @Test
     public void testCameraManagerCameraCharacteristics() {
         assertTrue("testCameraManagerCameraCharacteristics fail, see log for details",
                 testCameraManagerCharacteristicsNative());
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java
index 0933363..493e670 100644
--- a/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java
@@ -19,9 +19,16 @@
 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
 import android.util.Log;
 
+import org.junit.Test;
+
+import static junit.framework.Assert.*;
+
 /**
  * <p>Basic test for CameraManager class.</p>
  */
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+@RunWith(Parameterized.class)
 public class NativeImageReaderTest extends Camera2AndroidTestCase {
     private static final String TAG = "NativeImageReaderTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -33,26 +40,31 @@
         Log.i("NativeImageReaderTest", "after loadlibrary");
     }
 
+    @Test
     public void testJpeg() {
         assertTrue("testJpeg fail, see log for details",
                 testJpegNative(mDebugFileNameBase));
     }
 
+    @Test
     public void testY8() {
         assertTrue("testY8 fail, see log for details",
                 testY8Native(mDebugFileNameBase));
     }
 
+    @Test
     public void testHeic() {
         assertTrue("testHeic fail, see log for details",
                 testHeicNative(mDebugFileNameBase));
     }
 
+    @Test
     public void testDepthJpeg() {
         assertTrue("testDepthJpeg fail, see log for details",
                 testDepthJpegNative(mDebugFileNameBase));
     }
 
+    @Test
     public void testImageReaderCloseAcquiredImages() {
         assertTrue("testImageReaderClose fail, see log for details",
                 testImageReaderCloseAcquiredImagesNative());
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeStillCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeStillCaptureTest.java
index a892970..a778f7d 100644
--- a/tests/camera/src/android/hardware/camera2/cts/NativeStillCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeStillCaptureTest.java
@@ -21,6 +21,8 @@
 import android.util.Size;
 import android.view.Surface;
 
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
 import org.junit.Test;
 
 import static org.junit.Assert.assertTrue;
@@ -28,6 +30,8 @@
 /**
  * <p>Basic test for CameraManager class.</p>
  */
+
+@RunWith(Parameterized.class)
 public class NativeStillCaptureTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "NativeStillCaptureTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
diff --git a/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java b/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
index 2d6cb0a..0616433 100644
--- a/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
@@ -36,7 +36,7 @@
 import android.hardware.camera2.cts.CameraTestUtils.SimpleImageReaderListener;
 import android.hardware.camera2.cts.helpers.StaticMetadata;
 import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
-import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
+import android.hardware.camera2.cts.testcases.Camera2AndroidBasicTestCase;
 import android.hardware.camera2.params.InputConfiguration;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.media.Image;
@@ -71,7 +71,7 @@
  * Test camera2 API use case performance KPIs, such as camera open time, session creation time,
  * shutter lag etc. The KPI data will be reported in cts results.
  */
-public class PerformanceTest extends Camera2AndroidTestCase {
+public class PerformanceTest extends Camera2AndroidBasicTestCase {
     private static final String TAG = "PerformanceTest";
     private static final String REPORT_LOG_NAME = "CtsCameraTestCases";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -133,10 +133,10 @@
      * </p>
      */
     public void testCameraLaunch() throws Exception {
-        double[] avgCameraLaunchTimes = new double[mCameraIds.length];
+        double[] avgCameraLaunchTimes = new double[mCameraIdsUnderTest.length];
 
         int counter = 0;
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             // Do NOT move these variables to outer scope
             // They will be passed to DeviceReportLog and their references will be stored
             String streamName = "test_camera_launch";
@@ -259,7 +259,7 @@
                         + ". Max(ms): " + Stat.getMax(cameraLaunchTimes));
             }
         }
-        if (mCameraIds.length != 0) {
+        if (mCameraIdsUnderTest.length != 0) {
             String streamName = "test_camera_launch_average";
             mReportLog = new DeviceReportLog(REPORT_LOG_NAME, streamName);
             mReportLog.setSummary("camera_launch_average_time_for_all_cameras",
@@ -309,10 +309,10 @@
 
     private void testSingleCaptureForFormat(int[] formats, String formatDescription,
             boolean addPreviewDelay) throws Exception {
-        double[] avgResultTimes = new double[mCameraIds.length];
+        double[] avgResultTimes = new double[mCameraIdsUnderTest.length];
 
         int counter = 0;
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             // Do NOT move these variables to outer scope
             // They will be passed to DeviceReportLog and their references will be stored
             String streamName = appendFormatDescription("test_single_capture", formatDescription);
@@ -448,7 +448,7 @@
         }
 
         // Result will not be reported in CTS report if no summary is printed.
-        if (mCameraIds.length != 0) {
+        if (mCameraIdsUnderTest.length != 0) {
             String streamName = appendFormatDescription("test_single_capture_average",
                     formatDescription);
             mReportLog = new DeviceReportLog(REPORT_LOG_NAME, streamName);
@@ -470,8 +470,8 @@
      * </p>
      */
     public void testMultipleCapture() throws Exception {
-        double[] avgResultTimes = new double[mCameraIds.length];
-        double[] avgDurationMs = new double[mCameraIds.length];
+        double[] avgResultTimes = new double[mCameraIdsUnderTest.length];
+        double[] avgDurationMs = new double[mCameraIdsUnderTest.length];
 
         // A simple CaptureSession StateCallback to handle onCaptureQueueEmpty
         class MultipleCaptureStateCallback extends CameraCaptureSession.StateCallback {
@@ -520,7 +520,7 @@
         final MultipleCaptureStateCallback sessionListener = new MultipleCaptureStateCallback();
 
         int counter = 0;
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             // Do NOT move these variables to outer scope
             // They will be passed to DeviceReportLog and their references will be stored
             String streamName = "test_multiple_capture";
@@ -657,7 +657,7 @@
         }
 
         // Result will not be reported in CTS report if no summary is printed.
-        if (mCameraIds.length != 0) {
+        if (mCameraIdsUnderTest.length != 0) {
             String streamName = "test_multiple_capture_average";
             mReportLog = new DeviceReportLog(REPORT_LOG_NAME, streamName);
             mReportLog.setSummary("camera_multiple_capture_result_average_latency_for_all_cameras",
@@ -675,7 +675,7 @@
      * a reprocess request is issued to the time the reprocess image is returned.
      */
     public void testReprocessingLatency() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             for (int format : REPROCESS_FORMATS) {
                 if (!isReprocessSupported(id, format)) {
                     continue;
@@ -705,7 +705,7 @@
      *
      */
     public void testReprocessingThroughput() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             for (int format : REPROCESS_FORMATS) {
                 if (!isReprocessSupported(id, format)) {
                     continue;
@@ -734,7 +734,7 @@
      * time a reprocess request is issued to the time the reprocess image is returned.
      */
     public void testHighQualityReprocessingLatency() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             for (int format : REPROCESS_FORMATS) {
                 if (!isReprocessSupported(id, format)) {
                     continue;
@@ -764,7 +764,7 @@
      *
      */
     public void testHighQualityReprocessingThroughput() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             for (int format : REPROCESS_FORMATS) {
                 if (!isReprocessSupported(id, format)) {
                     continue;
@@ -792,7 +792,7 @@
      * Testing reprocessing caused preview stall (frame drops)
      */
     public void testReprocessingCaptureStall() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             for (int format : REPROCESS_FORMATS) {
                 if (!isReprocessSupported(id, format)) {
                     continue;
diff --git a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
index c61071e..30d1943 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
@@ -55,6 +55,8 @@
 
 import junit.framework.AssertionFailedError;
 
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
 import org.junit.Test;
 
 import java.io.File;
@@ -69,6 +71,7 @@
  * MediaCodec.
  */
 @LargeTest
+@RunWith(Parameterized.class)
 public class RecordingTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "RecordingTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -131,25 +134,25 @@
 
     private void doBasicRecording(boolean useVideoStab, boolean useIntermediateSurface)
             throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                Log.i(TAG, "Testing basic recording for camera " + mCameraIds[i]);
-                StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIds[i]);
+                Log.i(TAG, "Testing basic recording for camera " + mCameraIdsUnderTest[i]);
+                StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIdsUnderTest[i]);
                 if (!staticInfo.isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
 
                 // External camera doesn't support CamcorderProfile recording
                 if (staticInfo.isExternalCamera()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support CamcorderProfile, skipping");
                     continue;
                 }
 
                 if (!staticInfo.isVideoStabilizationSupported() && useVideoStab) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support video stabilization, skipping the stabilization"
                             + " test");
                     continue;
@@ -157,8 +160,8 @@
 
                 // Re-use the MediaRecorder object for the same camera device.
                 mMediaRecorder = new MediaRecorder();
-                openDevice(mCameraIds[i]);
-                initSupportedVideoSize(mCameraIds[i]);
+                openDevice(mCameraIdsUnderTest[i]);
+                initSupportedVideoSize(mCameraIdsUnderTest[i]);
 
                 basicRecordingTestByCamera(mCamcorderProfileList, useVideoStab,
                         useIntermediateSurface);
@@ -256,19 +259,19 @@
      */
     @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
     public void testSupportedVideoSizes() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                Log.i(TAG, "Testing supported video size recording for camera " + mCameraIds[i]);
-                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                Log.i(TAG, "Testing supported video size recording for camera " + mCameraIdsUnderTest[i]);
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
                 // Re-use the MediaRecorder object for the same camera device.
                 mMediaRecorder = new MediaRecorder();
-                openDevice(mCameraIds[i]);
+                openDevice(mCameraIdsUnderTest[i]);
 
-                initSupportedVideoSize(mCameraIds[i]);
+                initSupportedVideoSize(mCameraIdsUnderTest[i]);
 
                 recordingSizeTestByCamera();
             } finally {
@@ -357,7 +360,7 @@
 
     @Test
     public void testAbandonedHighSpeedRequest() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing bad suface for createHighSpeedRequestList for camera " + id);
                 StaticMetadata staticInfo = mAllStaticInfo.get(id);
@@ -479,25 +482,25 @@
      */
     @Test
     public void testRecordingFramerateLowToHigh() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                Log.i(TAG, "Testing basic recording for camera " + mCameraIds[i]);
-                StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIds[i]);
+                Log.i(TAG, "Testing recording framerate low to high for camera " + mCameraIdsUnderTest[i]);
+                StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIdsUnderTest[i]);
                 if (!staticInfo.isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
                 if (staticInfo.isExternalCamera()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support CamcorderProfile, skipping");
                     continue;
                 }
                 // Re-use the MediaRecorder object for the same camera device.
                 mMediaRecorder = new MediaRecorder();
-                openDevice(mCameraIds[i]);
+                openDevice(mCameraIdsUnderTest[i]);
 
-                initSupportedVideoSize(mCameraIds[i]);
+                initSupportedVideoSize(mCameraIdsUnderTest[i]);
 
                 int minFpsProfileId = -1, minFps = 1000;
                 int maxFpsProfileId = -1, maxFps = 0;
@@ -534,23 +537,23 @@
      */
     @Test
     public void testVideoPreviewSurfaceSharing() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIds[i]);
+                StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIdsUnderTest[i]);
                 if (staticInfo.isHardwareLevelLegacy()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] + " is legacy, skipping");
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] + " is legacy, skipping");
                     continue;
                 }
                 if (!staticInfo.isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
                 // Re-use the MediaRecorder object for the same camera device.
                 mMediaRecorder = new MediaRecorder();
-                openDevice(mCameraIds[i]);
+                openDevice(mCameraIdsUnderTest[i]);
 
-                initSupportedVideoSize(mCameraIds[i]);
+                initSupportedVideoSize(mCameraIdsUnderTest[i]);
 
                 videoPreviewSurfaceSharingTestByCamera();
             } finally {
@@ -561,6 +564,128 @@
     }
 
     /**
+     * <p>
+     * Test recording with same recording surface and different preview surfaces.
+     * </p>
+     * <p>
+     * This test maintains persistent video surface while changing preview surface.
+     * This exercises format/dataspace override behavior of the camera device.
+     * </p>
+     */
+    @Test
+    public void testRecordingWithDifferentPreviewSizes() throws Exception {
+        if (!MediaUtils.checkCodecForDomain(true /* encoder */, "video")) {
+            return; // skipped
+        }
+        mPersistentSurface = MediaCodec.createPersistentInputSurface();
+        assertNotNull("Failed to create persistent input surface!", mPersistentSurface);
+
+        try {
+            doRecordingWithDifferentPreviewSizes();
+        } finally {
+            mPersistentSurface.release();
+            mPersistentSurface = null;
+        }
+    }
+
+    public void doRecordingWithDifferentPreviewSizes() throws Exception {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+            try {
+                Log.i(TAG, "Testing recording with different preview sizes for camera " +
+                        mCameraIdsUnderTest[i]);
+                StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIdsUnderTest[i]);
+                if (!staticInfo.isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
+                            " does not support color outputs, skipping");
+                    continue;
+                }
+                if (staticInfo.isExternalCamera()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
+                            " does not support CamcorderProfile, skipping");
+                    continue;
+                }
+                // Re-use the MediaRecorder object for the same camera device.
+                mMediaRecorder = new MediaRecorder();
+                openDevice(mCameraIdsUnderTest[i]);
+
+                initSupportedVideoSize(mCameraIdsUnderTest[i]);
+
+                Size maxPreviewSize = mOrderedPreviewSizes.get(0);
+                List<Range<Integer> > fpsRanges = Arrays.asList(
+                        mStaticInfo.getAeAvailableTargetFpsRangesChecked());
+                int cameraId = Integer.valueOf(mCamera.getId());
+                int maxVideoFrameRate = -1;
+                for (int profileId : mCamcorderProfileList) {
+                    if (!CamcorderProfile.hasProfile(cameraId, profileId)) {
+                        continue;
+                    }
+                    CamcorderProfile profile = CamcorderProfile.get(cameraId, profileId);
+
+                    Size videoSz = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
+                    Range<Integer> fpsRange = new Range(
+                            profile.videoFrameRate, profile.videoFrameRate);
+                    if (maxVideoFrameRate < profile.videoFrameRate) {
+                        maxVideoFrameRate = profile.videoFrameRate;
+                    }
+
+                    if (allowedUnsupported(cameraId, profileId)) {
+                        continue;
+                    }
+
+                    if (mStaticInfo.isHardwareLevelLegacy() &&
+                            (videoSz.getWidth() > maxPreviewSize.getWidth() ||
+                             videoSz.getHeight() > maxPreviewSize.getHeight())) {
+                        // Skip. Legacy mode can only do recording up to max preview size
+                        continue;
+                    }
+                    assertTrue("Video size " + videoSz.toString() + " for profile ID " + profileId +
+                                    " must be one of the camera device supported video size!",
+                                    mSupportedVideoSizes.contains(videoSz));
+                    assertTrue("Frame rate range " + fpsRange + " (for profile ID " + profileId +
+                            ") must be one of the camera device available FPS range!",
+                            fpsRanges.contains(fpsRange));
+
+                    // Configure preview and recording surfaces.
+                    mOutMediaFileName = mDebugFileNameBase + "/test_video_surface_reconfig.mp4";
+
+                    // prepare preview surface by using video size.
+                    List<Size> previewSizes = getPreviewSizesForVideo(videoSz,
+                            profile.videoFrameRate);
+                    if (previewSizes.size() <= 1) {
+                        continue;
+                    }
+
+                    // 1. Do video recording using largest compatbile preview sizes
+                    prepareRecordingWithProfile(profile);
+                    updatePreviewSurface(previewSizes.get(0));
+                    SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+                    startRecording(
+                            /* useMediaRecorder */true, resultListener,
+                            /*useVideoStab*/false, fpsRange, false);
+                    SystemClock.sleep(RECORDING_DURATION_MS);
+                    stopRecording(/* useMediaRecorder */true, /* useIntermediateSurface */false,
+                            /* stopStreaming */false);
+
+                    // 2. Reconfigure with the same recording surface, but switch to a smaller
+                    // preview size.
+                    prepareRecordingWithProfile(profile);
+                    updatePreviewSurface(previewSizes.get(1));
+                    SimpleCaptureCallback resultListener2 = new SimpleCaptureCallback();
+                    startRecording(
+                            /* useMediaRecorder */true, resultListener2,
+                            /*useVideoStab*/false, fpsRange, false);
+                    SystemClock.sleep(RECORDING_DURATION_MS);
+                    stopRecording(/* useMediaRecorder */true);
+                    break;
+                }
+            } finally {
+                closeDevice();
+                releaseRecorder();
+            }
+        }
+    }
+
+    /**
      * Test camera preview and video surface sharing for maximum supported size.
      */
     private void videoPreviewSurfaceSharingTestByCamera() throws Exception {
@@ -652,7 +777,7 @@
      * </p>
      */
     private void slowMotionRecording() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing slow motion recording for camera " + id);
                 StaticMetadata staticInfo = mAllStaticInfo.get(id);
@@ -727,7 +852,7 @@
     }
 
     private void constrainedHighSpeedRecording() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing constrained high speed recording for camera " + id);
 
@@ -1027,7 +1152,8 @@
             SystemClock.sleep(RECORDING_DURATION_MS);
 
             // Stop recording and preview
-            stopRecording(/* useMediaRecorder */true, useIntermediateSurface);
+            stopRecording(/* useMediaRecorder */true, useIntermediateSurface,
+                    /* stopCameraStreaming */false);
             // Convert number of frames camera produced into the duration in unit of ms.
             float frameDurationMs = 1000.0f / profile.videoFrameRate;
             float durationMs = 0.f;
@@ -1142,7 +1268,7 @@
      * Simple wrapper to wrap normal/burst video snapshot tests
      */
     private void videoSnapshotHelper(boolean burstTest) throws Exception {
-            for (String id : mCameraIds) {
+            for (String id : mCameraIdsUnderTest) {
                 try {
                     Log.i(TAG, "Testing video snapshot for camera " + id);
 
@@ -1478,15 +1604,14 @@
     }
 
     /**
-     * Update preview size with video size.
+     * Find compatible preview sizes for video size and framerate.
      *
      * <p>Preview size will be capped with max preview size.</p>
      *
      * @param videoSize The video size used for preview.
      * @param videoFrameRate The video frame rate
-     *
      */
-    private void updatePreviewSurfaceWithVideo(Size videoSize, int videoFrameRate) {
+    private List<Size> getPreviewSizesForVideo(Size videoSize, int videoFrameRate) {
         if (mOrderedPreviewSizes == null) {
             throw new IllegalStateException("supported preview size list is not initialized yet");
         }
@@ -1496,7 +1621,7 @@
         HashMap<Size, Long> minFrameDurationMap = mStaticInfo.
                 getAvailableMinFrameDurationsForFormatChecked(ImageFormat.PRIVATE);
         Size maxPreviewSize = mOrderedPreviewSizes.get(0);
-        Size previewSize = null;
+        ArrayList<Size> previewSizes = new ArrayList<>();
         if (videoSize.getWidth() > maxPreviewSize.getWidth() ||
                 videoSize.getHeight() > maxPreviewSize.getHeight()) {
             for (Size s : mOrderedPreviewSizes) {
@@ -1510,18 +1635,32 @@
                 if (frameDuration <= videoFrameDuration &&
                         s.getWidth() <= videoSize.getWidth() &&
                         s.getHeight() <= videoSize.getHeight()) {
-                    Log.w(TAG, "Overwrite preview size from " + videoSize.toString() +
-                            " to " + s.toString());
-                    previewSize = s;
-                    break;
-                    // If all preview size doesn't work then we fallback to video size
+                    Log.v(TAG, "Add preview size " + s.toString() + " for video size " +
+                            videoSize.toString());
+                    previewSizes.add(s);
                 }
             }
         }
-        if (previewSize == null) {
-            previewSize = videoSize;
+
+        if (previewSizes.isEmpty()) {
+            previewSizes.add(videoSize);
         }
-        updatePreviewSurface(previewSize);
+
+        return previewSizes;
+    }
+
+    /**
+     * Update preview size with video size.
+     *
+     * <p>Preview size will be capped with max preview size.</p>
+     *
+     * @param videoSize The video size used for preview.
+     * @param videoFrameRate The video frame rate
+     *
+     */
+    private void updatePreviewSurfaceWithVideo(Size videoSize, int videoFrameRate) {
+        List<Size> previewSizes = getPreviewSizesForVideo(videoSize, videoFrameRate);
+        updatePreviewSurface(previewSizes.get(0));
     }
 
     private void prepareRecordingWithProfile(CamcorderProfile profile) throws Exception {
@@ -1734,15 +1873,18 @@
     }
 
     private int stopRecording(boolean useMediaRecorder) throws Exception {
-        return stopRecording(useMediaRecorder, false);
+        return stopRecording(useMediaRecorder, /*useIntermediateSurface*/false,
+                /*stopStreaming*/true);
     }
 
     // Stop recording and return the estimated video duration in milliseconds.
-    private int stopRecording(boolean useMediaRecorder, boolean useIntermediateSurface)
-            throws Exception {
+    private int stopRecording(boolean useMediaRecorder, boolean useIntermediateSurface,
+            boolean stopStreaming) throws Exception {
         long stopRecordingTime = SystemClock.elapsedRealtime();
         if (useMediaRecorder) {
-            stopCameraStreaming();
+            if (stopStreaming) {
+                stopCameraStreaming();
+            }
             if (useIntermediateSurface) {
                 mIntermediateReader.setOnImageAvailableListener(null, null);
                 mQueuer.expectInvalidSurface();
diff --git a/tests/camera/src/android/hardware/camera2/cts/ReprocessCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
index bdf98f5..e59b161 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
@@ -43,11 +43,15 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
 import org.junit.Test;
 
 /**
  * <p>Tests for Reprocess API.</p>
  */
+
+@RunWith(Parameterized.class)
 public class ReprocessCaptureTest extends Camera2SurfaceViewTestCase  {
     private static final String TAG = "ReprocessCaptureTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -90,7 +94,7 @@
      */
     @Test
     public void testBasicYuvToYuvReprocessing() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             if (!isYuvReprocessSupported(id)) {
                 continue;
             }
@@ -105,7 +109,7 @@
      */
     @Test
     public void testBasicYuvToJpegReprocessing() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             if (!isYuvReprocessSupported(id)) {
                 continue;
             }
@@ -120,7 +124,7 @@
      */
     @Test
     public void testBasicYuvToHeicReprocessing() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             if (!isYuvReprocessSupported(id)) {
                 continue;
             }
@@ -138,7 +142,7 @@
      */
     @Test
     public void testBasicOpaqueToYuvReprocessing() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             if (!isOpaqueReprocessSupported(id)) {
                 continue;
             }
@@ -153,7 +157,7 @@
      */
     @Test
     public void testBasicOpaqueToJpegReprocessing() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             if (!isOpaqueReprocessSupported(id)) {
                 continue;
             }
@@ -168,7 +172,7 @@
      */
     @Test
     public void testBasicOpaqueToHeicReprocessing() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             if (!isOpaqueReprocessSupported(id)) {
                 continue;
             }
@@ -186,7 +190,7 @@
      */
     @Test(timeout=400*60*1000) // timeout = 400 mins for long running reprocessing tests
     public void testReprocessingSizeFormat() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
                 continue;
             }
@@ -208,7 +212,7 @@
      */
     @Test(timeout=400*60*1000) // timeout = 400 mins for long running reprocessing tests
     public void testReprocessingSizeFormatWithPreview() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
                 continue;
             }
@@ -229,7 +233,7 @@
      */
     @Test
     public void testRecreateReprocessingSessions() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
                 continue;
             }
@@ -267,7 +271,7 @@
      */
     @Test
     public void testCrossSessionCaptureException() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             // Test one supported input format -> JPEG
             int inputFormat;
             int reprocessOutputFormat = ImageFormat.JPEG;
@@ -333,11 +337,78 @@
     }
 
     /**
+     * Verify queued input images are cleared in new reprocessable capture session.
+     *
+     * This tests the case where an application receives onCaptureBufferLost() for an
+     * output stream, resulting in pending input buffers not having corresponding request.
+     *
+     * For subsequent new reprocessable capture session, ImageWriter.queueInputBuffer may become
+     * stuck due to stale buffers from previous session.
+     */
+    @Test
+    public void testQueueImageWithoutRequest() throws Exception {
+        final int MAX_IMAGES = 1;
+        final int ITERATIONS = MAX_IMAGES + 3;
+        for (String id : mCameraIdsUnderTest) {
+            // Test one supported input format -> JPEG
+            int inputFormat;
+            int reprocessOutputFormat = ImageFormat.JPEG;
+
+            if (isOpaqueReprocessSupported(id)) {
+                inputFormat = ImageFormat.PRIVATE;
+            } else if (isYuvReprocessSupported(id)) {
+                inputFormat = ImageFormat.YUV_420_888;
+            } else {
+                continue;
+            }
+
+            openDevice(id);
+
+            // Test the largest sizes
+            Size inputSize =
+                    getMaxSize(inputFormat, StaticMetadata.StreamDirection.Input);
+            Size reprocessOutputSize =
+                    getMaxSize(reprocessOutputFormat, StaticMetadata.StreamDirection.Output);
+
+            try {
+                if (VERBOSE) {
+                    Log.v(TAG, "testQueueImageWithoutRequest: cameraId: " + id +
+                            " inputSize: " + inputSize + " inputFormat: " + inputFormat +
+                            " reprocessOutputSize: " + reprocessOutputSize +
+                            " reprocessOutputFormat: " + reprocessOutputFormat);
+                }
+
+                setupImageReaders(inputSize, inputFormat, reprocessOutputSize,
+                        reprocessOutputFormat, MAX_IMAGES);
+
+                for (int i = 0; i < ITERATIONS; i++) {
+                    setupReprocessableSession(/*previewSurface*/null, /*numImageWriterImages*/1);
+
+                    TotalCaptureResult result = submitCaptureRequest(mFirstImageReader.getSurface(),
+                            /*inputResult*/null);
+                    Image image = mFirstImageReaderListener.getImage(CAPTURE_TIMEOUT_MS);
+
+                    // queue the image to image writer
+                    mImageWriter.queueInputImage(image);
+
+                    mInputSurface = null;
+                    mImageWriter.close();
+                    mImageWriter = null;
+                }
+            } finally {
+                closeReprossibleSession();
+                closeImageReaders();
+                closeDevice();
+            }
+        }
+    }
+
+    /**
      * Test burst reprocessing captures with and without preview.
      */
     @Test(timeout=400*60*1000) // timeout = 400 mins for long running reprocessing tests
     public void testBurstReprocessing() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
                 continue;
             }
@@ -361,7 +432,7 @@
      */
     @Test(timeout=400*60*1000) // timeout = 400 mins for long running reprocessing tests
     public void testMixedBurstReprocessing() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
                 continue;
             }
@@ -387,7 +458,7 @@
      */
     @Test
     public void testReprocessAbort() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
                 continue;
             }
@@ -417,7 +488,7 @@
      */
     @Test
     public void testReprocessTimestamps() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
                 continue;
             }
@@ -448,7 +519,7 @@
      */
     @Test
     public void testReprocessJpegExif() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
                 continue;
             }
@@ -479,7 +550,7 @@
 
     @Test
     public void testReprocessRequestKeys() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
                 continue;
             }
diff --git a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index d108600e..e2bbb9c 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -54,12 +54,18 @@
 import java.util.Map;
 import java.util.Set;
 
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
 import static junit.framework.Assert.assertTrue;
 import static org.mockito.Mockito.*;
 
 /**
  * Tests exercising edge cases in camera setup, configuration, and usage.
  */
+
+@RunWith(Parameterized.class)
 public class RobustnessTest extends Camera2AndroidTestCase {
     private static final String TAG = "RobustnessTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -79,8 +85,9 @@
      * this surface are expected have the dimensions of the closest possible buffer size in the
      * available stream configurations for a surface with this format.
      */
+    @Test
     public void testBadSurfaceDimensions() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing Camera " + id);
                 openDevice(id);
@@ -160,8 +167,9 @@
     /**
      * Test for making sure the mandatory stream combinations work as expected.
      */
+    @Test
     public void testMandatoryOutputCombinations() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             openDevice(id);
             MandatoryStreamCombination[] combinations =
                     mStaticInfo.getCharacteristics().get(
@@ -186,7 +194,7 @@
                     Set<String> physicalCameraIds =
                             mStaticInfo.getCharacteristics().getPhysicalCameraIds();
                     for (String physicalId : physicalCameraIds) {
-                        if (Arrays.asList(mCameraIds).contains(physicalId)) {
+                        if (Arrays.asList(mCameraIdsUnderTest).contains(physicalId)) {
                             // If physicalId is advertised in camera ID list, do not need to test
                             // its stream combination through logical camera.
                             continue;
@@ -480,8 +488,9 @@
      * Test for making sure the required reprocess input/output combinations for each hardware
      * level and capability work as expected.
      */
+    @Test
     public void testMandatoryReprocessConfigurations() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             openDevice(id);
             MandatoryStreamCombination[] combinations =
                     mStaticInfo.getCharacteristics().get(
@@ -716,9 +725,10 @@
         }
     }
 
+    @Test
     public void testBasicTriggerSequence() throws Exception {
 
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             Log.i(TAG, String.format("Testing Camera %s", id));
 
             try {
@@ -856,8 +866,9 @@
 
     }
 
+    @Test
     public void testSimultaneousTriggers() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             Log.i(TAG, String.format("Testing Camera %s", id));
 
             try {
@@ -958,8 +969,9 @@
         }
     }
 
+    @Test
     public void testAfThenAeTrigger() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             Log.i(TAG, String.format("Testing Camera %s", id));
 
             try {
@@ -1074,8 +1086,9 @@
         }
     }
 
+    @Test
     public void testAeThenAfTrigger() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             Log.i(TAG, String.format("Testing Camera %s", id));
 
             try {
@@ -1190,9 +1203,10 @@
         }
     }
 
+    @Test
     public void testAeAndAfCausality() throws Exception {
 
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             Log.i(TAG, String.format("Testing Camera %s", id));
 
             try {
@@ -1372,8 +1386,9 @@
 
     }
 
+    @Test
     public void testAbandonRepeatingRequestSurface() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             Log.i(TAG, String.format(
                     "Testing Camera %s for abandoning surface of a repeating request", id));
 
@@ -1441,8 +1456,9 @@
         }
     }
 
+    @Test
     public void testConfigureAbandonedSurface() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             Log.i(TAG, String.format(
                     "Testing Camera %s for configuring abandoned surface", id));
 
@@ -1498,10 +1514,11 @@
         }
     }
 
+    @Test
     public void testAfSceneChange() throws Exception {
         final int NUM_FRAMES_VERIFIED = 3;
 
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             Log.i(TAG, String.format("Testing Camera %s for AF scene change", id));
 
             StaticMetadata staticInfo =
@@ -1548,10 +1565,11 @@
         }
     }
 
+    @Test
     public void testOisDataMode() throws Exception {
         final int NUM_FRAMES_VERIFIED = 3;
 
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             Log.i(TAG, String.format("Testing Camera %s for OIS mode", id));
 
             StaticMetadata staticInfo =
diff --git a/tests/camera/src/android/hardware/camera2/cts/StaticMetadataTest.java b/tests/camera/src/android/hardware/camera2/cts/StaticMetadataTest.java
index b56fcbf..1ffa501 100644
--- a/tests/camera/src/android/hardware/camera2/cts/StaticMetadataTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/StaticMetadataTest.java
@@ -41,6 +41,13 @@
 import java.util.List;
 import java.util.Set;
 
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
 /**
  * <p>
  * This class covers the {@link CameraCharacteristics} tests that are not
@@ -50,6 +57,8 @@
  * Note that most of the tests in this class don't require camera open.
  * </p>
  */
+
+@RunWith(Parameterized.class)
 public class StaticMetadataTest extends Camera2AndroidTestCase {
     private static final String TAG = "StaticMetadataTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -59,6 +68,7 @@
     /**
      * Test the available capability for different hardware support level devices.
      */
+    @Test
     public void testHwSupportedLevel() throws Exception {
         Key<StreamConfigurationMap> key =
                 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP;
@@ -135,6 +145,7 @@
     /**
      * Test max number of output stream reported by device
      */
+    @Test
     public void testMaxNumOutputStreams() throws Exception {
         for (String id : mAllCameraIds) {
             initStaticMetadata(id);
@@ -163,6 +174,7 @@
     /**
      * Test advertised capability does match available keys and vice versa
      */
+    @Test
     public void testCapabilities() throws Exception {
         for (String id : mAllCameraIds) {
             initStaticMetadata(id);
@@ -533,6 +545,7 @@
     /**
      * Test lens facing.
      */
+    @Test
     public void testLensFacing() throws Exception {
         for (String id : mAllCameraIds) {
             initStaticMetadata(id);
diff --git a/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
index e8d2812..25fde0e 100644
--- a/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
@@ -57,8 +57,11 @@
 
 import junit.framework.Assert;
 
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
 import org.junit.Test;
 
+@RunWith(Parameterized.class)
 public class StillCaptureTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "StillCaptureTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -91,15 +94,15 @@
      */
     @Test
     public void testJpegExif() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                Log.i(TAG, "Testing JPEG exif for Camera " + mCameraIds[i]);
-                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                Log.i(TAG, "Testing JPEG exif for Camera " + mCameraIdsUnderTest[i]);
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
-                openDevice(mCameraIds[i]);
+                openDevice(mCameraIdsUnderTest[i]);
                 Size maxJpegSize = mOrderedStillSizes.get(0);
                 stillExifTestByCamera(ImageFormat.JPEG, maxJpegSize);
             } finally {
@@ -114,25 +117,25 @@
      */
     @Test
     public void testHeicExif() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                Log.i(TAG, "Testing HEIC exif for Camera " + mCameraIds[i]);
-                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                Log.i(TAG, "Testing HEIC exif for Camera " + mCameraIdsUnderTest[i]);
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
-                if (!mAllStaticInfo.get(mCameraIds[i]).isHeicSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isHeicSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support HEIC, skipping");
                     continue;
                 }
 
-                openDevice(mCameraIds[i]);
+                openDevice(mCameraIdsUnderTest[i]);
 
                 // Test maximum Heic size capture
                 List<Size> orderedHeicSizes = CameraTestUtils.getSupportedHeicSizes(
-                        mCameraIds[i], mCameraManager, null/*bound*/);
+                        mCameraIdsUnderTest[i], mCameraManager, null/*bound*/);
                 Size maxHeicSize = orderedHeicSizes.get(0);
                 stillExifTestByCamera(ImageFormat.HEIC, maxHeicSize);
 
@@ -152,25 +155,25 @@
      */
     @Test
     public void testDynamicDepthCapture() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                Log.i(TAG, "Testing dynamic depth for Camera " + mCameraIds[i]);
-                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                Log.i(TAG, "Testing dynamic depth for Camera " + mCameraIdsUnderTest[i]);
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
-                if (!mAllStaticInfo.get(mCameraIds[i]).isDepthJpegSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isDepthJpegSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support dynamic depth, skipping");
                     continue;
                 }
 
-                openDevice(mCameraIds[i]);
+                openDevice(mCameraIdsUnderTest[i]);
 
                 // Check the maximum supported size.
                 List<Size> orderedDepthJpegSizes = CameraTestUtils.getSortedSizesForFormat(
-                        mCameraIds[i], mCameraManager, ImageFormat.DEPTH_JPEG, null/*bound*/);
+                        mCameraIdsUnderTest[i], mCameraManager, ImageFormat.DEPTH_JPEG, null/*bound*/);
                 Size maxDepthJpegSize = orderedDepthJpegSizes.get(0);
                 stillDynamicDepthTestByCamera(ImageFormat.DEPTH_JPEG, maxDepthJpegSize);
             } finally {
@@ -192,7 +195,7 @@
      */
     @Test
     public void testTakePicture() throws Exception{
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing basic take picture for Camera " + id);
                 if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
@@ -220,7 +223,7 @@
      */
     @Test
     public void testTakePictureZsl() throws Exception{
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing basic ZSL take picture for Camera " + id);
                 if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
@@ -246,18 +249,18 @@
      */
     @Test
     public void testBasicRawCapture()  throws Exception {
-       for (int i = 0; i < mCameraIds.length; i++) {
+       for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
            try {
-               Log.i(TAG, "Testing raw capture for Camera " + mCameraIds[i]);
+               Log.i(TAG, "Testing raw capture for Camera " + mCameraIdsUnderTest[i]);
 
-               if (!mAllStaticInfo.get(mCameraIds[i]).isCapabilitySupported(
+               if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isCapabilitySupported(
                        CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
-                   Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
+                   Log.i(TAG, "RAW capability is not supported in camera " + mCameraIdsUnderTest[i] +
                            ". Skip the test.");
                    continue;
                }
 
-               openDevice(mCameraIds[i]);
+               openDevice(mCameraIdsUnderTest[i]);
                rawCaptureTestByCamera(/*stillRequest*/null);
            } finally {
                closeDevice();
@@ -271,17 +274,17 @@
      */
     @Test
     public void testBasicRawZslCapture()  throws Exception {
-       for (int i = 0; i < mCameraIds.length; i++) {
+       for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
            try {
-               Log.i(TAG, "Testing raw ZSL capture for Camera " + mCameraIds[i]);
+               Log.i(TAG, "Testing raw ZSL capture for Camera " + mCameraIdsUnderTest[i]);
 
-               if (!mAllStaticInfo.get(mCameraIds[i]).isCapabilitySupported(
+               if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isCapabilitySupported(
                        CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
-                   Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
+                   Log.i(TAG, "RAW capability is not supported in camera " + mCameraIdsUnderTest[i] +
                            ". Skip the test.");
                    continue;
                }
-               openDevice(mCameraIds[i]);
+               openDevice(mCameraIdsUnderTest[i]);
                CaptureRequest.Builder stillRequest =
                        mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
                stillRequest.set(CaptureRequest.CONTROL_ENABLE_ZSL, true);
@@ -304,17 +307,17 @@
      */
     @Test
     public void testFullRawCapture() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                Log.i(TAG, "Testing raw+JPEG capture for Camera " + mCameraIds[i]);
-                if (!mAllStaticInfo.get(mCameraIds[i]).isCapabilitySupported(
+                Log.i(TAG, "Testing raw+JPEG capture for Camera " + mCameraIdsUnderTest[i]);
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isCapabilitySupported(
                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
-                    Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
+                    Log.i(TAG, "RAW capability is not supported in camera " + mCameraIdsUnderTest[i] +
                             ". Skip the test.");
                     continue;
                 }
 
-                openDevice(mCameraIds[i]);
+                openDevice(mCameraIdsUnderTest[i]);
                 fullRawCaptureTestByCamera(/*stillRequest*/null);
             } finally {
                 closeDevice();
@@ -333,16 +336,16 @@
      */
     @Test
     public void testFullRawZSLCapture() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                Log.i(TAG, "Testing raw+JPEG ZSL capture for Camera " + mCameraIds[i]);
-                if (!mAllStaticInfo.get(mCameraIds[i]).isCapabilitySupported(
+                Log.i(TAG, "Testing raw+JPEG ZSL capture for Camera " + mCameraIdsUnderTest[i]);
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isCapabilitySupported(
                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
-                    Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
+                    Log.i(TAG, "RAW capability is not supported in camera " + mCameraIdsUnderTest[i] +
                             ". Skip the test.");
                     continue;
                 }
-                openDevice(mCameraIds[i]);
+                openDevice(mCameraIdsUnderTest[i]);
                 CaptureRequest.Builder stillRequest =
                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
                 stillRequest.set(CaptureRequest.CONTROL_ENABLE_ZSL, true);
@@ -364,7 +367,7 @@
      */
     @Test
     public void testTouchForFocus() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing touch for focus for Camera " + id);
                 StaticMetadata staticInfo = mAllStaticInfo.get(id);
@@ -395,7 +398,7 @@
      */
     @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
     public void testStillPreviewCombination() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing Still preview capture combination for Camera " + id);
                 if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
@@ -423,7 +426,7 @@
      */
     @Test
     public void testAeCompensation() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing AE compensation for Camera " + id);
 
@@ -450,7 +453,7 @@
      */
     @Test
     public void testAeRegions() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing AE regions for Camera " + id);
                 openDevice(id);
@@ -476,7 +479,7 @@
      */
     @Test
     public void testAwbRegions() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing AE regions for Camera " + id);
                 openDevice(id);
@@ -502,7 +505,7 @@
      */
     @Test
     public void testAfRegions() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing AF regions for Camera " + id);
                 openDevice(id);
@@ -528,7 +531,7 @@
      */
     @Test
     public void testPreviewPersistence() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing preview persistence for Camera " + id);
                 if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
@@ -546,7 +549,7 @@
 
     @Test
     public void testAePrecaptureTriggerCancelJpegCapture() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing AE precapture cancel for jpeg capture for Camera " + id);
 
@@ -581,7 +584,7 @@
      */
     @Test
     public void testAllocateBitmap() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 Log.i(TAG, "Testing bitmap allocations for Camera " + id);
                 if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
@@ -605,7 +608,7 @@
      */
     @Test
     public void testFocalLengths() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 StaticMetadata staticInfo = mAllStaticInfo.get(id);
                 if (staticInfo.isHardwareLevelLegacy()) {
diff --git a/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java b/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
index 3d84d38..1f2e93e 100644
--- a/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
@@ -52,11 +52,15 @@
 import java.util.Arrays;
 import java.util.List;
 
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
 import org.junit.Test;
 
 /**
  * CameraDevice preview test by using SurfaceView.
  */
+
+@RunWith(Parameterized.class)
 public class SurfaceViewPreviewTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "SurfaceViewPreviewTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -86,15 +90,15 @@
      */
     @Test
     public void testCameraPreview() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                Log.i(TAG, "Testing preview for Camera " + mCameraIds[i]);
-                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                Log.i(TAG, "Testing preview for Camera " + mCameraIdsUnderTest[i]);
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
-                openDevice(mCameraIds[i]);
+                openDevice(mCameraIdsUnderTest[i]);
                 previewTestByCamera();
             } finally {
                 closeDevice();
@@ -111,15 +115,15 @@
      */
     @Test
     public void testBasicTestPatternPreview() throws Exception{
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                Log.i(TAG, "Testing preview for Camera " + mCameraIds[i]);
-                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                Log.i(TAG, "Testing preview for Camera " + mCameraIdsUnderTest[i]);
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
-                openDevice(mCameraIds[i]);
+                openDevice(mCameraIdsUnderTest[i]);
                 previewTestPatternTestByCamera();
             } finally {
                 closeDevice();
@@ -133,7 +137,7 @@
      */
     @Test
     public void testPreviewFpsRange() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
@@ -159,7 +163,7 @@
      */
     @Test
     public void testSurfaceSet() throws Exception {
-        for (String id : mCameraIds) {
+        for (String id : mCameraIdsUnderTest) {
             try {
                 if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
@@ -184,15 +188,15 @@
      */
     @Test
     public void testPreparePerformance() throws Throwable {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
-                openDevice(mCameraIds[i]);
-                preparePerformanceTestByCamera(mCameraIds[i]);
+                openDevice(mCameraIdsUnderTest[i]);
+                preparePerformanceTestByCamera(mCameraIdsUnderTest[i]);
             }
             finally {
                 closeDevice();
@@ -344,15 +348,15 @@
      */
     @Test
     public void testSurfaceEquality() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
-                openDevice(mCameraIds[i]);
-                surfaceEqualityTestByCamera(mCameraIds[i]);
+                openDevice(mCameraIdsUnderTest[i]);
+                surfaceEqualityTestByCamera(mCameraIdsUnderTest[i]);
             }
             finally {
                 closeDevice();
@@ -435,21 +439,21 @@
      */
     @Test
     public void testDeferredSurfaces() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
-                StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIds[i]);
+                StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIdsUnderTest[i]);
                 if (staticInfo.isHardwareLevelLegacy()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] + " is legacy, skipping");
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] + " is legacy, skipping");
                     continue;
                 }
                 if (!staticInfo.isColorOutputSupported()) {
-                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
 
-                openDevice(mCameraIds[i]);
-                testDeferredSurfacesByCamera(mCameraIds[i]);
+                openDevice(mCameraIdsUnderTest[i]);
+                testDeferredSurfacesByCamera(mCameraIdsUnderTest[i]);
             }
             finally {
                 closeDevice();
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidBasicTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidBasicTestCase.java
new file mode 100644
index 0000000..c240065
--- /dev/null
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidBasicTestCase.java
@@ -0,0 +1,631 @@
+/*
+ * 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.hardware.camera2.cts.testcases;
+
+import static android.hardware.camera2.cts.CameraTestUtils.*;
+import static com.android.ex.camera2.blocking.BlockingStateCallback.*;
+
+import android.content.Context;
+import android.graphics.ImageFormat;
+import android.graphics.Rect;
+
+import android.hardware.camera2.cts.CameraTestUtils;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.SessionConfiguration;
+import android.util.Size;
+import android.hardware.camera2.cts.helpers.CameraErrorCollector;
+import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
+import android.media.Image;
+import android.media.Image.Plane;
+import android.media.ImageReader;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import android.view.Surface;
+import android.view.WindowManager;
+
+import com.android.ex.camera2.blocking.BlockingSessionCallback;
+import com.android.ex.camera2.blocking.BlockingStateCallback;
+
+import java.io.File;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+public class Camera2AndroidBasicTestCase extends AndroidTestCase {
+    private static final String TAG = "Camera2AndroidBasicTestCase";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    // Default capture size: VGA size is required by CDD.
+    protected static final Size DEFAULT_CAPTURE_SIZE = new Size(640, 480);
+    protected static final int CAPTURE_WAIT_TIMEOUT_MS = 5000;
+
+    protected CameraManager mCameraManager;
+    protected CameraDevice mCamera;
+    protected CameraCaptureSession mCameraSession;
+    protected BlockingSessionCallback mCameraSessionListener;
+    protected BlockingStateCallback mCameraListener;
+    protected String[] mCameraIdsUnderTest;
+    // include both standalone camera IDs and "hidden" physical camera IDs
+    protected String[] mAllCameraIds;
+    protected HashMap<String, StaticMetadata> mAllStaticInfo;
+    protected ImageReader mReader;
+    protected Surface mReaderSurface;
+    protected Handler mHandler;
+    protected HandlerThread mHandlerThread;
+    protected StaticMetadata mStaticInfo;
+    protected CameraErrorCollector mCollector;
+    protected List<Size> mOrderedPreviewSizes; // In descending order.
+    protected List<Size> mOrderedVideoSizes; // In descending order.
+    protected List<Size> mOrderedStillSizes; // In descending order.
+    protected String mDebugFileNameBase;
+
+    protected WindowManager mWindowManager;
+
+    @Override
+    public void setContext(Context context) {
+        super.setContext(context);
+        mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
+        assertNotNull("Can't connect to camera manager!", mCameraManager);
+        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+    }
+
+    /**
+     * Set up the camera2 test case required environments, including CameraManager,
+     * HandlerThread, Camera IDs, and CameraStateCallback etc.
+     */
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        /**
+         * Workaround for mockito and JB-MR2 incompatibility
+         *
+         * Avoid java.lang.IllegalArgumentException: dexcache == null
+         * https://code.google.com/p/dexmaker/issues/detail?id=2
+         */
+        System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
+
+        mCameraIdsUnderTest = mCameraManager.getCameraIdListNoLazy();
+        assertNotNull("Camera ids shouldn't be null", mCameraIdsUnderTest);
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+        mCameraListener = new BlockingStateCallback();
+        mCollector = new CameraErrorCollector();
+
+        File filesDir = mContext.getPackageManager().isInstantApp()
+                ? mContext.getFilesDir()
+                : mContext.getExternalFilesDir(null);
+
+        mDebugFileNameBase = filesDir.getPath();
+
+        mAllStaticInfo = new HashMap<String, StaticMetadata>();
+        List<String> hiddenPhysicalIds = new ArrayList<>();
+        for (String cameraId : mCameraIdsUnderTest) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(cameraId);
+            StaticMetadata staticMetadata = new StaticMetadata(props,
+                    CheckLevel.ASSERT, /*collector*/null);
+            mAllStaticInfo.put(cameraId, staticMetadata);
+
+            for (String physicalId : props.getPhysicalCameraIds()) {
+                if (!Arrays.asList(mCameraIdsUnderTest).contains(physicalId) &&
+                        !hiddenPhysicalIds.contains(physicalId)) {
+                    hiddenPhysicalIds.add(physicalId);
+                    props = mCameraManager.getCameraCharacteristics(physicalId);
+                    staticMetadata = new StaticMetadata(
+                            mCameraManager.getCameraCharacteristics(physicalId),
+                            CheckLevel.ASSERT, /*collector*/null);
+                    mAllStaticInfo.put(physicalId, staticMetadata);
+                }
+            }
+        }
+        mAllCameraIds = new String[mCameraIdsUnderTest.length + hiddenPhysicalIds.size()];
+        System.arraycopy(mCameraIdsUnderTest, 0, mAllCameraIds, 0, mCameraIdsUnderTest.length);
+        for (int i = 0; i < hiddenPhysicalIds.size(); i++) {
+            mAllCameraIds[mCameraIdsUnderTest.length + i] = hiddenPhysicalIds.get(i);
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        String[] cameraIdsPostTest =
+                mCameraManager.getCameraIdListNoLazy();
+        assertNotNull("Camera ids shouldn't be null", cameraIdsPostTest);
+        Log.i(TAG, "Camera ids in setup:" + Arrays.toString(mCameraIdsUnderTest));
+        Log.i(TAG, "Camera ids in tearDown:" + Arrays.toString(cameraIdsPostTest));
+        assertTrue(
+                "Number of cameras changed from " + mCameraIdsUnderTest.length + " to " +
+                cameraIdsPostTest.length,
+                mCameraIdsUnderTest.length == cameraIdsPostTest.length);
+        mHandlerThread.quitSafely();
+        mHandler = null;
+        closeDefaultImageReader();
+
+        try {
+            mCollector.verify();
+        } catch (Throwable e) {
+            // When new Exception(e) is used, exception info will be printed twice.
+            throw new Exception(e.getMessage());
+        } finally {
+            super.tearDown();
+        }
+    }
+
+    /**
+     * Start capture with given {@link #CaptureRequest}.
+     *
+     * @param request The {@link #CaptureRequest} to be captured.
+     * @param repeating If the capture is single capture or repeating.
+     * @param listener The {@link #CaptureCallback} camera device used to notify callbacks.
+     * @param handler The handler camera device used to post callbacks.
+     */
+    protected void startCapture(CaptureRequest request, boolean repeating,
+            CaptureCallback listener, Handler handler) throws Exception {
+        if (VERBOSE) Log.v(TAG, "Starting capture from device");
+
+        if (repeating) {
+            mCameraSession.setRepeatingRequest(request, listener, handler);
+        } else {
+            mCameraSession.capture(request, listener, handler);
+        }
+    }
+
+    /**
+     * Stop the current active capture.
+     *
+     * @param fast When it is true, {@link CameraDevice#flush} is called, the stop capture
+     * could be faster.
+     */
+    protected void stopCapture(boolean fast) throws Exception {
+        if (VERBOSE) Log.v(TAG, "Stopping capture");
+
+        if (fast) {
+            /**
+             * Flush is useful for canceling long exposure single capture, it also could help
+             * to make the streaming capture stop sooner.
+             */
+            mCameraSession.abortCaptures();
+            mCameraSessionListener.getStateWaiter().
+                    waitForState(BlockingSessionCallback.SESSION_READY, CAMERA_IDLE_TIMEOUT_MS);
+        } else {
+            mCameraSession.close();
+            mCameraSessionListener.getStateWaiter().
+                    waitForState(BlockingSessionCallback.SESSION_CLOSED, CAMERA_IDLE_TIMEOUT_MS);
+        }
+    }
+
+    /**
+     * Open a {@link #CameraDevice camera device} and get the StaticMetadata for a given camera id.
+     * The default mCameraListener is used to wait for states.
+     *
+     * @param cameraId The id of the camera device to be opened.
+     */
+    protected void openDevice(String cameraId) throws Exception {
+        openDevice(cameraId, mCameraListener);
+    }
+
+    /**
+     * Open a {@link #CameraDevice} and get the StaticMetadata for a given camera id and listener.
+     *
+     * @param cameraId The id of the camera device to be opened.
+     * @param listener The {@link #BlockingStateCallback} used to wait for states.
+     */
+    protected void openDevice(String cameraId, BlockingStateCallback listener) throws Exception {
+        mCamera = CameraTestUtils.openCamera(
+                mCameraManager, cameraId, listener, mHandler);
+        mCollector.setCameraId(cameraId);
+        mStaticInfo = mAllStaticInfo.get(cameraId);
+        if (mStaticInfo.isColorOutputSupported()) {
+            mOrderedPreviewSizes = getSupportedPreviewSizes(
+                    cameraId, mCameraManager,
+                    getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
+            mOrderedVideoSizes = getSupportedVideoSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
+            mOrderedStillSizes = getSupportedStillSizes(cameraId, mCameraManager, null);
+        }
+
+        if (VERBOSE) {
+            Log.v(TAG, "Camera " + cameraId + " is opened");
+        }
+    }
+
+    /**
+     * Create a {@link #CameraCaptureSession} using the currently open camera.
+     *
+     * @param outputSurfaces The set of output surfaces to configure for this session
+     */
+    protected void createSession(List<Surface> outputSurfaces) throws Exception {
+        mCameraSessionListener = new BlockingSessionCallback();
+        mCameraSession = CameraTestUtils.configureCameraSession(mCamera, outputSurfaces,
+                mCameraSessionListener, mHandler);
+    }
+
+    /**
+     * Create a {@link #CameraCaptureSession} using the currently open camera with
+     * OutputConfigurations.
+     *
+     * @param outputSurfaces The set of output surfaces to configure for this session
+     */
+    protected void createSessionByConfigs(List<OutputConfiguration> outputConfigs) throws Exception {
+        mCameraSessionListener = new BlockingSessionCallback();
+        mCameraSession = CameraTestUtils.configureCameraSessionWithConfig(mCamera, outputConfigs,
+                mCameraSessionListener, mHandler);
+    }
+
+    /**
+     * Close a {@link #CameraDevice camera device} and clear the associated StaticInfo field for a
+     * given camera id. The default mCameraListener is used to wait for states.
+     * <p>
+     * This function must be used along with the {@link #openDevice} for the
+     * same camera id.
+     * </p>
+     *
+     * @param cameraId The id of the {@link #CameraDevice camera device} to be closed.
+     */
+    protected void closeDevice(String cameraId) {
+        closeDevice(cameraId, mCameraListener);
+    }
+
+    /**
+     * Close a {@link #CameraDevice camera device} and clear the associated StaticInfo field for a
+     * given camera id and listener.
+     * <p>
+     * This function must be used along with the {@link #openDevice} for the
+     * same camera id.
+     * </p>
+     *
+     * @param cameraId The id of the camera device to be closed.
+     * @param listener The BlockingStateCallback used to wait for states.
+     */
+    protected void closeDevice(String cameraId, BlockingStateCallback listener) {
+        if (mCamera != null) {
+            if (!cameraId.equals(mCamera.getId())) {
+                throw new IllegalStateException("Try to close a device that is not opened yet");
+            }
+            mCamera.close();
+            listener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
+            mCamera = null;
+            mCameraSession = null;
+            mCameraSessionListener = null;
+            mStaticInfo = null;
+            mOrderedPreviewSizes = null;
+            mOrderedVideoSizes = null;
+            mOrderedStillSizes = null;
+
+            if (VERBOSE) {
+                Log.v(TAG, "Camera " + cameraId + " is closed");
+            }
+        }
+    }
+
+    /**
+     * Create an {@link ImageReader} object and get the surface.
+     * <p>
+     * This function creates {@link ImageReader} object and surface, then assign
+     * to the default {@link mReader} and {@link mReaderSurface}. It closes the
+     * current default active {@link ImageReader} if it exists.
+     * </p>
+     *
+     * @param size The size of this ImageReader to be created.
+     * @param format The format of this ImageReader to be created
+     * @param maxNumImages The max number of images that can be acquired
+     *            simultaneously.
+     * @param listener The listener used by this ImageReader to notify
+     *            callbacks.
+     */
+    protected void createDefaultImageReader(Size size, int format, int maxNumImages,
+            ImageReader.OnImageAvailableListener listener) throws Exception {
+        closeDefaultImageReader();
+
+        mReader = createImageReader(size, format, maxNumImages, listener);
+        mReaderSurface = mReader.getSurface();
+        if (VERBOSE) Log.v(TAG, "Created ImageReader size " + size.toString());
+    }
+
+    /**
+     * Create an {@link ImageReader} object and get the surface.
+     * <p>
+     * This function creates {@link ImageReader} object and surface, then assign
+     * to the default {@link mReader} and {@link mReaderSurface}. It closes the
+     * current default active {@link ImageReader} if it exists.
+     * </p>
+     *
+     * @param size The size of this ImageReader to be created.
+     * @param format The format of this ImageReader to be created
+     * @param maxNumImages The max number of images that can be acquired
+     *            simultaneously.
+     * @param usage The usage flag of the ImageReader
+     * @param listener The listener used by this ImageReader to notify
+     *            callbacks.
+     */
+    protected void createDefaultImageReader(Size size, int format, int maxNumImages, long usage,
+            ImageReader.OnImageAvailableListener listener) throws Exception {
+        closeDefaultImageReader();
+
+        mReader = createImageReader(size, format, maxNumImages, usage, listener);
+        mReaderSurface = mReader.getSurface();
+        if (VERBOSE) Log.v(TAG, "Created ImageReader size " + size.toString());
+    }
+
+    /**
+     * Create an {@link ImageReader} object.
+     *
+     * <p>This function creates image reader object for given format, maxImages, and size.</p>
+     *
+     * @param size The size of this ImageReader to be created.
+     * @param format The format of this ImageReader to be created
+     * @param maxNumImages The max number of images that can be acquired simultaneously.
+     * @param listener The listener used by this ImageReader to notify callbacks.
+     */
+
+    protected ImageReader createImageReader(Size size, int format, int maxNumImages,
+            ImageReader.OnImageAvailableListener listener) throws Exception {
+
+        ImageReader reader = null;
+        reader = ImageReader.newInstance(size.getWidth(), size.getHeight(),
+                format, maxNumImages);
+
+        reader.setOnImageAvailableListener(listener, mHandler);
+        if (VERBOSE) Log.v(TAG, "Created ImageReader size " + size.toString());
+        return reader;
+    }
+
+    /**
+     * Create an {@link ImageReader} object.
+     *
+     * <p>This function creates image reader object for given format, maxImages, usage and size.</p>
+     *
+     * @param size The size of this ImageReader to be created.
+     * @param format The format of this ImageReader to be created
+     * @param maxNumImages The max number of images that can be acquired simultaneously.
+     * @param usage The usage flag of the ImageReader
+     * @param listener The listener used by this ImageReader to notify callbacks.
+     */
+
+    protected ImageReader createImageReader(Size size, int format, int maxNumImages, long usage,
+            ImageReader.OnImageAvailableListener listener) throws Exception {
+        ImageReader reader = null;
+        reader = ImageReader.newInstance(size.getWidth(), size.getHeight(),
+                format, maxNumImages, usage);
+
+        reader.setOnImageAvailableListener(listener, mHandler);
+        if (VERBOSE) Log.v(TAG, "Created ImageReader size " + size.toString());
+        return reader;
+    }
+
+    /**
+     * Close the pending images then close current default {@link ImageReader} object.
+     */
+    protected void closeDefaultImageReader() {
+        closeImageReader(mReader);
+        mReader = null;
+        mReaderSurface = null;
+    }
+
+    /**
+     * Close an image reader instance.
+     *
+     * @param reader
+     */
+    protected void closeImageReader(ImageReader reader) {
+        if (reader != null) {
+            try {
+                // Close all possible pending images first.
+                Image image = reader.acquireLatestImage();
+                if (image != null) {
+                    image.close();
+                }
+            } finally {
+                reader.close();
+                reader = null;
+            }
+        }
+    }
+
+    protected void checkImageReaderSessionConfiguration(String msg) throws Exception {
+        List<OutputConfiguration> outputConfigs = new ArrayList<OutputConfiguration>();
+        outputConfigs.add(new OutputConfiguration(mReaderSurface));
+
+        checkSessionConfigurationSupported(mCamera, mHandler, outputConfigs, /*inputConfig*/ null,
+                SessionConfiguration.SESSION_REGULAR, /*expectedResult*/ true, msg);
+    }
+
+    protected CaptureRequest prepareCaptureRequest() throws Exception {
+        return prepareCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+    }
+
+    protected CaptureRequest prepareCaptureRequest(int template) throws Exception {
+        List<Surface> outputSurfaces = new ArrayList<Surface>();
+        Surface surface = mReader.getSurface();
+        assertNotNull("Fail to get surface from ImageReader", surface);
+        outputSurfaces.add(surface);
+        return prepareCaptureRequestForSurfaces(outputSurfaces, template)
+                .build();
+    }
+
+    protected CaptureRequest.Builder prepareCaptureRequestForSurfaces(List<Surface> surfaces,
+            int template)
+            throws Exception {
+        createSession(surfaces);
+
+        CaptureRequest.Builder captureBuilder =
+                mCamera.createCaptureRequest(template);
+        assertNotNull("Fail to get captureRequest", captureBuilder);
+        for (Surface surface : surfaces) {
+            captureBuilder.addTarget(surface);
+        }
+
+        return captureBuilder;
+    }
+
+    protected CaptureRequest.Builder prepareCaptureRequestForConfigs(
+            List<OutputConfiguration> outputConfigs, int template) throws Exception {
+        createSessionByConfigs(outputConfigs);
+
+        CaptureRequest.Builder captureBuilder =
+                mCamera.createCaptureRequest(template);
+        assertNotNull("Fail to get captureRequest", captureBuilder);
+        for (OutputConfiguration config : outputConfigs) {
+            for (Surface s : config.getSurfaces()) {
+                captureBuilder.addTarget(s);
+            }
+        }
+
+        return captureBuilder;
+    }
+
+    /**
+     * Test the invalid Image access: accessing a closed image must result in
+     * {@link IllegalStateException}.
+     *
+     * @param closedImage The closed image.
+     * @param closedBuffer The ByteBuffer from a closed Image. buffer invalid
+     *            access will be skipped if it is null.
+     */
+    protected void imageInvalidAccessTestAfterClose(Image closedImage,
+            Plane closedPlane, ByteBuffer closedBuffer) {
+        if (closedImage == null) {
+            throw new IllegalArgumentException(" closedImage must be non-null");
+        }
+        if (closedBuffer != null && !closedBuffer.isDirect()) {
+            throw new IllegalArgumentException("The input ByteBuffer should be direct ByteBuffer");
+        }
+
+        if (closedPlane != null) {
+            // Plane#getBuffer test
+            try {
+                closedPlane.getBuffer(); // An ISE should be thrown here.
+                fail("Image should throw IllegalStateException when calling getBuffer"
+                        + " after the image is closed");
+            } catch (IllegalStateException e) {
+                // Expected.
+            }
+
+            // Plane#getPixelStride test
+            try {
+                closedPlane.getPixelStride(); // An ISE should be thrown here.
+                fail("Image should throw IllegalStateException when calling getPixelStride"
+                        + " after the image is closed");
+            } catch (IllegalStateException e) {
+                // Expected.
+            }
+
+            // Plane#getRowStride test
+            try {
+                closedPlane.getRowStride(); // An ISE should be thrown here.
+                fail("Image should throw IllegalStateException when calling getRowStride"
+                        + " after the image is closed");
+            } catch (IllegalStateException e) {
+                // Expected.
+            }
+        }
+
+        // ByteBuffer access test
+        if (closedBuffer != null) {
+            try {
+                closedBuffer.get(); // An ISE should be thrown here.
+                fail("Image should throw IllegalStateException when accessing a byte buffer"
+                        + " after the image is closed");
+            } catch (IllegalStateException e) {
+                // Expected.
+            }
+        }
+
+        // Image#getFormat test
+        try {
+            closedImage.getFormat();
+            fail("Image should throw IllegalStateException when calling getFormat"
+                    + " after the image is closed");
+        } catch (IllegalStateException e) {
+            // Expected.
+        }
+
+        // Image#getWidth test
+        try {
+            closedImage.getWidth();
+            fail("Image should throw IllegalStateException when calling getWidth"
+                    + " after the image is closed");
+        } catch (IllegalStateException e) {
+            // Expected.
+        }
+
+        // Image#getHeight test
+        try {
+            closedImage.getHeight();
+            fail("Image should throw IllegalStateException when calling getHeight"
+                    + " after the image is closed");
+        } catch (IllegalStateException e) {
+            // Expected.
+        }
+
+        // Image#getTimestamp test
+        try {
+            closedImage.getTimestamp();
+            fail("Image should throw IllegalStateException when calling getTimestamp"
+                    + " after the image is closed");
+        } catch (IllegalStateException e) {
+            // Expected.
+        }
+
+        // Image#getTimestamp test
+        try {
+            closedImage.getTimestamp();
+            fail("Image should throw IllegalStateException when calling getTimestamp"
+                    + " after the image is closed");
+        } catch (IllegalStateException e) {
+            // Expected.
+        }
+
+        // Image#getCropRect test
+        try {
+            closedImage.getCropRect();
+            fail("Image should throw IllegalStateException when calling getCropRect"
+                    + " after the image is closed");
+        } catch (IllegalStateException e) {
+            // Expected.
+        }
+
+        // Image#setCropRect test
+        try {
+            Rect rect = new Rect();
+            closedImage.setCropRect(rect);
+            fail("Image should throw IllegalStateException when calling setCropRect"
+                    + " after the image is closed");
+        } catch (IllegalStateException e) {
+            // Expected.
+        }
+
+        // Image#getPlanes test
+        try {
+            closedImage.getPlanes();
+            fail("Image should throw IllegalStateException when calling getPlanes"
+                    + " after the image is closed");
+        } catch (IllegalStateException e) {
+            // Expected.
+        }
+    }
+}
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
index 4cd0046..e4695e9 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.graphics.ImageFormat;
 import android.graphics.Rect;
+import android.hardware.cts.helpers.CameraParameterizedTestCase;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
 import android.hardware.camera2.CameraCharacteristics;
@@ -31,6 +32,7 @@
 import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.params.SessionConfiguration;
 import android.util.Size;
+import android.hardware.camera2.cts.Camera2ParameterizedTestCase;
 import android.hardware.camera2.cts.CameraTestUtils;
 import android.hardware.camera2.cts.helpers.CameraErrorCollector;
 import android.hardware.camera2.cts.helpers.StaticMetadata;
@@ -44,6 +46,7 @@
 import android.util.Log;
 import android.view.Surface;
 import android.view.WindowManager;
+import androidx.test.InstrumentationRegistry;
 
 import com.android.ex.camera2.blocking.BlockingSessionCallback;
 import com.android.ex.camera2.blocking.BlockingStateCallback;
@@ -55,7 +58,11 @@
 import java.util.HashMap;
 import java.util.List;
 
-public class Camera2AndroidTestCase extends AndroidTestCase {
+import org.junit.Ignore;
+import org.junit.Test;
+
+// TODO: Can we de-duplicate this with Camera2AndroidBasicTestCase keeping in mind CtsVerifier ?
+public class Camera2AndroidTestCase extends Camera2ParameterizedTestCase {
     private static final String TAG = "Camera2AndroidTestCase";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
 
@@ -63,12 +70,10 @@
     protected static final Size DEFAULT_CAPTURE_SIZE = new Size(640, 480);
     protected static final int CAPTURE_WAIT_TIMEOUT_MS = 5000;
 
-    protected CameraManager mCameraManager;
     protected CameraDevice mCamera;
     protected CameraCaptureSession mCameraSession;
     protected BlockingSessionCallback mCameraSessionListener;
     protected BlockingStateCallback mCameraListener;
-    protected String[] mCameraIds;
     // include both standalone camera IDs and "hidden" physical camera IDs
     protected String[] mAllCameraIds;
     protected HashMap<String, StaticMetadata> mAllStaticInfo;
@@ -85,32 +90,15 @@
 
     protected WindowManager mWindowManager;
 
-    @Override
-    public void setContext(Context context) {
-        super.setContext(context);
-        mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
-        assertNotNull("Can't connect to camera manager!", mCameraManager);
-        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
-    }
-
     /**
      * Set up the camera2 test case required environments, including CameraManager,
      * HandlerThread, Camera IDs, and CameraStateCallback etc.
      */
     @Override
-    protected void setUp() throws Exception {
+    public void setUp() throws Exception {
         super.setUp();
+        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
 
-        /**
-         * Workaround for mockito and JB-MR2 incompatibility
-         *
-         * Avoid java.lang.IllegalArgumentException: dexcache == null
-         * https://code.google.com/p/dexmaker/issues/detail?id=2
-         */
-        System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
-
-        mCameraIds = mCameraManager.getCameraIdList();
-        assertNotNull("Camera ids shouldn't be null", mCameraIds);
         mHandlerThread = new HandlerThread(TAG);
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
@@ -125,14 +113,14 @@
 
         mAllStaticInfo = new HashMap<String, StaticMetadata>();
         List<String> hiddenPhysicalIds = new ArrayList<>();
-        for (String cameraId : mCameraIds) {
+        for (String cameraId : mCameraIdsUnderTest) {
             CameraCharacteristics props = mCameraManager.getCameraCharacteristics(cameraId);
             StaticMetadata staticMetadata = new StaticMetadata(props,
                     CheckLevel.ASSERT, /*collector*/null);
             mAllStaticInfo.put(cameraId, staticMetadata);
 
             for (String physicalId : props.getPhysicalCameraIds()) {
-                if (!Arrays.asList(mCameraIds).contains(physicalId) &&
+                if (!Arrays.asList(mCameraIdsUnderTest).contains(physicalId) &&
                         !hiddenPhysicalIds.contains(physicalId)) {
                     hiddenPhysicalIds.add(physicalId);
                     props = mCameraManager.getCameraCharacteristics(physicalId);
@@ -143,23 +131,15 @@
                 }
             }
         }
-        mAllCameraIds = new String[mCameraIds.length + hiddenPhysicalIds.size()];
-        System.arraycopy(mCameraIds, 0, mAllCameraIds, 0, mCameraIds.length);
+        mAllCameraIds = new String[mCameraIdsUnderTest.length + hiddenPhysicalIds.size()];
+        System.arraycopy(mCameraIdsUnderTest, 0, mAllCameraIds, 0, mCameraIdsUnderTest.length);
         for (int i = 0; i < hiddenPhysicalIds.size(); i++) {
-            mAllCameraIds[mCameraIds.length + i] = hiddenPhysicalIds.get(i);
+            mAllCameraIds[mCameraIdsUnderTest.length + i] = hiddenPhysicalIds.get(i);
         }
     }
 
     @Override
-    protected void tearDown() throws Exception {
-        String[] cameraIdsPostTest = mCameraManager.getCameraIdList();
-        assertNotNull("Camera ids shouldn't be null", cameraIdsPostTest);
-        Log.i(TAG, "Camera ids in setup:" + Arrays.toString(mCameraIds));
-        Log.i(TAG, "Camera ids in tearDown:" + Arrays.toString(cameraIdsPostTest));
-        assertTrue(
-                "Number of cameras changed from " + mCameraIds.length + " to " +
-                cameraIdsPostTest.length,
-                mCameraIds.length == cameraIdsPostTest.length);
+    public void tearDown() throws Exception {
         mHandlerThread.quitSafely();
         mHandler = null;
         closeDefaultImageReader();
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
index c345b41..090aa6c 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
@@ -28,6 +28,8 @@
 import android.graphics.RectF;
 import android.graphics.SurfaceTexture;
 import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.cts.Camera2ParameterizedTestCase;
+import android.hardware.camera2.cts.CameraTestUtils;
 import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CameraManager;
@@ -58,6 +60,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 
 import java.util.Arrays;
@@ -67,7 +70,8 @@
 /**
  * Camera2 test case base class by using mixed SurfaceView and TextureView as rendering target.
  */
-public class Camera2MultiViewTestCase {
+
+public class Camera2MultiViewTestCase extends Camera2ParameterizedTestCase {
     private static final String TAG = "MultiViewTestCase";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
 
@@ -75,13 +79,10 @@
 
     protected TextureView[] mTextureView =
             new TextureView[Camera2MultiViewCtsActivity.MAX_TEXTURE_VIEWS];
-    protected String[] mCameraIds;
     protected Handler mHandler;
 
-    private CameraManager mCameraManager;
     private HandlerThread mHandlerThread;
     private Activity mActivity;
-    private Context mContext;
 
     private CameraHolder[] mCameraHolders;
     private HashMap<String, Integer> mCameraIdMap;
@@ -92,15 +93,10 @@
     public ActivityTestRule<Camera2MultiViewCtsActivity> mActivityRule =
             new ActivityTestRule<>(Camera2MultiViewCtsActivity.class);
 
-    @Before
+    @Override
     public void setUp() throws Exception {
+        super.setUp();
         mActivity = mActivityRule.getActivity();
-        mContext = mActivity.getApplicationContext();
-        assertNotNull("Unable to get activity", mContext);
-        mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
-        assertNotNull("Unable to get CameraManager", mCameraManager);
-        mCameraIds = mCameraManager.getCameraIdList();
-        assertNotNull("Unable to get camera ids", mCameraIds);
         mHandlerThread = new HandlerThread(TAG);
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
@@ -110,25 +106,17 @@
         }
         assertNotNull("Unable to get texture view", mTextureView);
         mCameraIdMap = new HashMap<String, Integer>();
-        int numCameras = mCameraIds.length;
+        int numCameras = mCameraIdsUnderTest.length;
         mCameraHolders = new CameraHolder[numCameras];
         for (int i = 0; i < numCameras; i++) {
-            mCameraHolders[i] = new CameraHolder(mCameraIds[i]);
-            mCameraIdMap.put(mCameraIds[i], i);
+            mCameraHolders[i] = new CameraHolder(mCameraIdsUnderTest[i]);
+            mCameraIdMap.put(mCameraIdsUnderTest[i], i);
         }
         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
     }
 
-    @After
+    @Override
     public void tearDown() throws Exception {
-        String[] cameraIdsPostTest = mCameraManager.getCameraIdList();
-        assertNotNull("Camera ids shouldn't be null", cameraIdsPostTest);
-        Log.i(TAG, "Camera ids in setup:" + Arrays.toString(mCameraIds));
-        Log.i(TAG, "Camera ids in tearDown:" + Arrays.toString(cameraIdsPostTest));
-        assertTrue(
-                "Number of cameras changed from " + mCameraIds.length + " to " +
-                cameraIdsPostTest.length,
-                mCameraIds.length == cameraIdsPostTest.length);
         mHandlerThread.quitSafely();
         mHandler = null;
         for (CameraHolder camera : mCameraHolders) {
@@ -137,6 +125,7 @@
                 camera = null;
             }
         }
+        super.tearDown();
     }
 
     /**
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
index 5a95d62..ecc87d6 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
@@ -19,6 +19,8 @@
 import static android.hardware.camera2.cts.CameraTestUtils.*;
 
 import static com.android.ex.camera2.blocking.BlockingStateCallback.STATE_CLOSED;
+import androidx.test.InstrumentationRegistry;
+import android.app.UiAutomation;
 
 import android.content.Context;
 import android.graphics.ImageFormat;
@@ -32,6 +34,7 @@
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.cts.Camera2SurfaceViewCtsActivity;
+import android.hardware.camera2.cts.Camera2ParameterizedTestCase;
 import android.hardware.camera2.cts.CameraTestUtils;
 import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
 import android.hardware.camera2.cts.helpers.CameraErrorCollector;
@@ -57,6 +60,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 
 import java.io.File;
@@ -64,6 +68,12 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
+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;
+
 
 /**
  * Camera2 Preview test case base class by using SurfaceView as rendering target.
@@ -75,7 +85,7 @@
  * </p>
  */
 
-public class Camera2SurfaceViewTestCase {
+public class Camera2SurfaceViewTestCase extends Camera2ParameterizedTestCase {
     private static final String TAG = "SurfaceViewTestCase";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
     private static final int WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS = 1000;
@@ -86,9 +96,6 @@
     protected static final int NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY = 8;
     protected static final int MIN_FRAME_DURATION_ERROR_MARGIN = 100; // ns
 
-    protected Context mContext;
-    protected CameraManager mCameraManager;
-    protected String[] mCameraIds;
     protected HandlerThread mHandlerThread;
     protected Handler mHandler;
     protected BlockingStateCallback mCameraListener;
@@ -119,18 +126,7 @@
 
     @Before
     public void setUp() throws Exception {
-        mContext = mActivityRule.getActivity().getApplicationContext();
-        /**
-         * Workaround for mockito and JB-MR2 incompatibility
-         *
-         * Avoid java.lang.IllegalArgumentException: dexcache == null
-         * https://code.google.com/p/dexmaker/issues/detail?id=2
-         */
-        System.setProperty("dexmaker.dexcache", mContext.getCacheDir().toString());
-        mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
-        assertNotNull("Unable to get CameraManager", mCameraManager);
-        mCameraIds = mCameraManager.getCameraIdList();
-        assertNotNull("Unable to get camera ids", mCameraIds);
+        super.setUp();
         mHandlerThread = new HandlerThread(TAG);
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
@@ -145,14 +141,14 @@
 
         mAllStaticInfo = new HashMap<String, StaticMetadata>();
         List<String> hiddenPhysicalIds = new ArrayList<>();
-        for (String cameraId : mCameraIds) {
+        for (String cameraId : mCameraIdsUnderTest) {
             CameraCharacteristics props = mCameraManager.getCameraCharacteristics(cameraId);
             StaticMetadata staticMetadata = new StaticMetadata(props,
                     CheckLevel.ASSERT, /*collector*/null);
             mAllStaticInfo.put(cameraId, staticMetadata);
 
             for (String physicalId : props.getPhysicalCameraIds()) {
-                if (!Arrays.asList(mCameraIds).contains(physicalId) &&
+                if (!Arrays.asList(mCameraIdsUnderTest).contains(physicalId) &&
                         !hiddenPhysicalIds.contains(physicalId)) {
                     hiddenPhysicalIds.add(physicalId);
                     props = mCameraManager.getCameraCharacteristics(physicalId);
@@ -169,15 +165,6 @@
 
     @After
     public void tearDown() throws Exception {
-        String[] cameraIdsPostTest = mCameraManager.getCameraIdList();
-        assertNotNull("Camera ids shouldn't be null", cameraIdsPostTest);
-        Log.i(TAG, "Camera ids in setup:" + Arrays.toString(mCameraIds));
-        Log.i(TAG, "Camera ids in tearDown:" + Arrays.toString(cameraIdsPostTest));
-        assertTrue(
-                "Number of cameras changed from " + mCameraIds.length + " to " +
-                cameraIdsPostTest.length,
-                mCameraIds.length == cameraIdsPostTest.length);
-        // Teardown the camera preview required environments.
         mHandlerThread.quitSafely();
         mHandler = null;
         mCameraListener = null;
@@ -188,6 +175,7 @@
             // When new Exception(e) is used, exception info will be printed twice.
             throw new Exception(e.getMessage());
         }
+        super.tearDown();
     }
 
     /**
@@ -541,7 +529,7 @@
         List<Integer> expectedAeStates = new ArrayList<Integer>();
         expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_LOCKED));
         CameraTestUtils.waitForAnyResultValue(resultListener, CaptureResult.CONTROL_AE_STATE,
-                expectedAeStates, WAIT_FOR_RESULT_TIMEOUT_MS, NUM_RESULTS_WAIT_TIMEOUT);
+                expectedAeStates, NUM_RESULTS_WAIT_TIMEOUT, WAIT_FOR_RESULT_TIMEOUT_MS);
     }
 
     /**
diff --git a/tests/camera/src/android/hardware/multiprocess/camera/cts/Camera2Activity.java b/tests/camera/src/android/hardware/multiprocess/camera/cts/Camera2Activity.java
index 418eb7c..398ffb8 100644
--- a/tests/camera/src/android/hardware/multiprocess/camera/cts/Camera2Activity.java
+++ b/tests/camera/src/android/hardware/multiprocess/camera/cts/Camera2Activity.java
@@ -63,7 +63,7 @@
                         " could not connect camera service");
                 return;
             }
-            String[] cameraIds = manager.getCameraIdList();
+            String[] cameraIds = manager.getCameraIdListNoLazy();
 
             if (cameraIds == null || cameraIds.length == 0) {
                 mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_ERROR, TAG +
diff --git a/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java b/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
index 2a69aed..14dbd92 100644
--- a/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
+++ b/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
@@ -165,7 +165,7 @@
     public void testBasicCamera2ActivityEviction() throws Throwable {
         CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
         assertNotNull(manager);
-        String[] cameraIds = manager.getCameraIdList();
+        String[] cameraIds = manager.getCameraIdListNoLazy();
 
         if (cameraIds.length == 0) {
             Log.i(TAG, "Skipping testBasicCamera2ActivityEviction, device has no cameras.");
@@ -269,7 +269,7 @@
         int PERMISSION_CALLBACK_TIMEOUT_MS = 2000;
         CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
         assertNotNull(manager);
-        String[] cameraIds = manager.getCameraIdList();
+        String[] cameraIds = manager.getCameraIdListNoLazy();
 
         if (cameraIds.length == 0) {
             Log.i(TAG, "Skipping testCamera2AccessCallback, device has no cameras.");
@@ -305,7 +305,7 @@
         int PERMISSION_CALLBACK_TIMEOUT_MS = 2000;
         CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
         assertNotNull(manager);
-        String[] cameraIds = manager.getCameraIdList();
+        String[] cameraIds = manager.getCameraIdListNoLazy();
 
         if (cameraIds.length == 0) {
             Log.i(TAG, "Skipping testBasicCamera2AccessCallback, device has no cameras.");
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/Camera2ParameterizedTestCase.java b/tests/camera/utils/src/android/hardware/camera2/cts/Camera2ParameterizedTestCase.java
new file mode 100644
index 0000000..c7deb5a
--- /dev/null
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/Camera2ParameterizedTestCase.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+package android.hardware.camera2.cts;
+
+import android.content.Context;
+import android.hardware.cts.helpers.CameraParameterizedTestCase;
+import android.hardware.camera2.cts.CameraTestUtils;
+import android.hardware.camera2.CameraManager;
+import android.util.Log;
+
+import java.util.Arrays;
+
+import org.junit.Ignore;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+
+public class Camera2ParameterizedTestCase extends CameraParameterizedTestCase {
+    private static final String TAG = "Camera2ParameterizedTestCase";
+    protected CameraManager mCameraManager;
+    // The list of camera ids we're testing. If we're testing system cameras
+    // (mAdoptShellPerm == true), we have only system camera ids in the array and not normal camera
+    // ids.
+    protected String[] mCameraIdsUnderTest;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        /**
+         * Workaround for mockito and JB-MR2 incompatibility
+         *
+         * Avoid java.lang.IllegalArgumentException: dexcache == null
+         * https://code.google.com/p/dexmaker/issues/detail?id=2
+         */
+        System.setProperty("dexmaker.dexcache", mContext.getCacheDir().toString());
+        mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
+        assertNotNull("Unable to get CameraManager", mCameraManager);
+        mCameraIdsUnderTest =
+                CameraTestUtils.getCameraIdListForTesting(mCameraManager, mAdoptShellPerm);
+        assertNotNull("Unable to get camera ids", mCameraIdsUnderTest);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        String[] cameraIdsPostTest =
+                CameraTestUtils.getCameraIdListForTesting(mCameraManager, mAdoptShellPerm);
+        assertNotNull("Camera ids shouldn't be null", cameraIdsPostTest);
+        Log.i(TAG, "Camera ids in setup:" + Arrays.toString(mCameraIdsUnderTest));
+        Log.i(TAG, "Camera ids in tearDown:" + Arrays.toString(cameraIdsPostTest));
+        assertTrue(
+                "Number of cameras changed from " + mCameraIdsUnderTest.length + " to " +
+                cameraIdsPostTest.length,
+                mCameraIdsUnderTest.length == cameraIdsPostTest.length);
+        super.tearDown();
+    }
+}
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
index e408cb5..b49a0a6 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -704,6 +704,38 @@
         }
     }
 
+    public static boolean hasCapability(CameraCharacteristics characteristics, int capability) {
+        int [] capabilities =
+                characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+        for (int c : capabilities) {
+            if (c == capability) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static boolean isSystemCamera(CameraManager manager, String cameraId)
+            throws CameraAccessException {
+        CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
+        return hasCapability(characteristics,
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA);
+    }
+
+    public static String[] getCameraIdListForTesting(CameraManager manager,
+            boolean getSystemCameras)
+            throws CameraAccessException {
+        String [] ids = manager.getCameraIdListNoLazy();
+        List<String> idsForTesting = new ArrayList<String>();
+        for (String id : ids) {
+            boolean isSystemCamera = isSystemCamera(manager, id);
+            if (getSystemCameras == isSystemCamera) {
+                idsForTesting.add(id);
+            }
+        }
+        return idsForTesting.toArray(new String[idsForTesting.size()]);
+    }
+
     /**
      * Block until the camera is opened.
      *
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/CameraMetadataGetter.java b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/CameraMetadataGetter.java
index db75cdd..e0a956e 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/CameraMetadataGetter.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/CameraMetadataGetter.java
@@ -108,7 +108,7 @@
         CameraCharacteristics staticMetadata;
         String[] cameraIds;
         try {
-            cameraIds = mCameraManager.getCameraIdList();
+            cameraIds = mCameraManager.getCameraIdListNoLazy();
         } catch (CameraAccessException e) {
             Log.e(TAG, "Unable to get camera ids, skip this info, error: " + e.getMessage());
             return "";
@@ -180,7 +180,7 @@
         StringBuffer templates = new StringBuffer("{\"CameraRequestTemplates\":{");
         String[] cameraIds;
         try {
-            cameraIds = mCameraManager.getCameraIdList();
+            cameraIds = mCameraManager.getCameraIdListNoLazy();
         } catch (CameraAccessException e) {
             Log.e(TAG, "Unable to get camera ids, skip this info, error: " + e.getMessage());
             return "";
diff --git a/tests/camera/utils/src/android/hardware/cts/helpers/CameraParameterizedTestCase.java b/tests/camera/utils/src/android/hardware/cts/helpers/CameraParameterizedTestCase.java
new file mode 100644
index 0000000..b2f4c04
--- /dev/null
+++ b/tests/camera/utils/src/android/hardware/cts/helpers/CameraParameterizedTestCase.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package android.hardware.cts.helpers;
+
+import android.app.UiAutomation;
+import android.content.Context;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import android.app.Activity;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.After;
+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.ArrayList;
+import java.util.List;
+
+public class CameraParameterizedTestCase {
+    protected UiAutomation mUiAutomation;
+    protected Context mContext;
+    @Parameter(0)
+    public boolean mAdoptShellPerm;
+
+    @Parameters
+    public static Iterable<? extends Object> data() {
+        List<Boolean> adoptShellPerm = new ArrayList<Boolean>();
+        adoptShellPerm.add(true);
+        adoptShellPerm.add(false);
+        return adoptShellPerm;
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        mContext = InstrumentationRegistry.getTargetContext();
+        if (mAdoptShellPerm) {
+            mUiAutomation.adoptShellPermissionIdentity();
+        }
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mAdoptShellPerm) {
+            mUiAutomation.dropShellPermissionIdentity();
+        }
+    }
+}
diff --git a/tests/camera/utils/src/android/hardware/cts/helpers/CameraUtils.java b/tests/camera/utils/src/android/hardware/cts/helpers/CameraUtils.java
index fe119a9..0c143a5 100644
--- a/tests/camera/utils/src/android/hardware/cts/helpers/CameraUtils.java
+++ b/tests/camera/utils/src/android/hardware/cts/helpers/CameraUtils.java
@@ -38,7 +38,7 @@
      */
     public static boolean isLegacyHAL(Context context, int cameraId) throws Exception {
         CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
-        String cameraIdStr = manager.getCameraIdList()[cameraId];
+        String cameraIdStr = manager.getCameraIdListNoLazy()[cameraId];
         CameraCharacteristics characteristics =
                 manager.getCameraCharacteristics(cameraIdStr);
 
@@ -56,7 +56,7 @@
      */
     public static boolean isExternal(Context context, int cameraId) throws Exception {
         CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
-        String cameraIdStr = manager.getCameraIdList()[cameraId];
+        String cameraIdStr = manager.getCameraIdListNoLazy()[cameraId];
         CameraCharacteristics characteristics =
                 manager.getCameraCharacteristics(cameraIdStr);
 
diff --git a/tests/contentcaptureservice/OWNERS b/tests/contentcaptureservice/OWNERS
index 4df1ffa..2abf830 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
+adamhe@google.com
+svetoslavganov@google.com
+felipeal@google.com
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java
index 8f77b2b..2534412 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java
@@ -44,6 +44,7 @@
 import com.android.compatibility.common.util.SettingsStateChangerRule;
 import com.android.compatibility.common.util.SettingsUtils;
 
+import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -201,6 +202,12 @@
         CtsContentCaptureService.resetStaticState();
     }
 
+    @After
+    public void clearServiceWatcher() {
+        Log.v(mTag, "@After: clearServiceWatcher()");
+        CtsContentCaptureService.clearServiceWatcher();
+    }
+
     @Nullable
     public static void setFeatureEnabledBySettings(@Nullable boolean enabled) {
         SettingsUtils.syncSet(sContext, CONTENT_CAPTURE_ENABLED, enabled ? "1" : "0");
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/ChildlessActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ChildlessActivityTest.java
index 9f453b8..5beb910 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ChildlessActivityTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ChildlessActivityTest.java
@@ -1140,6 +1140,7 @@
         watcher1.waitFor(DESTROYED);
 
         // Re-enable feature
+        CtsContentCaptureService.clearServiceWatcher();
         final ServiceWatcher reconnectionWatcher = CtsContentCaptureService.setServiceWatcher();
         reconnectionWatcher.whitelistSelf();
         setFeatureEnabled(service1, reason, /* enabled= */ true);
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsContentCaptureService.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsContentCaptureService.java
index 349ff36..840bd03 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsContentCaptureService.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsContentCaptureService.java
@@ -149,6 +149,16 @@
         }
     }
 
+    public static void clearServiceWatcher() {
+        if (sServiceWatcher != null) {
+            if (sServiceWatcher.mReadyToClear) {
+                sServiceWatcher.mService = null;
+                sServiceWatcher = null;
+            } else {
+                sServiceWatcher.mReadyToClear = true;
+            }
+        }
+    }
 
     /**
      * When set, doesn't throw exceptions when it receives an event from a session that doesn't
@@ -170,13 +180,14 @@
             return;
         }
 
-        if (sServiceWatcher.mService != null) {
+        if (!sServiceWatcher.mReadyToClear && sServiceWatcher.mService != null) {
             addException("onConnected(): already created: %s", sServiceWatcher);
             return;
         }
 
         sServiceWatcher.mService = this;
         sServiceWatcher.mCreated.countDown();
+        sServiceWatcher.mReadyToClear = false;
 
         if (mConnectedLatch.getCount() == 0) {
             addException("already connected: %s", mConnectedLatch);
@@ -208,8 +219,7 @@
             latch.countDown();
         }
         sServiceWatcher.mDestroyed.countDown();
-        sServiceWatcher.mService = null;
-        sServiceWatcher = null;
+        clearServiceWatcher();
     }
 
     /**
@@ -479,6 +489,7 @@
 
         private final CountDownLatch mCreated = new CountDownLatch(1);
         private final CountDownLatch mDestroyed = new CountDownLatch(1);
+        private boolean mReadyToClear = true;
         private Pair<Set<String>, Set<ComponentName>> mWhitelist;
 
         private CtsContentCaptureService mService;
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java
index e01ed72..74429de 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java
@@ -111,6 +111,65 @@
     }
 
     /**
+     * Test for session lifecycle events.
+     */
+    @Test
+    public void testSessionLifecycleEvents() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+        final AtomicReference<CustomView> customViewRef = new AtomicReference<>();
+
+        CustomViewActivity.setCustomViewDelegate((customView, structure) -> {
+            customViewRef.set(customView);
+            final ContentCaptureSession session = customView.getContentCaptureSession();
+            session.notifySessionResumed();
+            session.notifySessionPaused();
+        });
+
+        final CustomViewActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final Session session = service.getOnlyFinishedSession();
+        Log.v(TAG, "session id: " + session.id);
+
+        assertRightActivity(session, session.id, activity);
+
+        final View grandpa1 = (View) activity.mCustomView.getParent();
+        final View grandpa2 = (View) grandpa1.getParent();
+        final View decorView = activity.getDecorView();
+        final AutofillId customViewId = activity.mCustomView.getAutofillId();
+        Log.v(TAG, "assertJustInitialViewsAppeared(): grandpa1=" + grandpa1.getAutofillId()
+                + ", grandpa2=" + grandpa2.getAutofillId() + ", decor="
+                + decorView.getAutofillId() + "customView=" + customViewId);
+
+        final List<ContentCaptureEvent> events = session.getEvents();
+        Log.v(TAG, "events(" + events.size() + "): " + events);
+        final int additionalEvents = 2;
+
+        assertThat(events.size()).isAtLeast(CustomViewActivity.MIN_EVENTS + additionalEvents);
+
+        // Assert just the relevant events
+        assertSessionResumed(events, 0);
+        assertViewTreeStarted(events, 1);
+        assertDecorViewAppeared(events, 2, decorView);
+        assertViewAppeared(events, 3, grandpa2, decorView.getAutofillId());
+        assertViewAppeared(events, 4, grandpa1, grandpa2.getAutofillId());
+
+        // Assert for session lifecycle events.
+        assertSessionResumed(events, 5);
+        assertSessionPaused(events, 6);
+
+        assertViewWithUnknownParentAppeared(events, 7, session.id, customViewRef.get());
+        assertViewTreeFinished(events, 8);
+        assertSessionPaused(events, 9);
+
+        activity.assertInitialViewsDisappeared(events, additionalEvents);
+    }
+
+    /**
      * Tests when the view has virtual children but it doesn't return right away and calls
      * the session notification methods instead - this is wrong because the main view will be
      * notified last, but we cannot prevent the apps from doing so...
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivityTest.java
index ea22140..aa4fa9e 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivityTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivityTest.java
@@ -122,6 +122,46 @@
     }
 
     @Test
+    public void testContentCaptureSessionCache() throws Exception {
+        final CtsContentCaptureService service = enableService();
+        final ActivityWatcher watcher = startWatcher();
+
+        final ContentCaptureContext clientContext = newContentCaptureContext();
+
+        final AtomicReference<ContentCaptureSession> mainSessionRef = new AtomicReference<>();
+        final AtomicReference<ContentCaptureSession> childSessionRef = new AtomicReference<>();
+
+        LoginActivity.onRootView((activity, rootView) -> {
+            final ContentCaptureSession mainSession = rootView.getContentCaptureSession();
+            mainSessionRef.set(mainSession);
+            final ContentCaptureSession childSession = mainSession
+                    .createContentCaptureSession(clientContext);
+            childSessionRef.set(childSession);
+
+            rootView.setContentCaptureSession(childSession);
+            // Already called getContentCaptureSession() earlier, use cached session (main).
+            assertThat(rootView.getContentCaptureSession()).isEqualTo(childSession);
+
+            rootView.setContentCaptureSession(mainSession);
+            assertThat(rootView.getContentCaptureSession()).isEqualTo(mainSession);
+
+            rootView.setContentCaptureSession(childSession);
+            assertThat(rootView.getContentCaptureSession()).isEqualTo(childSession);
+        });
+
+        final LoginActivity activity = launchActivity();
+        watcher.waitFor(RESUMED);
+
+        activity.finish();
+        watcher.waitFor(DESTROYED);
+
+        final ContentCaptureSessionId childSessionId = childSessionRef.get()
+                .getContentCaptureSessionId();
+
+        assertSessionId(childSessionId, activity.getRootView());
+    }
+
+    @Test
     public void testSimpleLifecycle_rootViewSession() throws Exception {
         final CtsContentCaptureService service = enableService();
         final ActivityWatcher watcher = startWatcher();
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..7ae8d88 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -114,6 +114,8 @@
 
         <activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$CallbackTrackingActivity"/>
 
+        <activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$SecondCallbackTrackingActivity"/>
+
         <activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$TranslucentCallbackTrackingActivity"
                   android:theme="@android:style/Theme.Translucent.NoTitleBar" />
 
@@ -150,6 +152,18 @@
 
         <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.lifecycle.ActivityLifecycleClientTestBase$DifferentAffinityActivity"
+                  android:taskAffinity="nobody.but.DifferentAffinityActivity" />
+
+        <activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$TransitionSourceActivity"
+                  android:theme="@style/window_activity_transitions" />
+
+        <activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$TransitionDestinationActivity"
+                  android:theme="@style/window_activity_transitions" />
+
         <activity android:name="android.server.wm.StartActivityTests$TestActivity2" />
 
         <activity android:name="android.server.wm.MultiDisplaySystemDecorationTests$ImeTestActivity" />
@@ -233,6 +247,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 +282,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"
@@ -295,7 +316,7 @@
         <activity android:name="android.server.wm.WindowFocusTests$LosingFocusActivity" />
         <activity android:name="android.app.Activity"/>
 
-        <activity android:name="android.server.wm.DragDropActivity"
+        <activity android:name="android.server.wm.DragDropTest$DragDropActivity"
                   android:screenOrientation="locked"
                   android:turnScreenOn="true"
                   android:showWhenLocked="true"
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..879fbb7 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
@@ -220,6 +220,8 @@
         public static final String EXTRA_FIXED_ORIENTATION = "fixed_orientation";
         public static final String EXTRA_CONFIGURATION = "configuration";
         public static final String EXTRA_CONFIG_ASSETS_SEQ = "config_assets_seq";
+        public static final String EXTRA_INTENTS = "intents";
+        public static final String COMMAND_START_ACTIVITIES = "start_activities";
     }
 
     /**
@@ -273,6 +275,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/app_base/src/android/server/wm/app/TestActivity.java b/tests/framework/base/windowmanager/app_base/src/android/server/wm/app/TestActivity.java
index 575e878..2d7f83d 100644
--- a/tests/framework/base/windowmanager/app_base/src/android/server/wm/app/TestActivity.java
+++ b/tests/framework/base/windowmanager/app_base/src/android/server/wm/app/TestActivity.java
@@ -18,7 +18,9 @@
 
 import static android.server.wm.app.Components.TestActivity.EXTRA_CONFIG_ASSETS_SEQ;
 import static android.server.wm.app.Components.TestActivity.EXTRA_FIXED_ORIENTATION;
+import static android.server.wm.app.Components.TestActivity.EXTRA_INTENTS;
 import static android.server.wm.app.Components.TestActivity.TEST_ACTIVITY_ACTION_FINISH_SELF;
+import static android.server.wm.app.Components.TestActivity.COMMAND_START_ACTIVITIES;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -26,6 +28,9 @@
 import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.os.Bundle;
+import android.os.Parcelable;
+
+import java.util.Arrays;
 
 public class TestActivity extends AbstractLifecycleLogActivity {
 
@@ -71,6 +76,18 @@
     }
 
     @Override
+    public void handleCommand(String command, Bundle data) {
+        switch (command) {
+            case COMMAND_START_ACTIVITIES:
+                final Parcelable[] intents = data.getParcelableArray(EXTRA_INTENTS);
+                startActivities(Arrays.copyOf(intents, intents.length, Intent[].class));
+                break;
+            default:
+                super.handleCommand(command, data);
+        }
+    }
+
+    @Override
     public void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         dumpConfiguration(newConfig);
diff --git a/tests/framework/base/windowmanager/backgroundactivity/Android.mk b/tests/framework/base/windowmanager/backgroundactivity/Android.mk
index 3ddcdf0..18e466c 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/Android.mk
+++ b/tests/framework/base/windowmanager/backgroundactivity/Android.mk
@@ -29,7 +29,8 @@
     androidx.test.rules \
     cts-wm-util \
     cts-wm-app-base \
-    cts-core-test-runner-axt
+    cts-core-test-runner-axt \
+    cts-background-activity-common
 
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
diff --git a/tests/framework/base/windowmanager/backgroundactivity/AppA/Android.mk b/tests/framework/base/windowmanager/backgroundactivity/AppA/Android.mk
index 08d4f36..c3080f9 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/AppA/Android.mk
+++ b/tests/framework/base/windowmanager/backgroundactivity/AppA/Android.mk
@@ -22,7 +22,8 @@
 LOCAL_USE_AAPT2 := true
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    cts-wm-app-base
+    cts-wm-app-base \
+    cts-background-activity-common
 
 LOCAL_STATIC_ANDROID_LIBRARIES := \
     androidx.legacy_legacy-support-v4
diff --git a/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/SendPendingIntentReceiver.java b/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/SendPendingIntentReceiver.java
index 3647be4..7ec1cc9 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/SendPendingIntentReceiver.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/SendPendingIntentReceiver.java
@@ -23,11 +23,14 @@
 import static android.server.wm.backgroundactivity.appa.Components.StartBackgroundActivityReceiver.START_ACTIVITY_DELAY_MS_EXTRA;
 import static android.server.wm.backgroundactivity.appb.Components.APP_B_START_PENDING_INTENT_RECEIVER;
 import static android.server.wm.backgroundactivity.appb.Components.StartPendingIntentReceiver.PENDING_INTENT_EXTRA;
+import static android.server.wm.backgroundactivity.common.CommonComponents.EVENT_NOTIFIER_EXTRA;
 
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.os.ResultReceiver;
+import android.server.wm.backgroundactivity.common.CommonComponents.Event;
 
 /**
  * Receive broadcast command to create a pendingIntent and send it to AppB.
@@ -38,6 +41,10 @@
     public void onReceive(Context context, Intent receivedIntent) {
         boolean isBroadcast = receivedIntent.getBooleanExtra(IS_BROADCAST_EXTRA, false);
         int startActivityDelayMs = receivedIntent.getIntExtra(START_ACTIVITY_DELAY_MS_EXTRA, 0);
+        ResultReceiver eventNotifier = receivedIntent.getParcelableExtra(EVENT_NOTIFIER_EXTRA);
+        if (eventNotifier != null) {
+            eventNotifier.send(Event.APP_A_SEND_PENDING_INTENT_BROADCAST_RECEIVED, null);
+        }
 
         final PendingIntent pendingIntent;
         if (isBroadcast) {
@@ -46,6 +53,7 @@
             Intent newIntent = new Intent();
             newIntent.setComponent(APP_A_START_ACTIVITY_RECEIVER);
             newIntent.putExtra(START_ACTIVITY_DELAY_MS_EXTRA, startActivityDelayMs);
+            newIntent.putExtra(EVENT_NOTIFIER_EXTRA, eventNotifier);
             pendingIntent = PendingIntent.getBroadcast(context, 0,
                     newIntent, PendingIntent.FLAG_UPDATE_CURRENT);
         } else {
@@ -53,7 +61,6 @@
             Intent newIntent = new Intent();
             newIntent.setComponent(APP_A_BACKGROUND_ACTIVITY);
             newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
             pendingIntent = PendingIntent.getActivity(context, 0,
                     newIntent, PendingIntent.FLAG_UPDATE_CURRENT);
         }
@@ -62,6 +69,7 @@
         Intent intent = new Intent();
         intent.setComponent(APP_B_START_PENDING_INTENT_RECEIVER);
         intent.putExtra(PENDING_INTENT_EXTRA, pendingIntent);
+        intent.putExtra(EVENT_NOTIFIER_EXTRA, eventNotifier);
         context.sendBroadcast(intent);
     }
 }
diff --git a/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/StartBackgroundActivityReceiver.java b/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/StartBackgroundActivityReceiver.java
index 844dd4f..0622a45 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/StartBackgroundActivityReceiver.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/StartBackgroundActivityReceiver.java
@@ -17,11 +17,14 @@
 package android.server.wm.backgroundactivity.appa;
 
 import static android.server.wm.backgroundactivity.appa.Components.StartBackgroundActivityReceiver.START_ACTIVITY_DELAY_MS_EXTRA;
+import static android.server.wm.backgroundactivity.common.CommonComponents.EVENT_NOTIFIER_EXTRA;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.os.ResultReceiver;
 import android.os.SystemClock;
+import android.server.wm.backgroundactivity.common.CommonComponents.Event;
 
 /**
  * A class to help test case to start background activity.
@@ -30,6 +33,11 @@
 
     @Override
     public void onReceive(Context context, Intent intent) {
+        ResultReceiver eventNotifier = intent.getParcelableExtra(EVENT_NOTIFIER_EXTRA);
+        if (eventNotifier != null) {
+            eventNotifier.send(Event.APP_A_START_BACKGROUND_ACTIVITY_BROADCAST_RECEIVED, null);
+        }
+
         if (!intent.hasExtra(START_ACTIVITY_DELAY_MS_EXTRA)) {
             startActivityNow(context);
             return;
diff --git a/tests/framework/base/windowmanager/backgroundactivity/AppB/Android.mk b/tests/framework/base/windowmanager/backgroundactivity/AppB/Android.mk
index 0398b74..d632370 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/AppB/Android.mk
+++ b/tests/framework/base/windowmanager/backgroundactivity/AppB/Android.mk
@@ -22,7 +22,8 @@
 LOCAL_USE_AAPT2 := true
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    cts-wm-app-base
+    cts-wm-app-base \
+    cts-background-activity-common
 
 LOCAL_STATIC_ANDROID_LIBRARIES := \
     androidx.legacy_legacy-support-v4
diff --git a/tests/framework/base/windowmanager/backgroundactivity/AppB/src/android/server/wm/backgroundactivity/appb/StartPendingIntentReceiver.java b/tests/framework/base/windowmanager/backgroundactivity/AppB/src/android/server/wm/backgroundactivity/appb/StartPendingIntentReceiver.java
index 830a611..e8f6991 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/AppB/src/android/server/wm/backgroundactivity/appb/StartPendingIntentReceiver.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/AppB/src/android/server/wm/backgroundactivity/appb/StartPendingIntentReceiver.java
@@ -17,11 +17,14 @@
 package android.server.wm.backgroundactivity.appb;
 
 import static android.server.wm.backgroundactivity.appb.Components.StartPendingIntentReceiver.PENDING_INTENT_EXTRA;
+import static android.server.wm.backgroundactivity.common.CommonComponents.EVENT_NOTIFIER_EXTRA;
 
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.os.ResultReceiver;
+import android.server.wm.backgroundactivity.common.CommonComponents.Event;
 
 /**
  * Receive pending intent from AppA and launch it
@@ -31,6 +34,11 @@
     @Override
     public void onReceive(Context context, Intent intent) {
         PendingIntent pendingIntent = intent.getParcelableExtra(PENDING_INTENT_EXTRA);
+        ResultReceiver eventNotifier = intent.getParcelableExtra(EVENT_NOTIFIER_EXTRA);
+        if (eventNotifier != null) {
+            eventNotifier.send(Event.APP_B_START_PENDING_INTENT_BROADCAST_RECEIVED, null);
+        }
+
         try {
             pendingIntent.send();
         } catch (PendingIntent.CanceledException e) {
diff --git a/tests/backup/app/permission22/Android.mk b/tests/framework/base/windowmanager/backgroundactivity/common/Android.mk
similarity index 66%
copy from tests/backup/app/permission22/Android.mk
copy to tests/framework/base/windowmanager/backgroundactivity/common/Android.mk
index 8790e19..a7eaad6 100644
--- a/tests/backup/app/permission22/Android.mk
+++ b/tests/framework/base/windowmanager/backgroundactivity/common/Android.mk
@@ -16,16 +16,17 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_MODULE_TAGS := optional
+# Don't include this package in any target.
+LOCAL_MODULE_TAGS := tests
 
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    androidx.annotation_annotation \
+    guava
 
-LOCAL_PACKAGE_NAME := CtsPermissionBackupApp22
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := test_current
 
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_MODULE := cts-background-activity-common
 
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/framework/base/windowmanager/backgroundactivity/common/src/android/server/wm/backgroundactivity/common/CommonComponents.java b/tests/framework/base/windowmanager/backgroundactivity/common/src/android/server/wm/backgroundactivity/common/CommonComponents.java
new file mode 100644
index 0000000..1981ffb
--- /dev/null
+++ b/tests/framework/base/windowmanager/backgroundactivity/common/src/android/server/wm/backgroundactivity/common/CommonComponents.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.server.wm.backgroundactivity.common;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Constant-holding class common to AppA, AppB and tests.
+ */
+public class CommonComponents {
+
+    public static final String EVENT_NOTIFIER_EXTRA = "EVENT_NOTIFIER_EXTRA";
+
+    @IntDef({
+            Event.APP_A_SEND_PENDING_INTENT_BROADCAST_RECEIVED,
+            Event.APP_B_START_PENDING_INTENT_BROADCAST_RECEIVED,
+            Event.APP_A_START_BACKGROUND_ACTIVITY_BROADCAST_RECEIVED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Event {
+        int APP_A_SEND_PENDING_INTENT_BROADCAST_RECEIVED = 0;
+        int APP_B_START_PENDING_INTENT_BROADCAST_RECEIVED = 1;
+        int APP_A_START_BACKGROUND_ACTIVITY_BROADCAST_RECEIVED = 2;
+    }
+}
diff --git a/tests/framework/base/windowmanager/backgroundactivity/common/src/android/server/wm/backgroundactivity/common/EventReceiver.java b/tests/framework/base/windowmanager/backgroundactivity/common/src/android/server/wm/backgroundactivity/common/EventReceiver.java
new file mode 100644
index 0000000..4812fa6
--- /dev/null
+++ b/tests/framework/base/windowmanager/backgroundactivity/common/src/android/server/wm/backgroundactivity/common/EventReceiver.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 android.server.wm.backgroundactivity.common;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import android.os.Bundle;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ResultReceiver;
+import android.server.wm.backgroundactivity.common.CommonComponents.Event;
+
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Class used to register for events sent via IPC using {@link #getNotifier()} parcelable.
+ *
+ * Create an instance for the event of interest, pass {@link #getNotifier()} to the target, either
+ * via some AIDL or intent extra, have the caller call {@link ResultReceiver#send(int, Bundle)},
+ * then finally wait for the event with {@link #waitForEventOrThrow(long)}.
+ */
+public class EventReceiver {
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+    private final ConditionVariable mEventReceivedVariable = new ConditionVariable(false);
+
+    @Event
+    private final int mEvent;
+
+    public EventReceiver(@Event int event) {
+        mEvent = event;
+    }
+
+    public ResultReceiver getNotifier() {
+        return new ResultReceiver(mHandler) {
+            @Override
+            protected void onReceiveResult(@Event int event, Bundle data) {
+                if (event == mEvent) {
+                    mEventReceivedVariable.open();
+                }
+            }
+        };
+    }
+
+    /**
+     * Waits for registered event or throws {@link TimeoutException}.
+     */
+    public void waitForEventOrThrow(long timeoutMs) throws TimeoutException {
+        // Avoid deadlocks
+        checkState(Thread.currentThread() != mHandler.getLooper().getThread());
+
+        if (!mEventReceivedVariable.block(timeoutMs)) {
+            throw new TimeoutException("Timed out waiting for event " + mEvent);
+        }
+    }
+}
diff --git a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
index b4d3ee9..94dd7fa 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
@@ -40,6 +40,7 @@
 import static android.server.wm.backgroundactivity.appa.Components.SendPendingIntentReceiver.IS_BROADCAST_EXTRA;
 import static android.server.wm.backgroundactivity.appa.Components.StartBackgroundActivityReceiver.START_ACTIVITY_DELAY_MS_EXTRA;
 import static android.server.wm.backgroundactivity.appb.Components.APP_B_FOREGROUND_ACTIVITY;
+import static android.server.wm.backgroundactivity.common.CommonComponents.EVENT_NOTIFIER_EXTRA;
 
 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
@@ -56,9 +57,13 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.os.ResultReceiver;
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
+import android.server.wm.backgroundactivity.common.CommonComponents.Event;
+import android.server.wm.backgroundactivity.common.EventReceiver;
 
+import androidx.annotation.Nullable;
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.FlakyTest;
 
@@ -69,6 +74,7 @@
 import org.junit.Test;
 
 import java.util.List;
+import java.util.concurrent.TimeoutException;
 
 /**
  * This class covers all test cases for starting/blocking background activities.
@@ -87,6 +93,12 @@
     private static final String TEST_PACKAGE_APP_A = "android.server.wm.backgroundactivity.appa";
     private static final String TEST_PACKAGE_APP_B = "android.server.wm.backgroundactivity.appb";
 
+    /**
+     * Tests can be executed as soon as the device has booted. When that happens the broadcast queue
+     * is long and it takes some time to process the broadcast we just sent.
+     */
+    private static final int BROADCAST_DELIVERY_TIMEOUT_MS = 25000;
+
     @Before
     public void setUp() throws Exception {
         mContext = InstrumentationRegistry.getContext();
@@ -115,15 +127,12 @@
     public void tearDown() throws Exception {
         stopTestPackage(TEST_PACKAGE_APP_A);
         stopTestPackage(TEST_PACKAGE_APP_B);
-        pressHomeButton();
+        launchHomeActivity();
         AppOpsUtils.reset(APP_A_PACKAGE_NAME);
-        mAmWmState.waitForHomeActivityVisible();
         runWithShellPermissionIdentity(() -> {
             runShellCommand("dpm remove-active-admin --user current "
                     + APP_A_SIMPLE_ADMIN_RECEIVER.flattenToString());
         });
-        // TODO(b/130169434): Remove it when app switch protection bug is fixed
-        SystemClock.sleep(5000);
     }
 
     @Test
@@ -136,16 +145,15 @@
         assertFalse("Should not able to launch background activity", result);
         assertTaskStack(null, APP_A_BACKGROUND_ACTIVITY);
 
-        // TODO(b/137134312): Bring this back once the stacks leakage issue is fixed
         // Make sure aborting activity starts won't have any empty task/stack leaks.
-        // List<ActivityManagerState.ActivityStack> stacks = mAmWmState.getAmState().getStacks();
-        // for (ActivityManagerState.ActivityStack stack : stacks) {
-        //     assertThat(stack.getTopTask()).isNotNull();
-        //     List<ActivityManagerState.ActivityTask> tasks = stack.getTasks();
-        //     for (ActivityManagerState.ActivityTask task : tasks) {
-        //         assertThat(task.getActivities().size()).isGreaterThan(0);
-        //     }
-        // }
+        List<ActivityManagerState.ActivityStack> stacks = mAmWmState.getAmState().getStacks();
+        for (ActivityManagerState.ActivityStack stack : stacks) {
+            assertThat(stack.getTopTask()).isNotNull();
+            List<ActivityManagerState.ActivityTask> tasks = stack.getTasks();
+            for (ActivityManagerState.ActivityTask task : tasks) {
+                assertThat(task.getActivities().size()).isGreaterThan(0);
+            }
+        }
     }
 
     @Test
@@ -207,7 +215,7 @@
         intent.setComponent(APP_A_FOREGROUND_ACTIVITY);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         intent.putExtra(LAUNCH_BACKGROUND_ACTIVITY_EXTRA, true);
-        intent.putExtra(START_ACTIVITY_FROM_FG_ACTIVITY_DELAY_MS_EXTRA, 7000);
+        intent.putExtra(START_ACTIVITY_FROM_FG_ACTIVITY_DELAY_MS_EXTRA, 2000);
         mContext.startActivity(intent);
         boolean result = waitForActivityFocused(ACTIVITY_FOCUS_TIMEOUT_MS,
                 APP_A_FOREGROUND_ACTIVITY);
@@ -215,10 +223,8 @@
         assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
 
         // The foreground activity will be paused but will attempt to restart itself in onPause()
-        pressHomeButton();
-        mAmWmState.waitForHomeActivityVisible();
+        pressHomeAndResumeAppSwitch();
 
-        waitToPreventAppSwitchProtection();
         result = waitForActivityFocused(APP_A_FOREGROUND_ACTIVITY);
         assertFalse("Previously foreground Activity should not be able to relaunch itself", result);
         result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
@@ -236,7 +242,7 @@
         intent.setComponent(APP_A_FOREGROUND_ACTIVITY);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         intent.putExtra(LAUNCH_BACKGROUND_ACTIVITY_EXTRA, true);
-        intent.putExtra(START_ACTIVITY_FROM_FG_ACTIVITY_DELAY_MS_EXTRA, 7000);
+        intent.putExtra(START_ACTIVITY_FROM_FG_ACTIVITY_DELAY_MS_EXTRA, 2000);
         intent.putExtra(START_ACTIVITY_FROM_FG_ACTIVITY_NEW_TASK_EXTRA, true);
         mContext.startActivity(intent);
         boolean result = waitForActivityFocused(ACTIVITY_FOCUS_TIMEOUT_MS,
@@ -245,10 +251,8 @@
         assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
 
         // The foreground activity will be paused but will attempt to restart itself in onPause()
-        pressHomeButton();
-        mAmWmState.waitForHomeActivityVisible();
+        pressHomeAndResumeAppSwitch();
 
-        waitToPreventAppSwitchProtection();
         result = waitForActivityFocused(APP_A_FOREGROUND_ACTIVITY);
         assertFalse("Previously foreground Activity should not be able to relaunch itself", result);
         result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
@@ -271,10 +275,8 @@
         assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
 
         // The foreground activity will be paused but will attempt to restart itself in onPause()
-        pressHomeButton();
-        mAmWmState.waitForHomeActivityVisible();
+        pressHomeAndResumeAppSwitch();
 
-        waitToPreventAppSwitchProtection();
         result = waitForActivityFocused(APP_A_FOREGROUND_ACTIVITY);
         assertFalse("Previously foreground Activity should not be able to relaunch itself", result);
         assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
@@ -310,9 +312,7 @@
         boolean result = waitForActivityFocused(APP_A_FOREGROUND_ACTIVITY);
         assertTrue("Not able to start foreground activity", result);
         assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
-        pressHomeButton();
-        mAmWmState.waitForHomeActivityVisible();
-        waitToPreventAppSwitchProtection();
+        pressHomeAndResumeAppSwitch();
 
         // The activity, now in the background, will attempt to start 2 activities in quick
         // succession
@@ -390,6 +390,7 @@
     }
 
     @Test
+    @FlakyTest(bugId = 141344170)
     public void testPendingIntentBroadcastTimeout_delay1s() throws Exception {
         assertPendingIntentBroadcastTimeoutTest(1000, true);
     }
@@ -401,9 +402,16 @@
 
     @Test
     public void testPendingIntentBroadcast_appBIsBackground() throws Exception {
+        EventReceiver receiver = new EventReceiver(
+                Event.APP_A_START_BACKGROUND_ACTIVITY_BROADCAST_RECEIVED);
+
         // Send pendingIntent from AppA to AppB, and the AppB launch the pending intent to start
         // activity in App A
-        sendPendingIntentBroadcast(0);
+        sendPendingIntentBroadcast(0, receiver.getNotifier());
+
+        // Waits for final hoop in AppA to start looking for activity, otherwise it could succeed
+        // if the broadcast took long time to get executed (which may happen after boot).
+        receiver.waitForEventOrThrow(BROADCAST_DELIVERY_TIMEOUT_MS);
         boolean result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
         assertFalse("Should not able to launch background activity", result);
         assertTaskStack(null, APP_A_BACKGROUND_ACTIVITY);
@@ -420,17 +428,29 @@
         String cmdResult = runShellCommand("dpm set-device-owner --user cur "
                 + APP_A_SIMPLE_ADMIN_RECEIVER.flattenToString());
         assertThat(cmdResult).contains("Success");
+        EventReceiver receiver = new EventReceiver(
+                Event.APP_A_START_BACKGROUND_ACTIVITY_BROADCAST_RECEIVED);
         Intent intent = new Intent();
         intent.setComponent(APP_A_START_ACTIVITY_RECEIVER);
+        intent.putExtra(EVENT_NOTIFIER_EXTRA, receiver.getNotifier());
+
         mContext.sendBroadcast(intent);
+
+        // Waits for final hoop in AppA to start looking for activity
+        receiver.waitForEventOrThrow(BROADCAST_DELIVERY_TIMEOUT_MS);
         boolean result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
         assertTrue("Not able to launch background activity", result);
         assertTaskStack(new ComponentName[]{APP_A_BACKGROUND_ACTIVITY}, APP_A_BACKGROUND_ACTIVITY);
     }
 
-    private void waitToPreventAppSwitchProtection() {
-        // Any activity launch will be blocked for 5s because of app switching protection.
-        SystemClock.sleep(7000);
+    private void pressHomeAndResumeAppSwitch() {
+        // Press home key to ensure stopAppSwitches is called because the last-stop-app-switch-time
+        // is a criteria of allowing background start.
+        pressHomeButton();
+        // Resume the stopped state (it won't affect last-stop-app-switch-time) so we don't need to
+        // wait extra time to prevent the next launch from being delayed.
+        resumeAppSwitches();
+        mAmWmState.waitForHomeActivityVisible();
     }
 
     private void assertTaskStack(ComponentName[] expectedComponents,
@@ -448,7 +468,8 @@
         }
     }
 
-    private void assertPendingIntentBroadcastTimeoutTest(int delayMs, boolean expectedResult) {
+    private void assertPendingIntentBroadcastTimeoutTest(int delayMs, boolean expectedResult)
+            throws TimeoutException {
         // Start AppB foreground activity
         Intent intent = new Intent();
         intent.setComponent(APP_B_FOREGROUND_ACTIVITY);
@@ -457,10 +478,15 @@
         boolean result = waitForActivityFocused(APP_B_FOREGROUND_ACTIVITY);
         assertTrue("Not able to start foreground Activity", result);
         assertTaskStack(new ComponentName[]{APP_B_FOREGROUND_ACTIVITY}, APP_B_FOREGROUND_ACTIVITY);
+        EventReceiver receiver = new EventReceiver(
+                Event.APP_A_START_BACKGROUND_ACTIVITY_BROADCAST_RECEIVED);
 
         // Send pendingIntent from AppA to AppB, and the AppB launch the pending intent to start
         // activity in App A
-        sendPendingIntentBroadcast(delayMs);
+        sendPendingIntentBroadcast(delayMs, receiver.getNotifier());
+
+        // Waits for final hoop in AppA to start looking for activity
+        receiver.waitForEventOrThrow(BROADCAST_DELIVERY_TIMEOUT_MS);
         result = waitForActivityFocused(ACTIVITY_FOCUS_TIMEOUT_MS + delayMs,
                 APP_A_BACKGROUND_ACTIVITY);
         assertEquals(expectedResult, result);
@@ -502,13 +528,14 @@
         mContext.sendBroadcast(intent);
     }
 
-    private void sendPendingIntentBroadcast(int delayMs) {
+    private void sendPendingIntentBroadcast(int delayMs, @Nullable ResultReceiver eventNotifier) {
         Intent intent = new Intent();
         intent.setComponent(APP_A_SEND_PENDING_INTENT_RECEIVER);
         intent.putExtra(IS_BROADCAST_EXTRA, true);
         if (delayMs > 0) {
             intent.putExtra(START_ACTIVITY_DELAY_MS_EXTRA, delayMs);
         }
+        intent.putExtra(EVENT_NOTIFIER_EXTRA, eventNotifier);
         mContext.sendBroadcast(intent);
     }
 }
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/res/layout/transition_destination_layout.xml b/tests/framework/base/windowmanager/res/layout/transition_destination_layout.xml
new file mode 100644
index 0000000..b7b4e3d
--- /dev/null
+++ b/tests/framework/base/windowmanager/res/layout/transition_destination_layout.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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+
+    <ImageView
+        android:id="@+id/transitionView"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:transitionName="sharedTransition"
+        android:src="@android:drawable/ic_media_play"/>
+
+</LinearLayout>
+
diff --git a/tests/framework/base/windowmanager/res/layout/transition_source_layout.xml b/tests/framework/base/windowmanager/res/layout/transition_source_layout.xml
new file mode 100644
index 0000000..7612430
--- /dev/null
+++ b/tests/framework/base/windowmanager/res/layout/transition_source_layout.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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+
+    <ImageView
+        android:id="@+id/transitionView"
+        android:layout_width="200dp"
+        android:layout_height="200dp"
+        android:transitionName="sharedTransition"
+        android:src="@android:drawable/ic_media_play" />
+
+</LinearLayout>
+
diff --git a/tests/framework/base/windowmanager/res/values/styles.xml b/tests/framework/base/windowmanager/res/values/styles.xml
index a3de74e..c315745 100644
--- a/tests/framework/base/windowmanager/res/values/styles.xml
+++ b/tests/framework/base/windowmanager/res/values/styles.xml
@@ -31,4 +31,7 @@
         <item name="android:windowContentTransitions">false</item>
         <item name="android:windowAnimationStyle">@null</item>
     </style>
+    <style name="window_activity_transitions" parent="@android:style/Theme.DeviceDefault">
+        <item name="android:windowActivityTransitions">true</item>
+    </style>
 </resources>
\ No newline at end of file
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..d11ae93 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,12 +249,11 @@
      * 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());
 
         // Test hot launch
-        pressHomeButton();
+        launchHomeActivityNoWait();
         waitForDeviceIdle(1000);
         mMetricsReader.checkpoint(); // clear out old logs
 
@@ -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..63947ba 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();
@@ -499,7 +511,7 @@
             waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
                     "Activity launched on default display must be focused");
 
-            // Press home button
+            // Start home activity directly
             launchHomeActivity();
 
             mAmWmState.assertHomeActivityVisible(true);
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/AmProfileTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AmProfileTests.java
index d08c9d2..24d2daa 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AmProfileTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AmProfileTests.java
@@ -107,7 +107,7 @@
         executeShellCommand(
                 getStartCmd(PROFILEABLE_APP_ACTIVITY, startActivityFirst, sampling, streaming));
         // Go to home screen and then warm start the activity to generate some interesting trace.
-        pressHomeButton();
+        launchHomeActivity();
         launchActivity(PROFILEABLE_APP_ACTIVITY);
 
         executeShellCommand(getStopProfileCmd(PROFILEABLE_APP_ACTIVITY));
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..fb39c49 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AmStartOptionsTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AmStartOptionsTests.java
@@ -20,7 +20,6 @@
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
 import static android.server.wm.ComponentNameUtils.getActivityName;
-import static android.server.wm.UiDeviceUtils.pressHomeButton;
 import static android.server.wm.app.Components.ENTRY_POINT_ALIAS_ACTIVITY;
 import static android.server.wm.app.Components.LAUNCHING_ACTIVITY;
 import static android.server.wm.app.Components.SINGLE_TASK_ACTIVITY;
@@ -34,8 +33,6 @@
 import android.content.ComponentName;
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.filters.FlakyTest;
-
 import org.junit.Test;
 
 /**
@@ -70,7 +67,6 @@
     }
 
     @Test
-    @FlakyTest
     public void testDashW_Indirect() throws Exception {
         testDashW(ENTRY_POINT_ALIAS_ACTIVITY, SINGLE_TASK_ACTIVITY);
     }
@@ -82,7 +78,7 @@
                 .setTargetActivity(TEST_ACTIVITY).execute();
 
         // Return to home
-        pressHomeButton();
+        launchHomeActivity();
 
         // Start LaunchingActivity again and finish TestActivity
         final int flags =
@@ -99,7 +95,7 @@
         startActivityAndVerifyResult(entryActivity, actualActivity, true);
 
         // Test warm start
-        pressHomeButton();
+        launchHomeActivity();
         startActivityAndVerifyResult(entryActivity, actualActivity, false);
 
         // Test "hot" start (app already in front)
@@ -120,4 +116,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
deleted file mode 100644
index 2126f83..0000000
--- a/tests/framework/base/windowmanager/src/android/server/wm/AnimationBackgroundTests.java
+++ /dev/null
@@ -1,88 +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 android.server.wm;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.server.wm.app.Components.ANIMATION_TEST_ACTIVITY;
-import static android.server.wm.app.Components.LAUNCHING_ACTIVITY;
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeFalse;
-
-import android.content.ComponentName;
-import android.platform.test.annotations.Presubmit;
-import android.server.wm.WindowManagerState.Display;
-
-import org.junit.Test;
-
-/**
- * Build/Install/Run:
- *     atest CtsWindowManagerDeviceTestCases:AnimationBackgroundTests
- */
-@Presubmit
-public class AnimationBackgroundTests extends ActivityManagerTestBase {
-
-    @Test
-    public void testAnimationBackground_duringAnimation() throws Exception {
-        launchActivityOnDisplay(LAUNCHING_ACTIVITY, DEFAULT_DISPLAY);
-        getLaunchActivityBuilder()
-                .setTargetActivity(ANIMATION_TEST_ACTIVITY)
-                .setWaitForLaunched(false)
-                .execute();
-
-        // Make sure we're testing an activity that runs on fullscreen display. This animation API
-        // doesn't make much sense in freeform displays.
-        assumeActivityNotInFreeformDisplay(ANIMATION_TEST_ACTIVITY);
-
-        // Make sure we are in the middle of the animation.
-        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());
-    }
-
-    @Test
-    public void testAnimationBackground_gone() throws Exception {
-        launchActivityOnDisplay(LAUNCHING_ACTIVITY, DEFAULT_DISPLAY);
-        getLaunchActivityBuilder().setTargetActivity(ANIMATION_TEST_ACTIVITY).execute();
-        mAmWmState.computeState(ANIMATION_TEST_ACTIVITY);
-        mAmWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY);
-
-        // Make sure we're testing an activity that runs on fullscreen display. This animation API
-        // doesn't make much sense in freeform displays.
-        assumeActivityNotInFreeformDisplay(ANIMATION_TEST_ACTIVITY);
-
-        assertFalse("window animation background needs to be gone", mAmWmState.getWmState()
-                .getStandardStackByWindowingMode(WINDOWING_MODE_FULLSCREEN)
-                .isWindowAnimationBackgroundSurfaceShowing());
-    }
-
-    private void assumeActivityNotInFreeformDisplay(ComponentName activity) throws Exception {
-        mAmWmState.waitForValidState(activity);
-        final int displayId = mAmWmState.getAmState().getDisplayByActivity(activity);
-        final Display display = mAmWmState.getWmState().getDisplay(displayId);
-        assumeFalse("Animation test activity is in freeform display. It may not run "
-                + "cross-task animations.", display.getWindowingMode() == WINDOWING_MODE_FREEFORM);
-    }
-}
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..9fffd6b 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());
 
@@ -259,7 +256,7 @@
         // Resize docked stack to fullscreen size. This will trigger activity relaunch with
         // non-empty override configuration corresponding to fullscreen size.
         separateTestJournal();
-        resizeStack(stack.mStackId, displayRect.left, displayRect.top, displayRect.width(),
+        resizeDockedStack(displayRect.left, displayRect.top, displayRect.width(),
                 displayRect.height());
 
         // Move activity back to fullscreen stack.
@@ -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());
 
@@ -308,7 +302,7 @@
         final int smallWidthPx = dpToPx(SMALL_WIDTH_DP, density);
         final int smallHeightPx = dpToPx(SMALL_HEIGHT_DP, density);
 
-        resizeStack(stack.mStackId, 0, 0, smallWidthPx, smallHeightPx);
+        resizeDockedStack(0, 0, smallWidthPx, smallHeightPx);
         mAmWmState.waitForValidState(
                 new WaitForValidActivityState.Builder(DIALOG_WHEN_LARGE_ACTIVITY)
                         .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
@@ -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/DragDropActivity.java b/tests/framework/base/windowmanager/src/android/server/wm/DragDropActivity.java
deleted file mode 100644
index be741f3..0000000
--- a/tests/framework/base/windowmanager/src/android/server/wm/DragDropActivity.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.wm;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-import android.server.wm.cts.R;
-
-public class DragDropActivity extends Activity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.drag_drop_layout);
-    }
-}
\ No newline at end of file
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DragDropTest.java b/tests/framework/base/windowmanager/src/android/server/wm/DragDropTest.java
index 2983eb8..bd054b9 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/DragDropTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DragDropTest.java
@@ -21,12 +21,14 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
 
 import android.app.Instrumentation;
 import android.app.UiAutomation;
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.pm.PackageManager;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
@@ -38,12 +40,10 @@
 import android.view.ViewGroup;
 
 import androidx.test.InstrumentationRegistry;
-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.runner.RunWith;
 
@@ -54,16 +54,12 @@
 import java.util.stream.IntStream;
 
 @RunWith(AndroidJUnit4.class)
-public class DragDropTest {
+public class DragDropTest extends WindowManagerTestBase {
     static final String TAG = "DragDropTest";
 
     final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
     final UiAutomation mAutomation = mInstrumentation.getUiAutomation();
 
-    @Rule
-    public ActivityTestRule<DragDropActivity> mActivityRule =
-            new ActivityTestRule<>(DragDropActivity.class);
-
     private DragDropActivity mActivity;
 
     private CountDownLatch mStartReceived;
@@ -307,22 +303,19 @@
         });
     }
 
-    private boolean init() {
-        // Only run for non-watch devices
-        if (mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
-            return false;
-        }
-        return true;
+    /** Checks if device type is watch. */
+    private boolean isWatchDevice() {
+        return mInstrumentation.getTargetContext().getPackageManager()
+                .hasSystemFeature(PackageManager.FEATURE_WATCH);
     }
 
     @Before
-    public void setUp() {
-        mActivity = mActivityRule.getActivity();
+    public void setUp() throws InterruptedException {
+        assumeFalse(isWatchDevice());
+        mActivity = startActivity(DragDropActivity.class);
+
         mStartReceived = new CountDownLatch(1);
         mEndReceived = new CountDownLatch(1);
-
-        // Wait for idle
-        mInstrumentation.waitForIdleSync();
     }
 
     @After
@@ -387,10 +380,6 @@
      */
     @Test
     public void testNoExtraEvents() throws Exception {
-        if (!init()) {
-            return;
-        }
-
         runOnMain(() -> {
             // Tell all views in layout to return false to all events, and log them.
             setRejectingHandlersOnTree(mActivity.findViewById(R.id.drag_drop_activity_main));
@@ -435,10 +424,6 @@
      */
     @Test
     public void testBlackHole() throws Exception {
-        if (!init()) {
-            return;
-        }
-
         runOnMain(() -> {
             // Accepting child.
             mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
@@ -483,10 +468,6 @@
      */
     @Test
     public void testEnterExit() throws Exception {
-        if (!init()) {
-            return;
-        }
-
         runOnMain(() -> {
             // The setup is same as for testBlackHole.
 
@@ -546,10 +527,6 @@
      */
     @Test
     public void testOverNowhere() throws Exception {
-        if (!init()) {
-            return;
-        }
-
         runOnMain(() -> {
             // Accepting child.
             mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
@@ -589,10 +566,6 @@
      */
     @Test
     public void testAcceptingGroupInTheMiddle() throws Exception {
-        if (!init()) {
-            return;
-        }
-
         runOnMain(() -> {
             // Set accepting handlers to the inner view and its 2 ancestors.
             mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
@@ -645,10 +618,6 @@
      */
     @Test
     public void testDrawableState() throws Exception {
-        if (!init()) {
-            return;
-        }
-
         runOnMain(() -> {
             // Set accepting handler for the inner view.
             mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
@@ -689,4 +658,46 @@
             assertFalse(drawableStateContains(R.id.inner, android.R.attr.state_drag_hovered));
         });
     }
-}
\ No newline at end of file
+
+    /**
+     * Tests if window is removing, it should not perform drag.
+     */
+    @Test
+    public void testNoDragIfWindowCantReceiveInput() throws InterruptedException {
+        injectMouse5(R.id.draggable, MotionEvent.ACTION_DOWN);
+
+        runOnMain(() -> {
+            // finish activity and start drag drop.
+            View v = mActivity.findViewById(R.id.draggable);
+            mActivity.finish();
+            assertFalse("Shouldn't start drag",
+                    v.startDragAndDrop(sClipData, new View.DragShadowBuilder(v), sLocalState, 0));
+        });
+
+        injectMouse5(R.id.draggable, MotionEvent.ACTION_UP);
+    }
+
+    /**
+     * Tests if there is no touch down, it should not perform drag.
+     */
+    @Test
+    public void testNoDragIfNoTouchDown() throws InterruptedException {
+        // perform a click.
+        injectMouse5(R.id.draggable, MotionEvent.ACTION_DOWN);
+        injectMouse5(R.id.draggable, MotionEvent.ACTION_UP);
+
+        runOnMain(() -> {
+            View v = mActivity.findViewById(R.id.draggable);
+            assertFalse("Shouldn't start drag",
+                v.startDragAndDrop(sClipData, new View.DragShadowBuilder(v), sLocalState, 0));
+        });
+    }
+
+    public static class DragDropActivity extends FocusableActivity {
+        @Override
+        protected void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            setContentView(R.layout.drag_drop_layout);
+        }
+    }
+}
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..c1f97a3 100755
--- a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java
@@ -23,7 +23,6 @@
 import static android.server.wm.ComponentNameUtils.getActivityName;
 import static android.server.wm.ComponentNameUtils.getWindowName;
 import static android.server.wm.UiDeviceUtils.pressBackButton;
-import static android.server.wm.UiDeviceUtils.pressHomeButton;
 import static android.server.wm.app.Components.BROADCAST_RECEIVER_ACTIVITY;
 import static android.server.wm.app.Components.DISMISS_KEYGUARD_ACTIVITY;
 import static android.server.wm.app.Components.DISMISS_KEYGUARD_METHOD_ACTIVITY;
@@ -533,7 +532,7 @@
     public void testUnoccludeRotationChange() throws Exception {
 
         // Go home now to make sure Home is behind Keyguard.
-        pressHomeButton();
+        launchHomeActivity();
         try (final LockScreenSession lockScreenSession = new LockScreenSession();
              final RotationSession rotationSession = new RotationSession()) {
             lockScreenSession.gotoKeyguard();
@@ -553,7 +552,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/ManifestLayoutTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ManifestLayoutTests.java
index 4086978..0632c4a 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ManifestLayoutTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ManifestLayoutTests.java
@@ -141,22 +141,22 @@
         getDisplayAndWindowState(activityName, true);
 
         final Rect containingRect = mWindowState.getContainingFrame();
-        final Rect appRect = mDisplay.getAppRect();
+        final Rect stableBounds = mDisplay.getStableBounds();
         final int expectedWidthPx, expectedHeightPx;
         // Evaluate the expected window size in px. If we're using fraction dimensions,
         // calculate the size based on the app rect size. Otherwise, convert the expected
         // size in dp to px.
         if (fraction) {
-            expectedWidthPx = (int) (appRect.width() * DEFAULT_WIDTH_FRACTION);
-            expectedHeightPx = (int) (appRect.height() * DEFAULT_HEIGHT_FRACTION);
+            expectedWidthPx = (int) (stableBounds.width() * DEFAULT_WIDTH_FRACTION);
+            expectedHeightPx = (int) (stableBounds.height() * DEFAULT_HEIGHT_FRACTION);
         } else {
             final int densityDpi = mDisplay.getDpi();
             expectedWidthPx = dpToPx(DEFAULT_WIDTH_DP, densityDpi);
             expectedHeightPx = dpToPx(DEFAULT_HEIGHT_DP, densityDpi);
         }
 
-        verifyFrameSizeAndPosition(
-                vGravity, hGravity, expectedWidthPx, expectedHeightPx, containingRect, appRect);
+        verifyFrameSizeAndPosition(vGravity, hGravity, expectedWidthPx, expectedHeightPx,
+                containingRect, stableBounds);
     }
 
     private void getDisplayAndWindowState(ComponentName activityName, boolean checkFocus)
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..e30da26 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
@@ -19,13 +19,17 @@
 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;
 import static android.server.wm.ActivityManagerState.STATE_STOPPED;
 import static android.server.wm.ComponentNameUtils.getActivityName;
+import static android.server.wm.UiDeviceUtils.pressHomeButton;
 import static android.server.wm.app.Components.ALT_LAUNCHING_ACTIVITY;
 import static android.server.wm.app.Components.BROADCAST_RECEIVER_ACTIVITY;
 import static android.server.wm.app.Components.LAUNCHING_ACTIVITY;
@@ -35,6 +39,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 +47,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 +56,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 +267,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");
@@ -492,6 +501,7 @@
     @Test
     public void testLaunchPendingActivityOnSecondaryDisplay() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            pressHomeButton();
             // Create new simulated display.
             final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
                     .createDisplay();
@@ -504,8 +514,7 @@
                     .putExtra(KEY_NEW_TASK, true);
             mContext.startActivity(intent, bundle);
 
-            // ActivityManagerTestBase.setup would press home key event, which would cause
-            // PhoneWindowManager.startDockOrHome to call AMS.stopAppSwitches.
+            // If home key was pressed, stopAppSwitches will be called.
             // Since this test case is not start activity from shell, it won't grant
             // STOP_APP_SWITCHES and this activity should be put into pending activity queue
             // and this activity should been launched after
@@ -848,4 +857,88 @@
                             SINGLE_TASK_INSTANCE_DISPLAY_ACTIVITY3));
         }
     }
+
+    @Test
+    public void testLaunchPendingIntentActivity() throws Exception {
+        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..4d4d120 100755
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayClientTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayClientTests.java
@@ -20,6 +20,7 @@
 import static android.server.wm.CommandSession.ActivityCallback.ON_CONFIGURATION_CHANGED;
 import static android.server.wm.CommandSession.ActivityCallback.ON_RESUME;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -28,6 +29,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,15 +38,17 @@
 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.View;
 import android.view.WindowManager;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.EditText;
 import android.widget.LinearLayout;
 
 import androidx.test.filters.FlakyTest;
+import androidx.test.filters.MediumTest;
 import androidx.test.rule.ActivityTestRule;
 
 import com.android.cts.mockime.ImeEventStream;
@@ -60,6 +64,7 @@
  *     atest CtsActivityManagerDeviceTestCases:MultiDisplayClientTests
  */
 @Presubmit
+@MediumTest
 public class MultiDisplayClientTests extends MultiDisplayTestBase {
 
     private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(10); // 10 seconds
@@ -73,13 +78,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 */);
     }
@@ -103,12 +106,13 @@
             // Move the activity to the new secondary display.
             separateTestJournal();
             final ActivityOptions launchOptions = ActivityOptions.makeBasic();
-            launchOptions.setLaunchDisplayId(newDisplay.mId);
+            final int displayId = newDisplay.mId;
+            launchOptions.setLaunchDisplayId(displayId);
             final Intent newDisplayIntent = new Intent(mContext, activityClass);
             newDisplayIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
             getInstrumentation().getTargetContext().startActivity(newDisplayIntent,
                     launchOptions.toBundle());
-            waitAndAssertTopResumedActivity(activityName, newDisplay.mId,
+            waitAndAssertTopResumedActivity(activityName, displayId,
                     "Activity moved to secondary display must be focused");
 
             if (handlesConfigChange) {
@@ -119,48 +123,30 @@
                 waitAndAssertResume(activityName);
                 activity = activityTestRule.getActivity();
             }
-            final String message = "Display id must be updated";
-            assertEquals(message, newDisplay.mId, activity.getDisplayId());
-            assertEquals(message, newDisplay.mId, activity.getDisplay().getDisplayId());
+
+            final String suffix = " must be updated.";
+            assertEquals("Activity#getDisplayId()" + suffix, displayId, activity.getDisplayId());
+            assertEquals("Activity#getDisplay" + suffix,
+                    displayId, activity.getDisplay().getDisplayId());
+
             final WindowManager wm = activity.getWindowManager();
-            assertEquals(message, newDisplay.mId, wm.getDefaultDisplay().getDisplayId());
+            assertEquals("WM#getDefaultDisplay()" + suffix,
+                    displayId, wm.getDefaultDisplay().getDisplayId());
+
+            final View view = activity.getWindow().getDecorView();
+            assertEquals("View#getDisplay()" + suffix,
+                    displayId, view.getDisplay().getDisplayId());
         }
     }
 
-    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));
-    }
-
-    private void waitAndAssertResume(ComponentName activityName) {
-        mAmWmState.waitForWithAmState((state) ->
-                getCallbackCount(activityName, ON_RESUME) == 1, "waitForResume");
-        assertEquals("Must be resumed once", 1, getCallbackCount(activityName, ON_RESUME));
-    }
-
-    private int getCallbackCount(ComponentName activityName,
-            CommandSession.ActivityCallback callback) {
-        final ActivityLifecycleCounts lifecycles = new ActivityLifecycleCounts(activityName);
-        return lifecycles.getCount(callback);
-    }
-
     @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);
-        }
+        testDisplayIdUpdateWhenImeMove(ClientTestActivity.class);
     }
 
     @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);
-        }
+        testDisplayIdUpdateWhenImeMove(NoRelaunchActivity.class);
     }
 
     private void testDisplayIdUpdateWhenImeMove(Class<? extends ImeTestActivity> activityClass)
@@ -196,13 +182,13 @@
         expectEvent(stream, event -> "showSoftInput".equals(event.getEventName()), TIMEOUT);
         mAmWmState.waitAndAssertImeWindowShownOnDisplay(targetDisplayId);
 
-        final int displayId = expectCommand(stream, imeSession.callGetDisplayId(), TIMEOUT)
+        final int imeDisplayId = expectCommand(stream, imeSession.callGetDisplayId(), TIMEOUT)
                 .getReturnIntegerValue();
-        assertEquals("Display ID must match", targetDisplayId, displayId);
+        assertEquals("IME#getDisplayId() must match when IME move.",
+                targetDisplayId, imeDisplayId);
     }
 
     @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.
@@ -215,10 +201,93 @@
             final InputMethodManager imm =
                     newDisplayContext.getSystemService(InputMethodManager.class);
 
-            assertEquals(newDisplay.mId, imm.getDisplayId());
+            assertEquals("IMM#getDisplayId() must match.", newDisplay.mId, imm.getDisplayId());
         }
     }
 
+    @Test
+    public void testViewGetDisplayOnPrimaryDisplay() throws Exception {
+        testViewGetDisplay(true /* isPrimary */);
+    }
+
+    @Test
+    public void testViewGetDisplayOnSecondaryDisplay() throws Exception {
+        testViewGetDisplay(false /* isPrimary */);
+    }
+
+    private void testViewGetDisplay(boolean isPrimary) throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession();
+             final TestActivitySession<ClientTestActivity> activitySession =
+                     new TestActivitySession<>()) {
+
+            final ActivityManagerState.ActivityDisplay newDisplay = virtualDisplaySession
+                    .setSimulateDisplay(true).createDisplay();
+            final int displayId = (isPrimary) ? DEFAULT_DISPLAY : newDisplay.mId;
+
+            separateTestJournal();
+            activitySession.launchTestActivityOnDisplaySync(
+                    ClientTestActivity.class, displayId);
+
+            final Activity activity = activitySession.getActivity();
+            final ComponentName activityName = activity.getComponentName();
+
+            waitAndAssertTopResumedActivity(activityName, displayId,
+                    "Activity launched on display:" + displayId + " must be focused");
+
+            // Test View#getdisplay() from activity
+            final View view = activity.getWindow().getDecorView();
+            assertEquals("View#getDisplay() must match.",
+                    displayId, view.getDisplay().getDisplayId());
+
+            final int[] resultDisplayId = new int[] { INVALID_DISPLAY };
+            activitySession.runOnMainAndAssertWithTimeout(
+                    () -> {
+                        // Test View#getdisplay() from WM#addView()
+                        final WindowManager wm = activity.getWindowManager();
+                        final View addedView = new View(activity);
+                        wm.addView(addedView, new WindowManager.LayoutParams());
+
+                        // Get display ID from callback in case the added view has not be attached.
+                        addedView.addOnAttachStateChangeListener(
+                                new View.OnAttachStateChangeListener() {
+                                    @Override
+                                    public void onViewAttachedToWindow(View view) {
+                                        resultDisplayId[0] = view.getDisplay().getDisplayId();
+                                    }
+
+                                    @Override
+                                    public void onViewDetachedFromWindow(View view) {}
+                                });
+
+                        return displayId == resultDisplayId[0];
+                    }, TIMEOUT, "Display from added view must match. "
+                            + "Should be display:" + displayId
+                            + ", but was display:" + resultDisplayId[0]
+            );
+
+        }
+    }
+
+    private void waitAndAssertConfigurationChange(ComponentName activityName) {
+        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) {
+        assertTrue("Must be resumed once",
+                mAmWmState.waitForWithAmState(
+                        state -> getCallbackCount(activityName, ON_RESUME) == 1,
+                        activityName + " performs resume"));
+    }
+
+    private static int getCallbackCount(ComponentName activityName,
+            CommandSession.ActivityCallback callback) {
+        final ActivityLifecycleCounts lifecycles = new ActivityLifecycleCounts(activityName);
+        return lifecycles.getCount(callback);
+    }
+
     public static class ClientTestActivity extends ImeTestActivity { }
 
     public static class NoRelaunchActivity extends ImeTestActivity { }
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..580af40 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayLockedKeyguardTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayLockedKeyguardTests.java
@@ -56,7 +56,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()) {
@@ -137,6 +136,7 @@
         }
     }
 
+    @FlakyTest(bugId = 141674516)
     @Test
     public void testDismissKeyguard_whileOccluded_secondaryDisplay() throws Exception {
         try (final LockScreenSession lockScreenSession =
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/MultiDisplayPrivateDisplayTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPrivateDisplayTests.java
new file mode 100644
index 0000000..ee278d8
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPrivateDisplayTests.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.wm;
+
+import static android.server.wm.app.Components.LAUNCH_BROADCAST_RECEIVER;
+import static android.server.wm.app.Components.LaunchBroadcastReceiver.LAUNCH_BROADCAST_ACTION;
+import static android.server.wm.app.Components.TEST_ACTIVITY;
+import static android.view.Display.FLAG_PRIVATE;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.platform.test.annotations.Presubmit;
+import android.server.wm.ActivityManagerState.ActivityDisplay;
+import android.server.wm.TestJournalProvider.TestJournalContainer;
+import android.server.wm.WindowManagerState.Display;
+import android.util.Log;
+import android.view.View;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import java.util.ArrayList;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ *     atest CtsWindowManagerDeviceTestCases:MultiDisplayPrivateDisplayTests
+ *
+ * Tests if be allowed to launch/access an activity on private display
+ * in multi-display environment.
+ */
+@Presubmit
+public class MultiDisplayPrivateDisplayTests extends MultiDisplayTestBase {
+    private static final String TAG = "MultiDisplayPrivateDisplayTests";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final String INTERNAL_SYSTEM_WINDOW =
+            "android.permission.INTERNAL_SYSTEM_WINDOW";
+    private ArrayList<Integer> mPrivateDisplayIds = new ArrayList<>();
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        assumeTrue(supportsMultiDisplay());
+        findPrivateDisplays();
+        assumeFalse("Skipping test: no physical private display found.",
+                mPrivateDisplayIds.isEmpty());
+    }
+
+    /** Saves physical private displays in mPrivateDisplayIds */
+    private void findPrivateDisplays() {
+        mPrivateDisplayIds.clear();
+        mAmWmState.computeState(true);
+
+        for (ActivityDisplay activityDisplay: getDisplaysStates()) {
+            int displayId = activityDisplay.mId;
+            Display display = mAmWmState.getWmState().getDisplay(displayId);
+            if ((display.getFlags() & FLAG_PRIVATE) != 0) {
+                mPrivateDisplayIds.add(displayId);
+            }
+        }
+    }
+
+    /**
+     * Tests launching an activity on a private display without special permission must not be
+     * allowed.
+     */
+    @Test
+    public void testCantLaunchOnPrivateDisplay() throws Exception {
+        // try on each private display
+        for (int displayId: mPrivateDisplayIds) {
+            separateTestJournal();
+
+            getLaunchActivityBuilder()
+                .setDisplayId(displayId)
+                .setTargetActivity(TEST_ACTIVITY)
+                .execute();
+
+            assertSecurityExceptionFromActivityLauncher();
+
+            mAmWmState.computeState(TEST_ACTIVITY);
+            assertFalse("Activity must not be launched on a private display",
+                mAmWmState.getAmState().containsActivity(TEST_ACTIVITY));
+        }
+    }
+
+    /**
+     * Tests
+     * {@link android.app.ActivityManager#isActivityStartAllowedOnDisplay(Context, int, Intent)}
+     * call to start an activity on private display is not allowed without special permission
+     */
+    @Test
+    public void testCantAccessPrivateDisplay() throws Exception {
+        final ActivityManager activityManager =
+            (ActivityManager) mTargetContext.getSystemService(Context.ACTIVITY_SERVICE);
+        final Intent intent = new Intent(Intent.ACTION_VIEW).setComponent(TEST_ACTIVITY);
+
+        for (int displayId: mPrivateDisplayIds) {
+            assertFalse(activityManager.isActivityStartAllowedOnDisplay(mTargetContext,
+                displayId, intent));
+        }
+    }
+
+    /**
+     * Tests
+     * {@link android.app.ActivityManager#isActivityStartAllowedOnDisplay(Context, int, Intent)}
+     * for a private display with INTERNAL_SYSTEM_WINDOW permission.
+     */
+    @Test
+    public void testCanAccessPrivateDisplayWithInternalPermission() throws Exception {
+        final ActivityManager activityManager =
+            (ActivityManager) mTargetContext.getSystemService(Context.ACTIVITY_SERVICE);
+        final Intent intent = new Intent(Intent.ACTION_VIEW)
+            .setComponent(TEST_ACTIVITY);
+
+        for (int displayId: mPrivateDisplayIds) {
+            SystemUtil.runWithShellPermissionIdentity(() ->
+                assertTrue(activityManager.isActivityStartAllowedOnDisplay(mTargetContext,
+                    displayId, intent)), INTERNAL_SYSTEM_WINDOW);
+        }
+    }
+}
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..9ff341c 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,19 +687,6 @@
         }
     }
 
-    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");
-    }
-
     /**
      * Test that only private virtual display can show content with insecure keyguard.
      */
@@ -727,7 +709,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 +752,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 +782,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 +828,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 +849,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..53e3a93 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,11 +212,10 @@
      * 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.
-            mAmWmState.waitAndAssertNavBarShownOnDisplay(DEFAULT_DISPLAY);
+            // Wait for system decoration showing and record current nav states.
+            mAmWmState.waitForHomeActivityVisible();
             final List<WindowState> expected = mAmWmState.getWmState().getAllNavigationBarStates();
 
             externalDisplaySession.setPublicDisplay(true)
@@ -236,11 +230,10 @@
      * 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.
-            mAmWmState.waitAndAssertNavBarShownOnDisplay(DEFAULT_DISPLAY);
+            // Wait for system decoration showing and record current nav states.
+            mAmWmState.waitForHomeActivityVisible();
             final List<WindowState> expected = mAmWmState.getWmState().getAllNavigationBarStates();
 
             externalDisplaySession.setPublicDisplay(false)
@@ -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..a805e7e 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,16 @@
         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);
+        });
+    }
+
+    void assertSecurityExceptionFromActivityLauncher() {
+        waitForOrFail("SecurityException from " + ActivityLauncher.TAG,
+            ActivityLauncher::hasCaughtSecurityException);
     }
 
     /**
@@ -462,7 +457,7 @@
 
     // TODO(b/112837428): Merge into VirtualDisplaySession when all usages are migrated.
     protected class VirtualDisplayLauncher extends VirtualDisplaySession {
-        private final ActivitySessionClient mActivitySessionClient = new ActivitySessionClient();
+        private final ActivitySessionClient mActivitySessionClient = ActivitySessionClient.create();
 
         ActivitySession launchActivityOnDisplay(ComponentName activityName,
                 ActivityDisplay display) {
@@ -510,6 +505,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 +531,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 +550,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 +564,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 +585,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..96649f9 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
@@ -16,6 +16,7 @@
 
 package android.server.wm;
 
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -64,7 +65,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 +108,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 +216,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 +225,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 +244,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 +253,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 +263,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();
 
@@ -302,7 +291,7 @@
         final int stackId = getPinnedStack().mStackId;
         final int top = 0;
         final int left = displayRect.width() - 200;
-        resizeStack(stackId, left, top, left + 500, top + 500);
+        resizePinnedStack(stackId, left, top, left + 500, top + 500);
 
         // Ensure that the surface insets are not negative
         windowState = getWindowState(PIP_ACTIVITY);
@@ -640,7 +629,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 +663,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 +678,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,
@@ -816,6 +802,9 @@
         SystemUtil.runWithShellPermissionIdentity(() -> {
             try {
                 mAtm.startSystemLockTaskMode(task.mTaskId);
+                waitForOrFail("Task in lock mode", () -> {
+                    return mAm.getLockTaskModeState() != LOCK_TASK_MODE_NONE;
+                });
                 mBroadcastActionTrigger.doAction(ACTION_ENTER_PIP);
                 waitForEnterPip(PIP_ACTIVITY);
                 assertPinnedStackDoesNotExist();
@@ -827,7 +816,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
@@ -1024,7 +1013,8 @@
         // got resumed.
         separateTestJournal();
         SystemUtil.runWithShellPermissionIdentity(
-                () -> mAtm.resizeStack(stackId, new Rect(20, 20, 500, 500), true /* animate */));
+                () -> mAtm.resizePinnedStack(stackId, new Rect(20, 20, 500, 500),
+                        true /* animate */));
         mBroadcastActionTrigger.doAction(TEST_ACTIVITY_ACTION_FINISH_SELF);
         mAmWmState.waitFor((amState, wmState) ->
                         !amState.containsActivity(TRANSLUCENT_TEST_ACTIVITY),
@@ -1123,6 +1113,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 +1136,6 @@
     }
 
     /** Test that reported display size corresponds to fullscreen after exiting PiP. */
-    @FlakyTest
     @Test
     public void testDisplayMetricsPinUnpin() throws Exception {
         separateTestJournal();
@@ -1180,6 +1170,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 +1219,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);
@@ -1278,7 +1268,7 @@
         } else {
             offsetBoundsOut.offset(0, -offsetY);
         }
-        resizeStack(stack.mStackId, offsetBoundsOut.left, offsetBoundsOut.top,
+        resizePinnedStack(stack.mStackId, offsetBoundsOut.left, offsetBoundsOut.top,
                 offsetBoundsOut.right, offsetBoundsOut.bottom);
     }
 
@@ -1413,7 +1403,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 +1413,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 +1435,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 +1444,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..a4857ba 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
@@ -488,8 +479,7 @@
                 // Go home and check the app transition
                 assertNotEquals(TRANSIT_WALLPAPER_OPEN,
                         mAmWmState.getWmState().getDefaultDisplayLastTransition());
-                pressHomeButton();
-                mAmWmState.waitForHomeActivityVisible();
+                launchHomeActivity();
                 mAmWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY);
 
                 assertEquals(TRANSIT_WALLPAPER_OPEN,
@@ -498,7 +488,6 @@
         }
     }
 
-    @FlakyTest(bugId = 73813034)
     @Test
     public void testFinishDockActivityWhileMinimized() throws Exception {
         launchActivityInDockStackAndMinimize(TEST_ACTIVITY);
@@ -552,7 +541,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 +569,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testDifferentProcessActivityResumedPreQ() {
         launchActivitiesInSplitScreen(
                 getLaunchActivityBuilder().setTargetActivity(SDK_27_TEST_ACTIVITY),
@@ -655,7 +642,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testStackListOrderOnSplitScreenDismissed() throws Exception {
         launchActivitiesInSplitScreen(
                 getLaunchActivityBuilder().setTargetActivity(DOCKED_ACTIVITY),
@@ -686,7 +672,7 @@
     private void launchActivityInDockStackAndMinimize(ComponentName activityName, int createMode)
             throws Exception {
         launchActivityInSplitScreenWithRecents(activityName, createMode);
-        pressHomeButton();
+        launchHomeActivityNoWait();
         waitForAndAssertDockMinimized();
     }
 
@@ -710,11 +696,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..86e3578 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java
@@ -16,16 +16,28 @@
 
 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.LAUNCHING_ACTIVITY;
+import static android.server.wm.app.Components.NO_RELAUNCH_ACTIVITY;
 import static android.server.wm.app.Components.TEST_ACTIVITY;
+import static android.server.wm.app.Components.TRANSLUCENT_ACTIVITY;
+import static android.server.wm.app.Components.TestActivity.EXTRA_INTENTS;
+import static android.server.wm.app.Components.TestActivity.COMMAND_START_ACTIVITIES;
 import static android.server.wm.app27.Components.SDK_27_LAUNCHING_ACTIVITY;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 
 import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
 import android.platform.test.annotations.Presubmit;
+import android.server.wm.CommandSession.ActivitySession;
+import android.server.wm.CommandSession.ActivitySessionClient;
 
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.filters.FlakyTest;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -39,14 +51,15 @@
 
     @Rule
     public final ActivityTestRule<TestActivity2> mTestActivity2Rule =
-            new ActivityTestRule<>(TestActivity2.class);
+            new ActivityTestRule<>(TestActivity2.class, false /* initialTouchMode */,
+                    false /* launchActivity */);
 
     /**
      * Ensures {@link Activity} can only be launched from an {@link Activity}
      * {@link android.content.Context}.
      */
     @Test
-    public void testStartActivityContexts() throws Exception {
+    public void testStartActivityContexts() {
         // Launch Activity from application context.
         getLaunchActivityBuilder()
                 .setTargetActivity(TEST_ACTIVITY)
@@ -85,14 +98,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)
@@ -103,6 +139,46 @@
                 TEST_ACTIVITY);
     }
 
+    /**
+     * <pre>
+     * Assume there are 3 activities (X, Y, Z) have different task affinities:
+     * 1. Activity X started.
+     * 2. X launches 2 activities (Y with NEW_TASK, Z) by {@link Activity#startActivities}.
+     * Expect the result should be 2 tasks: [X] and [Y, Z].
+     * </pre>
+     */
+    @Test
+    public void testStartActivitiesInNewAndSameTask() {
+        final ActivitySession activity = ActivitySessionClient.create().startActivity(
+                getLaunchActivityBuilder().setUseInstrumentation()
+                        .setTargetActivity(TEST_ACTIVITY));
+
+        final Intent[] intents = {
+                new Intent().setComponent(NO_RELAUNCH_ACTIVITY)
+                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
+                new Intent().setComponent(LAUNCHING_ACTIVITY)
+        };
+
+        final Bundle intentBundle = new Bundle();
+        intentBundle.putParcelableArray(EXTRA_INTENTS, intents);
+        // The {@link Activity#startActivities} cannot be called from the instrumentation
+        // package because the implementation (given by test runner) may be overridden.
+        activity.sendCommand(COMMAND_START_ACTIVITIES, intentBundle);
+
+        // The {@code intents} are started, wait for the last (top) activity to be ready and then
+        // verify their task ids.
+        mAmWmState.computeState(intents[1].getComponent());
+        final ActivityManagerState amState = mAmWmState.getAmState();
+        final int callerTaskId = amState.getTaskByActivity(TEST_ACTIVITY).getTaskId();
+        final int i0TaskId = amState.getTaskByActivity(intents[0].getComponent()).getTaskId();
+        final int i1TaskId = amState.getTaskByActivity(intents[1].getComponent()).getTaskId();
+
+        assertNotEquals("The activities started by startActivities() should have a different task"
+                + " from their caller activity", callerTaskId, i0TaskId);
+        assertEquals("The activities started by startActivities() should be put in the same task",
+                i0TaskId, i1TaskId);
+    }
+
     public static class TestActivity2 extends Activity {
     }
 }
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..14a7059 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java
@@ -39,8 +39,6 @@
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.filters.FlakyTest;
-
 import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
@@ -58,7 +56,6 @@
  *     atest CtsWindowManagerDeviceTestCases:TransitionSelectionTests
  */
 @Presubmit
-@FlakyTest(bugId = 71792333)
 public class TransitionSelectionTests extends ActivityManagerTestBase {
 
     @Override
@@ -118,7 +115,6 @@
                 false /*slowStop*/, TRANSIT_TASK_OPEN);
     }
 
-    @FlakyTest(bugId = 71792333)
     @Test
     public void testCloseTask_NeitherWallpaper() {
         testCloseTask(false /*bottomWallpaper*/, false /*topWallpaper*/,
@@ -177,7 +173,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*/,
@@ -223,7 +218,6 @@
                 TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE);
     }
 
-    @FlakyTest(bugId = 71792333)
     @Test
     public void testCloseTask_BottomWallpaper_Translucent() {
         testCloseTaskTranslucent(true /*bottomWallpaper*/, false /*topWallpaper*/,
@@ -306,13 +300,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..390de30 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java
@@ -16,10 +16,12 @@
 
 package android.server.wm;
 
-import static android.server.wm.UiDeviceUtils.pressHomeButton;
+import static android.server.wm.ActivityManagerTestBase.launchHomeActivityNoWait;
 import static android.server.wm.UiDeviceUtils.pressUnlockButton;
 import static android.server.wm.UiDeviceUtils.pressWakeupButton;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static org.junit.Assert.assertEquals;
 
 import android.app.Activity;
@@ -31,8 +33,6 @@
 import android.view.WindowInsets;
 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 +48,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);
@@ -64,9 +63,9 @@
     public void setUp() {
         pressWakeupButton();
         pressUnlockButton();
-        pressHomeButton();
+        launchHomeActivityNoWait();
 
-        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mInstrumentation = getInstrumentation();
         mActivity = mActivityRule.launchActivity(null);
         mInstrumentation.waitForIdleSync();
         mClickCount = 0;
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowManagerTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowManagerTestBase.java
index 3490e00..10358dc 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowManagerTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowManagerTestBase.java
@@ -17,12 +17,12 @@
 package android.server.wm;
 
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.server.wm.UiDeviceUtils.pressHomeButton;
+import static android.server.wm.ActivityManagerTestBase.launchHomeActivityNoWait;
 import static android.server.wm.UiDeviceUtils.pressUnlockButton;
 import static android.server.wm.UiDeviceUtils.pressWakeupButton;
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static org.junit.Assert.assertEquals;
 
@@ -42,7 +42,7 @@
     public void setupBase() {
         pressWakeupButton();
         pressUnlockButton();
-        pressHomeButton();
+        launchHomeActivityNoWait();
     }
 
     static <T extends FocusableActivity> T startActivity(Class<T> cls) throws InterruptedException {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowlessWmTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowlessWmTests.java
new file mode 100644
index 0000000..f061aeb
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowlessWmTests.java
@@ -0,0 +1,177 @@
+/*
+ * 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.UiDeviceUtils.pressHomeButton;
+import static android.server.wm.UiDeviceUtils.pressUnlockButton;
+import static android.server.wm.UiDeviceUtils.pressWakeupButton;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.SurfaceHolder;
+import android.view.SurfaceHolder.Callback;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.WindowlessViewRoot;
+import android.widget.FrameLayout;
+import android.widget.Button;
+
+import android.view.SurfaceView;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.CtsTouchUtils;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Ensure end-to-end functionality of the WindowlessWindowManager.
+ *
+ * Build/Install/Run:
+ *     atest CtsWindowManagerDeviceTestCases:WindowlessWmTests
+ */
+@Presubmit
+@FlakyTest
+public class WindowlessWmTests implements SurfaceHolder.Callback {
+    private final ActivityTestRule<Activity> mActivityRule = new ActivityTestRule<>(Activity.class);
+
+    private Instrumentation mInstrumentation;
+    private Activity mActivity;
+    private SurfaceView mSurfaceView;
+
+    private WindowlessViewRoot mVr;
+    private View mEmbeddedView;
+
+    private boolean mClicked = false;
+
+    /*
+     * Configurable state to control how the surfaceCreated callback
+     * will initialize the embedded view hierarchy.
+     */
+    int mEmbeddedViewWidth = 100;
+    int mEmbeddedViewHeight = 100;
+
+    private static final int DEFAULT_SURFACE_VIEW_WIDTH = 100;
+    private static final int DEFAULT_SURFACE_VIEW_HEIGHT = 100;
+
+    @Before
+    public void setUp() {
+        pressWakeupButton();
+        pressUnlockButton();
+        pressHomeButton();
+
+        mClicked = false;
+
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mActivity = mActivityRule.launchActivity(null);
+        mInstrumentation.waitForIdleSync();
+    }
+
+    private void addSurfaceView(int width, int height) throws Throwable {
+        mActivityRule.runOnUiThread(() -> {
+            final FrameLayout content = new FrameLayout(mActivity);
+            mSurfaceView = new SurfaceView(mActivity);
+            mSurfaceView.setZOrderOnTop(true);
+            content.addView(mSurfaceView, new FrameLayout.LayoutParams(
+                width, height, Gravity.LEFT | Gravity.TOP));
+            mActivity.setContentView(content, new ViewGroup.LayoutParams(width, height));
+            mSurfaceView.getHolder().addCallback(this);
+        });
+    }
+
+    private void addViewToSurfaceView(SurfaceView sv, View v, int width, int height) {
+        mVr = new WindowlessViewRoot(mActivity, mActivity.getDisplay(),
+                sv.getSurfaceControl());
+        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height,
+                WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.OPAQUE);
+        mVr.addView(v, lp);
+    }
+
+    @Override
+    public void surfaceCreated(SurfaceHolder holder) {
+        addViewToSurfaceView(mSurfaceView, mEmbeddedView,
+                mEmbeddedViewWidth, mEmbeddedViewHeight);
+    }
+
+    @Override
+    public void surfaceDestroyed(SurfaceHolder holder) {
+    }
+
+    @Override
+    public void surfaceChanged(SurfaceHolder holder, int format, int width,
+            int height) {
+    }
+
+    @Test
+    public void testEmbeddedViewReceivesInput() throws Throwable {
+        mEmbeddedView = new Button(mActivity);
+        mEmbeddedView.setOnClickListener((View v) -> {
+            mClicked = true;
+        });
+
+        addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
+        mInstrumentation.waitForIdleSync();
+
+        CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
+        assertTrue(mClicked);
+    }
+
+    @Test
+    public void testEmbeddedViewResizes() throws Throwable {
+        mEmbeddedView = new Button(mActivity);
+        mEmbeddedView.setOnClickListener((View v) -> {
+            mClicked = true;
+        });
+
+        final int bigEdgeLength = mEmbeddedViewWidth * 3;
+
+        // We make the SurfaceView more than twice as big as the embedded view
+        // so that a touch in the middle of the SurfaceView won't land
+        // on the embedded view.
+        addSurfaceView(bigEdgeLength, bigEdgeLength);
+        mInstrumentation.waitForIdleSync();
+
+        CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
+        assertFalse(mClicked);
+
+        mActivityRule.runOnUiThread(() -> {
+                WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                        bigEdgeLength, bigEdgeLength,
+                        WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.OPAQUE);
+                mVr.relayout(lp);
+        });
+        mInstrumentation.waitForIdleSync();
+
+        // But after the click should hit.
+        CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
+        assertTrue(mClicked);
+    }
+}
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..a2eeac8 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
@@ -37,12 +37,12 @@
      */
     public void cleanUp(List<ComponentName> activitiesInUsedInTest) throws Exception {
         super.tearDown();
-        UiDeviceUtils.pressHomeButton();
+        launchHomeActivityNoWait();
         removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
 
         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..67b4f7f 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,9 @@
 
 package android.server.wm.lifecycle;
 
+import static android.app.Activity.RESULT_OK;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 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;
@@ -36,6 +39,7 @@
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
 
 import android.app.Activity;
+import android.app.ActivityOptions;
 import android.app.PictureInPictureParams;
 import android.content.ComponentName;
 import android.content.Context;
@@ -46,8 +50,13 @@
 import android.os.Handler;
 import android.server.wm.MultiDisplayTestBase;
 import android.server.wm.lifecycle.LifecycleLog.ActivityCallback;
+import android.transition.Transition;
+import android.transition.TransitionListenerAdapter;
 import android.util.Pair;
 
+import android.server.wm.cts.R;
+
+import androidx.annotation.Nullable;
 import androidx.test.rule.ActivityTestRule;
 
 import org.junit.Before;
@@ -58,8 +67,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 +128,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;
@@ -136,8 +155,16 @@
 
     /** Launch an activity given a class. */
     protected Activity launchActivity(Class<? extends Activity> activityClass) {
+        return launchActivity(activityClass, null /* options */);
+    }
+
+    /** Launch an activity given a class and options. */
+    protected Activity launchActivity(Class<? extends Activity> activityClass,
+            @Nullable ActivityOptions options) {
         final Intent intent = new Intent(mTargetContext, activityClass);
-        return getInstrumentation().startActivitySync(intent);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        return getInstrumentation().startActivitySync(intent,
+                options != null ? options.toBundle() : null);
     }
 
     /**
@@ -322,6 +349,10 @@
         }
     }
 
+    // Just another callback tracking activity, nothing special.
+    public static class SecondCallbackTrackingActivity extends CallbackTrackingActivity {
+    }
+
     // Translucent callback tracking test activity
     public static class TranslucentCallbackTrackingActivity extends CallbackTrackingActivity {
     }
@@ -339,6 +370,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 +381,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 +406,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 +435,41 @@
                 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 {
+        static final String EXTRA_LAUNCH_ACTIVITY = "extra_launch_activity";
+        static final String EXTRA_NEW_TASK = "extra_new_task";
+
+        @Override
+        protected void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+
+            if (getIntent().getBooleanExtra(EXTRA_LAUNCH_ACTIVITY, false)) {
+                final Intent intent = new Intent(this, CallbackTrackingActivity.class);
+                if (getIntent().getBooleanExtra(EXTRA_NEW_TASK, false)) {
+                    intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK);
+                }
+                startActivity(intent);
+            }
+        }
     }
 
     /** Test activity that can call {@link Activity#recreate()} if requested in a new intent. */
@@ -448,6 +552,41 @@
         }
     }
 
+    public static class DifferentAffinityActivity extends LifecycleTrackingActivity {
+    }
+
+    public static class TransitionSourceActivity extends LifecycleTrackingActivity {
+        @Override
+        protected void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            setContentView(R.layout.transition_source_layout);
+        }
+
+        void launchActivityWithTransition() {
+            final ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this,
+                    findViewById(R.id.transitionView), "sharedTransition");
+            final Intent intent = new Intent(this, TransitionDestinationActivity.class);
+            startActivity(intent, options.toBundle());
+        }
+    }
+
+    public static class TransitionDestinationActivity extends LifecycleTrackingActivity {
+        @Override
+        protected void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            setContentView(R.layout.transition_destination_layout);
+            final Transition sharedElementEnterTransition =
+                    getWindow().getSharedElementEnterTransition();
+            sharedElementEnterTransition.addListener(new TransitionListenerAdapter() {
+                @Override
+                public void onTransitionEnd(Transition transition) {
+                    super.onTransitionEnd(transition);
+                    finishAfterTransition();
+                }
+            });
+        }
+    }
+
     static ComponentName getComponentName(Class<? extends Activity> activity) {
         return new ComponentName(getInstrumentation().getContext(), activity);
     }
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..0d0278e 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
@@ -37,7 +37,6 @@
 import android.content.Intent;
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
 
 import org.junit.Before;
@@ -50,7 +49,6 @@
  * Build/Install/Run:
  *     atest CtsWindowManagerDeviceTestCases:ActivityLifecyclePipTests
  */
-@FlakyTest(bugId = 77652261)
 @MediumTest
 @Presubmit
 public class ActivityLifecyclePipTests extends ActivityLifecycleClientTestBase {
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..5ec2c11 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,17 +93,25 @@
         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);
+
+        // Wait for SecondActivity in primary split screen leave minimize dock.
+        waitAndAssertActivityStates(state(secondActivity, ON_RESUME));
 
         // 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
@@ -353,8 +361,7 @@
         waitForActivityTransitions(CallbackTrackingActivity.class, expectedEnterSequence);
         LifecycleVerifier.assertOrder(getLifecycleLog(), CallbackTrackingActivity.class,
                 Arrays.asList(ON_TOP_POSITION_LOST, ON_PAUSE, ON_STOP, ON_DESTROY, ON_CREATE,
-                        ON_RESUME, ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST),
-                "moveToSplitScreen");
+                        ON_RESUME), "moveToSplitScreen");
         LifecycleVerifier.assertTransitionObserved(getLifecycleLog(),
                 transition(CallbackTrackingActivity.class, ON_MULTI_WINDOW_MODE_CHANGED),
                 "moveToSplitScreen");
@@ -395,9 +402,9 @@
 
         // Wait for the activity to receive the change
         waitForActivityTransitions(ConfigChangeHandlingActivity.class,
-                Arrays.asList(ON_MULTI_WINDOW_MODE_CHANGED, ON_TOP_POSITION_LOST));
+                Arrays.asList(ON_TOP_POSITION_LOST, ON_MULTI_WINDOW_MODE_CHANGED));
         LifecycleVerifier.assertOrder(getLifecycleLog(), ConfigChangeHandlingActivity.class,
-                Arrays.asList(ON_MULTI_WINDOW_MODE_CHANGED, ON_TOP_POSITION_LOST),
+                Arrays.asList(ON_TOP_POSITION_LOST, ON_MULTI_WINDOW_MODE_CHANGED),
                 "moveToSplitScreen");
 
         // Exit split-screen
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..cf8e125 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,10 @@
 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.ActivityLifecycleClientTestBase.NoDisplayActivity.EXTRA_LAUNCH_ACTIVITY;
+import static android.server.wm.lifecycle.ActivityLifecycleClientTestBase.NoDisplayActivity.EXTRA_NEW_TASK;
 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 +38,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,8 +56,10 @@
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
 
 import com.android.compatibility.common.util.AmUtils;
+import com.android.compatibility.common.util.SystemUtil;
 
 import org.junit.Test;
 
@@ -63,7 +70,6 @@
  * Build/Install/Run:
  *     atest CtsWindowManagerDeviceTestCases:ActivityLifecycleTests
  */
-@FlakyTest(bugId = 77652261)
 @MediumTest
 @Presubmit
 public class ActivityLifecycleTests extends ActivityLifecycleClientTestBase {
@@ -273,6 +279,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());
@@ -283,6 +368,53 @@
         LifecycleVerifier.assertLaunchAndDestroySequence(FirstActivity.class, getLifecycleLog());
     }
 
+    @FlakyTest(bugId = 139808754)
+    @Test
+    public void testTrampoline() throws Exception {
+        testTrampolineLifecycle(false /* newTask */);
+    }
+
+    @FlakyTest(bugId = 139808754)
+    @Test
+    public void testTrampolineNewTask() throws Exception {
+        testTrampolineLifecycle(true /* newTask */);
+    }
+
+    /**
+     * Verifies that activity start from a trampoline will have the correct lifecycle and complete
+     * in time. The expected lifecycle is that the trampoline will skip ON_START - ON_STOP part of
+     * the usual sequence, and will go straight to ON_DESTROY after creation.
+     */
+    private void testTrampolineLifecycle(boolean newTask) {
+        // Run activity start manually (without using instrumentation) to make it async and measure
+        // time from the request correctly.
+        final Intent newTaskIntent = new Intent(mTargetContext, NoDisplayActivity.class);
+        newTaskIntent.putExtra(EXTRA_LAUNCH_ACTIVITY, true);
+        newTaskIntent.putExtra(EXTRA_FINISH_IN_ON_CREATE, true);
+        if (newTask) {
+            newTaskIntent.putExtra(EXTRA_NEW_TASK, true);
+        }
+        newTaskIntent.addFlags(FLAG_ACTIVITY_NEW_TASK);
+        // Using Shell permission identity to allow task switching and avoid delay.
+        SystemUtil.runWithShellPermissionIdentity(
+                () -> mTargetContext.startActivity(newTaskIntent)
+        );
+        waitAndAssertActivityStates(state(NoDisplayActivity.class, ON_DESTROY),
+                state(CallbackTrackingActivity.class, ON_TOP_POSITION_GAINED));
+
+        LifecycleVerifier.assertEntireSequence(Arrays.asList(
+                transition(NoDisplayActivity.class, PRE_ON_CREATE),
+                transition(NoDisplayActivity.class, ON_CREATE),
+                transition(CallbackTrackingActivity.class, PRE_ON_CREATE),
+                transition(CallbackTrackingActivity.class, ON_CREATE),
+                transition(CallbackTrackingActivity.class, ON_START),
+                transition(CallbackTrackingActivity.class, ON_POST_CREATE),
+                transition(CallbackTrackingActivity.class, ON_RESUME),
+                transition(CallbackTrackingActivity.class, ON_TOP_POSITION_GAINED),
+                transition(NoDisplayActivity.class, ON_DESTROY)),
+                getLifecycleLog(), "trampolineLaunch");
+    }
+
     @Test
     public void testRelaunchResumed() throws Exception {
         final Activity activity = mFirstActivityTestRule.launchActivity(new Intent());
@@ -730,4 +862,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..92c5251 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
@@ -16,9 +16,11 @@
 
 package android.server.wm.lifecycle;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 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 +70,6 @@
  * Build/Install/Run:
  *     atest CtsWindowManagerDeviceTestCases:ActivityLifecycleTopResumedStateTests
  */
-@FlakyTest(bugId = 117135575)
 @MediumTest
 @Presubmit
 public class ActivityLifecycleTopResumedStateTests extends ActivityLifecycleClientTestBase {
@@ -444,15 +445,13 @@
         waitAndAssertActivityStates(state(singleTopActivity, ON_TOP_POSITION_GAINED),
                 state(topActivity, ON_DESTROY));
 
-        LifecycleVerifier.assertEntireSequence(Arrays.asList(
+        LifecycleVerifier.assertOrder(getLifecycleLog(), Arrays.asList(
                 transition(TranslucentCallbackTrackingActivity.class, ON_TOP_POSITION_LOST),
                 transition(TranslucentCallbackTrackingActivity.class, ON_PAUSE),
                 transition(SingleTopActivity.class, ON_NEW_INTENT),
                 transition(SingleTopActivity.class, ON_RESUME),
-                transition(SingleTopActivity.class, ON_TOP_POSITION_GAINED),
-                transition(TranslucentCallbackTrackingActivity.class, ON_STOP),
-                transition(TranslucentCallbackTrackingActivity.class, ON_DESTROY)),
-                getLifecycleLog(), "Single top resolution sequence must match");
+                transition(SingleTopActivity.class, ON_TOP_POSITION_GAINED)),
+                "Single top resolution sequence must match");
     }
 
     @Test
@@ -988,6 +987,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());
@@ -1005,8 +1091,21 @@
 
         // Wait and assert lifecycle
         waitAndAssertActivityStates(state(pipActivity, ON_PAUSE));
-        // PipMenuActivity will start and briefly get the top position, so we ignore the rest
-        // of the possibilities.
+
+        // The PipMenuActivity could start anytime after moving pipActivity to pinned stack,
+        // however, we cannot control when would it start or finish, so this test could fail when
+        // PipMenuActivity just start and pipActivity call finish almost at the same time.
+        // So the strategy here is to wait the PipMenuActivity start and finish after pipActivity
+        // moved to pinned stack and paused, because pipActivity is not focusable but the
+        // PipMenuActivity is focusable, when the pinned stack gain top focus means the
+        // PipMenuActivity is launched and resumed, then when pinned stack lost top focus means the
+        // PipMenuActivity is finished.
+        mAmWmState.waitWindowingModeTopFocus(WINDOWING_MODE_PINNED, true /* topFocus */
+                , "wait PipMenuActivity get top focus");
+        mAmWmState.waitWindowingModeTopFocus(WINDOWING_MODE_PINNED, false /* topFocus */
+                , "wait PipMenuActivity lost top focus");
+        waitAndAssertActivityStates(state(activity, ON_TOP_POSITION_GAINED));
+
         LifecycleVerifier.assertOrder(getLifecycleLog(), Arrays.asList(
                 transition(CallbackTrackingActivity.class, ON_TOP_POSITION_LOST),
                 transition(CallbackTrackingActivity.class, ON_PAUSE),
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..f30ed4e
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityTests.java
@@ -0,0 +1,289 @@
+/*
+ * 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.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_DESTROY;
+import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_PAUSE;
+import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_RESTART;
+import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_RESUME;
+import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_START;
+import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_STOP;
+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.LifecycleVerifier.transition;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.MediumTest;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+
+/**
+ * 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());
+    }
+
+    /**
+     * Verify that {@link Activity#finishAndRemoveTask()} removes all activities in task if called
+     * for root of task.
+     */
+    @FlakyTest(bugId=137329632)
+    @Test
+    public void testFinishTask_FromRoot() throws Exception {
+        final Class<? extends Activity> rootActivityClass = CallbackTrackingActivity.class;
+        final Activity rootActivity = launchActivity(rootActivityClass);
+        final Class<? extends Activity> topActivityClass = SecondCallbackTrackingActivity.class;
+        final Activity topActivity = launchActivity(topActivityClass);
+        waitAndAssertActivityStates(state(rootActivity, ON_STOP),
+                state(topActivity, ON_TOP_POSITION_GAINED));
+
+        getLifecycleLog().clear();
+        rootActivity.finishAndRemoveTask();
+
+        waitAndAssertActivityStates(state(rootActivity, ON_DESTROY),
+                state(topActivity, ON_DESTROY));
+        // Cannot guarantee exact sequence among top and bottom activities, so verifying
+        // independently
+        LifecycleVerifier.assertSequence(rootActivityClass, getLifecycleLog(),
+                Arrays.asList(ON_DESTROY), "finishAndRemoveTask");
+        LifecycleVerifier.assertSequence(topActivityClass, getLifecycleLog(),
+                Arrays.asList(ON_TOP_POSITION_LOST, ON_PAUSE, ON_STOP, ON_DESTROY),
+                "finishAndRemoveTask");
+    }
+
+    /**
+     * Verify that {@link Activity#finishAndRemoveTask()} removes all activities in task if called
+     * for root of task. This version verifies lifecycle when top activity is translucent
+     */
+    @FlakyTest(bugId=137329632)
+    @Test
+    public void testFinishTask_FromRoot_TranslucentOnTop() throws Exception {
+        final Class<? extends Activity> rootActivityClass = CallbackTrackingActivity.class;
+        final Activity rootActivity = launchActivity(rootActivityClass);
+        final Class<? extends Activity> topActivityClass =
+                TranslucentCallbackTrackingActivity.class;
+        final Activity topActivity = launchActivity(topActivityClass);
+        waitAndAssertActivityStates(state(rootActivity, ON_PAUSE),
+                state(topActivity, ON_TOP_POSITION_GAINED));
+
+        getLifecycleLog().clear();
+        rootActivity.finishAndRemoveTask();
+
+        waitAndAssertActivityStates(state(rootActivity, ON_DESTROY),
+                state(topActivity, ON_DESTROY));
+        // Cannot guarantee exact sequence among top and bottom activities, so verifying
+        // independently
+        LifecycleVerifier.assertSequence(rootActivityClass, getLifecycleLog(),
+                Arrays.asList(ON_STOP, ON_DESTROY), "finishAndRemoveTask");
+        LifecycleVerifier.assertSequence(topActivityClass, getLifecycleLog(),
+                Arrays.asList(ON_TOP_POSITION_LOST, ON_PAUSE, ON_STOP, ON_DESTROY),
+                "finishAndRemoveTask");
+    }
+
+    /**
+     * Verify that {@link Activity#finishAndRemoveTask()} only removes one activity in task if
+     * called not for root of task.
+     */
+    @FlakyTest(bugId=137329632)
+    @Test
+    public void testFinishTask_NotFromRoot() throws Exception {
+        final Class<? extends Activity> rootActivityClass = CallbackTrackingActivity.class;
+        final Activity rootActivity = launchActivity(rootActivityClass);
+        final Class<? extends Activity> midActivityClass = SecondActivity.class;
+        final Activity midActivity = launchActivity(midActivityClass);
+        final Class<? extends Activity> topActivityClass = SecondCallbackTrackingActivity.class;
+        final Activity topActivity = launchActivity(topActivityClass);
+        waitAndAssertActivityStates(state(rootActivity, ON_STOP), state(midActivity, ON_STOP),
+                state(topActivity, ON_TOP_POSITION_GAINED));
+
+        getLifecycleLog().clear();
+        midActivity.finishAndRemoveTask();
+
+        waitAndAssertActivityStates(state(midActivity, ON_DESTROY));
+        LifecycleVerifier.assertEntireSequence(Arrays.asList(
+                transition(midActivityClass, ON_DESTROY)), getLifecycleLog(),
+                "finishAndRemoveTask");
+    }
+
+    /**
+     * Verify the lifecycle of {@link Activity#finishAfterTransition()} for activity that has a
+     * transition set.
+     */
+    @FlakyTest(bugId=137329632)
+    @Test
+    public void testFinishAfterTransition() throws Exception {
+        final TransitionSourceActivity rootActivity =
+                (TransitionSourceActivity) launchActivity(TransitionSourceActivity.class);
+        waitAndAssertActivityStates(state(rootActivity, ON_RESUME));
+
+        // Launch activity with configured shared element transition. It will call
+        // finishAfterTransition() on its own after transition completes.
+        rootActivity.runOnUiThread(() -> rootActivity.launchActivityWithTransition());
+        waitAndAssertActivityStates(state(TransitionDestinationActivity.class, ON_DESTROY),
+                state(rootActivity, ON_RESUME));
+        LifecycleVerifier.assertLaunchAndDestroySequence(TransitionDestinationActivity.class,
+                getLifecycleLog());
+    }
+
+    /**
+     * Verify the lifecycle of {@link Activity#finishAfterTransition()} for activity with no
+     * transition set (root of task).
+     */
+    @FlakyTest(bugId=137329632)
+    @Test
+    public void testFinishAfterTransition_noTransition_rootOfTask() throws Exception {
+        final Activity activity = launchActivity(FirstActivity.class);
+        waitAndAssertActivityStates(state(activity, ON_RESUME));
+
+        getLifecycleLog().clear();
+        activity.finishAfterTransition();
+        waitAndAssertActivityStates(state(FirstActivity.class, ON_DESTROY));
+        LifecycleVerifier.assertSequence(FirstActivity.class, getLifecycleLog(),
+                Arrays.asList(ON_PAUSE, ON_STOP, ON_DESTROY), "finishAfterTransition");
+    }
+
+    /**
+     * Verify the lifecycle of {@link Activity#finishAfterTransition()} for activity with no
+     * transition set.
+     */
+    @FlakyTest(bugId=137329632)
+    @Test
+    public void testFinishAfterTransition_noTransition() throws Exception {
+        final Activity rootActivity = launchActivity(FirstActivity.class);
+        final Activity topActivity = launchActivity(SecondActivity.class);
+        waitAndAssertActivityStates(state(topActivity, ON_RESUME), state(rootActivity, ON_STOP));
+
+        getLifecycleLog().clear();
+        topActivity.finishAfterTransition();
+        waitAndAssertActivityStates(state(SecondActivity.class, ON_DESTROY));
+        LifecycleVerifier.assertSequence(SecondActivity.class, getLifecycleLog(),
+                Arrays.asList(ON_PAUSE, ON_STOP, ON_DESTROY), "finishAfterTransition");
+    }
+
+    /**
+     * Verify that {@link Activity#finishAffinity()} will finish all activities with the same
+     * affinity below the target activity.
+     */
+    @FlakyTest(bugId=137329632)
+    @Test
+    public void testFinishAffinity() throws Exception {
+        final Activity firstActivity = launchActivity(FirstActivity.class);
+        final Activity secondActivity = launchActivity(SecondActivity.class);
+        final Activity thirdActivity = launchActivity(ThirdActivity.class);
+        waitAndAssertActivityStates(state(thirdActivity, ON_RESUME), state(secondActivity, ON_STOP),
+                state(firstActivity, ON_STOP));
+
+        getLifecycleLog().clear();
+        secondActivity.finishAffinity();
+        waitAndAssertActivityStates(state(FirstActivity.class, ON_DESTROY),
+                state(SecondActivity.class, ON_DESTROY));
+        LifecycleVerifier.assertEmptySequence(ThirdActivity.class, getLifecycleLog(),
+                "finishAffinityBelow");
+    }
+
+    /**
+     * Verify that {@link Activity#finishAffinity()} will not finish activities with different
+     * affinities in the same task.
+     */
+    @FlakyTest(bugId=137329632)
+    @Test
+    public void testFinishAffinity_differentAffinity() throws Exception {
+        final Activity firstActivity = launchActivity(FirstActivity.class);
+        final Activity differentAffinityActivity = launchActivity(DifferentAffinityActivity.class);
+        waitAndAssertActivityStates(state(differentAffinityActivity, ON_RESUME),
+                state(firstActivity, ON_STOP));
+
+        getLifecycleLog().clear();
+        differentAffinityActivity.finishAffinity();
+        waitAndAssertActivityStates(state(DifferentAffinityActivity.class, ON_DESTROY));
+        LifecycleVerifier.assertSequence(FirstActivity.class, getLifecycleLog(),
+                Arrays.asList(ON_RESTART, ON_START, ON_RESUME), "finishAffinity");
+    }
+
+    /**
+     * Verify that {@link Activity#finishAffinity()} will not finish activities with the same
+     * affinity in different tasks.
+     */
+    @FlakyTest(bugId=137329632)
+    @Test
+    public void testFinishAffinity_multiTask() throws Exception {
+        final Activity firstActivity = launchActivity(FirstActivity.class);
+        final Intent newTaskIntent = new Intent();
+        newTaskIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK);
+        final Activity secondActivity = mSecondActivityTestRule.launchActivity(newTaskIntent);
+        waitAndAssertActivityStates(state(secondActivity, ON_RESUME),
+                state(firstActivity, ON_STOP));
+
+        getLifecycleLog().clear();
+        secondActivity.finishAffinity();
+        waitAndAssertActivityStates(state(SecondActivity.class, ON_DESTROY),
+                state(firstActivity, ON_RESUME));
+    }
+}
\ 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..d50bed9 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,54 @@
                                 || 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(
+    public void waitWindowingModeTopFocus(int windowingMode, boolean topFocus, String message) {
+        waitForWithAmState(amState -> {
+            final ActivityStack stack = amState.getStandardStackByWindowingMode(windowingMode);
+            return stack != null
+                    && topFocus == (amState.getFocusedStackId() == stack.getStackId());
+        }, message);
+    }
+
+    /** @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 +913,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..7f2e144 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
@@ -24,7 +24,6 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
-import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -67,6 +66,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";
@@ -82,12 +86,6 @@
      */
     public static final String KEY_USE_APPLICATION_CONTEXT = "use_application_context";
     /**
-     * Key for boolean extra, indicates if instrumentation context will be used for launch. This
-     * means that {@link PendingIntent} should be used instead of a regular one, because application
-     * switch will not be allowed otherwise.
-     */
-    public static final String KEY_USE_INSTRUMENTATION = "use_instrumentation";
-    /**
      * Key for boolean extra, indicates if any exceptions thrown during launch other then
      * {@link SecurityException} should be suppressed. A {@link SecurityException} is never thrown,
      * it's always written to logs.
@@ -167,12 +165,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);
             }
         }
@@ -196,30 +197,9 @@
                 context.getApplicationContext() : context;
 
         try {
-            if (extras.getBoolean(KEY_USE_INSTRUMENTATION)) {
-                // Using PendingIntent for Instrumentation launches, because otherwise we won't
-                // be allowed to switch the current activity with ours with different uid.
-                // android.permission.STOP_APP_SWITCHES is needed to do this directly.
-                // PendingIntent.FLAG_CANCEL_CURRENT is needed here, or we may get an existing
-                // PendingIntent if it is same kind of PendingIntent request to previous one.
-                // Note: optionsBundle is not taking into account for PendingIntentRecord.Key
-                // hashcode calculation.
-                final PendingIntent pendingIntent = PendingIntent.getActivity(launchContext, 0,
-                        newIntent, PendingIntent.FLAG_CANCEL_CURRENT, optionsBundle);
-                pendingIntent.send();
-            } else {
-                launchContext.startActivity(newIntent, optionsBundle);
-            }
+            launchContext.startActivity(newIntent, optionsBundle);
         } catch (SecurityException e) {
             handleSecurityException(context, e);
-        } catch (PendingIntent.CanceledException e) {
-            if (extras.getBoolean(KEY_SUPPRESS_EXCEPTIONS)) {
-                Log.e(TAG, "Exception launching activity with pending intent");
-            } else {
-                throw new RuntimeException(e);
-            }
-            // Bypass the exception although it is not SecurityException.
-            handleSecurityException(context, e);
         } catch (Exception e) {
             if (extras.getBoolean(KEY_SUPPRESS_EXCEPTIONS)) {
                 Log.e(TAG, "Exception launching activity");
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerState.java b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerState.java
index d7733dc..0ebee7b 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerState.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerState.java
@@ -807,7 +807,7 @@
         public boolean translucent;
 
         Activity(ActivityRecordProto proto) {
-            super(proto.configurationContainer);
+            super(proto.appWindowToken.windowToken.windowContainer.configurationContainer);
             name = proto.identifier.title;
             state = proto.state;
             visible = proto.visible;
@@ -827,6 +827,7 @@
         }
     }
 
+    // TODO: Switch to extending WindowContainer once unification is done.
     static abstract class ActivityContainer extends WindowManagerState.ConfigurationContainer {
         protected boolean mFullscreen;
         protected Rect mBounds;
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..6e27a27 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;
@@ -56,13 +57,11 @@
 import static android.server.wm.ActivityLauncher.KEY_SUPPRESS_EXCEPTIONS;
 import static android.server.wm.ActivityLauncher.KEY_TARGET_COMPONENT;
 import static android.server.wm.ActivityLauncher.KEY_USE_APPLICATION_CONTEXT;
-import static android.server.wm.ActivityLauncher.KEY_USE_INSTRUMENTATION;
 import static android.server.wm.ActivityLauncher.launchActivityFromExtras;
 import static android.server.wm.ActivityManagerState.STATE_RESUMED;
 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 +92,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 +122,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;
@@ -157,13 +155,11 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 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;
@@ -430,7 +426,7 @@
 
         pressWakeupButton();
         pressUnlockButton();
-        pressHomeButton();
+        launchHomeActivityNoWait();
         removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
 
         // Clear launch params for all test packages to make sure each test is run in a clean state.
@@ -446,8 +442,16 @@
         stopTestPackage(TEST_PACKAGE);
         stopTestPackage(SECOND_TEST_PACKAGE);
         stopTestPackage(THIRD_TEST_PACKAGE);
-        pressHomeButton();
+        launchHomeActivityNoWait();
+    }
 
+    /**
+     * 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) {
@@ -498,6 +502,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,15 +574,10 @@
         getInstrumentation().waitForIdleSync();
     }
 
-    /** Returns the set of stack ids. */
-    private HashSet<Integer> getStackIds() {
-        mAmWmState.computeState(true);
-        final List<ActivityManagerState.ActivityStack> stacks = mAmWmState.getAmState().getStacks();
-        final HashSet<Integer> stackIds = new HashSet<>();
-        for (ActivityManagerState.ActivityStack s : stacks) {
-            stackIds.add(s.mStackId);
-        }
-        return stackIds;
+    static void waitForOrFail(String message, BooleanSupplier condition) {
+        Condition.waitFor(new Condition<>(message, condition)
+                .setRetryIntervalMs(500)
+                .setOnFailure(unusedResult -> fail("FAILED because unsatisfied: " + message)));
     }
 
     /** Returns the stack that contains the provided task. */
@@ -588,8 +594,17 @@
         return null;
     }
 
-    protected void launchHomeActivity() {
+    /**
+     * Launches the home activity directly. If there is no specific reason to simulate a home key
+     * (which will trigger stop-app-switches), it is the recommended method to go home.
+     */
+    protected static void launchHomeActivityNoWait() {
         executeShellCommand(AM_START_HOME_ACTIVITY_COMMAND);
+    }
+
+    /** Launches the home activity directly with waiting for it to be visible. */
+    protected void launchHomeActivity() {
+        launchHomeActivityNoWait();
         mAmWmState.waitForHomeActivityVisible();
     }
 
@@ -762,10 +777,10 @@
                         new Rect(0, 0, taskWidth, taskHeight)));
     }
 
-    protected void resizeStack(int stackId, int stackLeft, int stackTop, int stackWidth,
-            int stackHeight) {
-        SystemUtil.runWithShellPermissionIdentity(() -> mAtm.resizeStack(stackId,
-                new Rect(stackLeft, stackTop, stackWidth, stackHeight)));
+    protected void resizePinnedStack(
+            int stackId, int stackLeft, int stackTop, int stackWidth, int stackHeight) {
+        SystemUtil.runWithShellPermissionIdentity(() -> mAtm.resizePinnedStack(stackId,
+                new Rect(stackLeft, stackTop, stackWidth, stackHeight), false /* animate */));
     }
 
     protected void pressAppSwitchButtonAndWaitForRecents() {
@@ -833,9 +848,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 +1106,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 +1226,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 +1247,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 +1405,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 +1425,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 +1489,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 +1497,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 +1505,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 +1513,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 +1521,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 +1563,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 +1571,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 +1583,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 +1604,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 +1615,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 +1668,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 +1703,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 +1729,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 +1783,11 @@
             return this;
         }
 
+        public LaunchActivityBuilder setLaunchTaskBehind(boolean launchTaskBehind) {
+            mLaunchTaskBehind = launchTaskBehind;
+            return this;
+        }
+
         public LaunchActivityBuilder setReorderToFront(boolean reorderToFront) {
             mReorderToFront = reorderToFront;
             return this;
@@ -1954,13 +1911,13 @@
         /** Launch an activity using instrumentation. */
         private void launchUsingInstrumentation() {
             final Bundle b = new Bundle();
-            b.putBoolean(KEY_USE_INSTRUMENTATION, true);
             b.putBoolean(KEY_LAUNCH_ACTIVITY, true);
             b.putBoolean(KEY_LAUNCH_TO_SIDE, mToSide);
             b.putBoolean(KEY_RANDOM_DATA, mRandomData);
             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/CommandSession.java b/tests/framework/base/windowmanager/util/src/android/server/wm/CommandSession.java
index 468bd30..6675c68 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/CommandSession.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/CommandSession.java
@@ -16,7 +16,7 @@
 
 package android.server.wm;
 
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import android.app.Activity;
 import android.content.BroadcastReceiver;
@@ -371,8 +371,12 @@
         private final ArrayMap<String, ActivitySession> mSessions = new ArrayMap<>();
         private boolean mClosed;
 
-        public ActivitySessionClient() {
-            this(getInstrumentation().getContext());
+        /**
+         * Creates a {#link ActivitySessionClient} instance with instrumentation context. It is used
+         * when the caller doen't need try-with-resource.
+         */
+        public static ActivitySessionClient create() {
+            return new ActivitySessionClient(getInstrumentation().getContext());
         }
 
         public ActivitySessionClient(Context context) {
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/TestJournalProvider.java b/tests/framework/base/windowmanager/util/src/android/server/wm/TestJournalProvider.java
index b2d92a3..aa3d63f 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/TestJournalProvider.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/TestJournalProvider.java
@@ -274,9 +274,9 @@
          * Perform the action which may have thread safety concerns when accessing the fields of
          * {@link TestJournal}.
          */
-        public static void withThreadSafeAccess(Runnable aciton) {
+        public static void withThreadSafeAccess(Runnable action) {
             synchronized (getInstance()) {
-                aciton.run();
+                action.run();
             }
         }
 
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/UiDeviceUtils.java b/tests/framework/base/windowmanager/util/src/android/server/wm/UiDeviceUtils.java
index 5da87dd..03fdf04 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/UiDeviceUtils.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/UiDeviceUtils.java
@@ -23,7 +23,7 @@
 import static android.view.KeyEvent.KEYCODE_WAKEUP;
 import static android.view.KeyEvent.KEYCODE_WINDOW;
 
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import android.app.KeyguardManager;
 import android.graphics.Point;
@@ -66,6 +66,10 @@
         getDevice().pressEnter();
     }
 
+    /**
+     * Simulates a pressed event of {@link KeyEvent#KEYCODE_HOME}. Note this will stop app switches
+     * for 5s (see android.permission.STOP_APP_SWITCHES).
+     */
     public static void pressHomeButton() {
         if (DEBUG) Log.d(TAG, "pressHomeButton");
         getDevice().pressHome();
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..f80ffee 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;
     }
 
@@ -602,7 +609,6 @@
 
         int mStackId;
         ArrayList<WindowTask> mTasks = new ArrayList<>();
-        boolean mWindowAnimationBackgroundSurfaceShowing;
         boolean mAnimatingBounds;
 
         WindowStack(StackProto proto) {
@@ -616,7 +622,6 @@
                 mTasks.add(task);
                 mSubWindows.addAll(task.getWindows());
             }
-            mWindowAnimationBackgroundSurfaceShowing = proto.animationBackgroundSurfaceIsDimming;
             mAnimatingBounds = proto.animatingBounds;
         }
 
@@ -628,10 +633,6 @@
             }
             return null;
         }
-
-        boolean isWindowAnimationBackgroundSurfaceShowing() {
-            return mWindowAnimationBackgroundSurfaceShowing;
-        }
     }
 
     static class WindowTask extends WindowContainer {
@@ -730,6 +731,7 @@
         private Rect mDisplayRect = new Rect();
         private Rect mAppRect = new Rect();
         private int mDpi;
+        private int mFlags;
         private Rect mStableBounds;
         private String mName;
         private int mSurfaceSize;
@@ -755,6 +757,7 @@
                 mDisplayRect.set(0, 0, infoProto.logicalWidth, infoProto.logicalHeight);
                 mAppRect.set(0, 0, infoProto.appWidth, infoProto.appHeight);
                 mName = infoProto.name;
+                mFlags = infoProto.flags;
             }
             final DisplayFramesProto displayFramesProto = proto.displayFrames;
             if (displayFramesProto != null) {
@@ -795,14 +798,18 @@
             return mDisplayRect;
         }
 
-        Rect getAppRect() {
-            return mAppRect;
+        Rect getStableBounds() {
+            return mStableBounds;
         }
 
         String getName() {
             return mName;
         }
 
+        int getFlags() {
+            return mFlags;
+        }
+
         int getSurfaceSize() {
             return mSurfaceSize;
         }
@@ -818,7 +825,7 @@
         @Override
         public String toString() {
             return "Display #" + mDisplayId + ": name=" + mName + " mDisplayRect=" + mDisplayRect
-                    + " mAppRect=" + mAppRect;
+                + " mAppRect=" + mAppRect + " mFlags=" + mFlags;
         }
     }
 
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 7ee21d3..42f057a 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..eb77b32
--- /dev/null
+++ b/tests/tests/appenumeration/AndroidTest.xml
@@ -0,0 +1,42 @@
+<?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="CtsAppEnumerationForceQueryable.apk" />
+        <option name="test-file-name" value="CtsAppEnumerationFilters.apk" />
+        <option name="test-file-name" value="CtsAppEnumerationNoApi.apk" />
+        <option name="test-file-name" value="CtsAppEnumerationQueriesNothing.apk" />
+        <option name="test-file-name" value="CtsAppEnumerationQueriesActivityViaAction.apk" />
+        <option name="test-file-name" value="CtsAppEnumerationQueriesServiceViaAction.apk" />
+        <option name="test-file-name" value="CtsAppEnumerationQueriesProviderViaAuthority.apk" />
+        <option name="test-file-name" value="CtsAppEnumerationQueriesPackage.apk" />
+        <option name="test-file-name" value="CtsAppEnumerationQueriesNothingTargetsQ.apk" />
+        <option name="test-file-name" value="CtsAppEnumerationQueriesNothingHasPermission.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/tests/tests/appenumeration/app/source/Android.bp b/tests/tests/appenumeration/app/source/Android.bp
new file mode 100644
index 0000000..1d23254
--- /dev/null
+++ b/tests/tests/appenumeration/app/source/Android.bp
@@ -0,0 +1,111 @@
+// 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: "CtsAppEnumerationQueriesNothing",
+    manifest: "AndroidManifest-queriesNothing.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: "CtsAppEnumerationQueriesActivityViaAction",
+    manifest: "AndroidManifest-queriesActivityAction.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: "CtsAppEnumerationQueriesServiceViaAction",
+    manifest: "AndroidManifest-queriesServiceAction.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: "CtsAppEnumerationQueriesProviderViaAuthority",
+    manifest: "AndroidManifest-queriesProviderAuthority.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: "CtsAppEnumerationQueriesPackage",
+    manifest: "AndroidManifest-queriesPackage.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: "CtsAppEnumerationQueriesNothingTargetsQ",
+    manifest: "AndroidManifest-queriesNothing-targetsQ.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: "CtsAppEnumerationQueriesNothingHasPermission",
+    manifest: "AndroidManifest-queriesNothing-hasPermission.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/source/AndroidManifest-queriesActivityAction.xml b/tests/tests/appenumeration/app/source/AndroidManifest-queriesActivityAction.xml
new file mode 100644
index 0000000..2eba524
--- /dev/null
+++ b/tests/tests/appenumeration/app/source/AndroidManifest-queriesActivityAction.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.queries.activity.action">
+
+    <queries>
+        <intent>
+            <action android:name="android.appenumeration.action.ACTIVITY" />
+        </intent>
+    </queries>
+
+    <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/AndroidManifest-queriesNothing-hasPermission.xml b/tests/tests/appenumeration/app/source/AndroidManifest-queriesNothing-hasPermission.xml
new file mode 100644
index 0000000..09c75ae
--- /dev/null
+++ b/tests/tests/appenumeration/app/source/AndroidManifest-queriesNothing-hasPermission.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.appenumeration.queries.nothing.haspermission">
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+    <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/AndroidManifest-queriesNothing-targetsQ.xml b/tests/tests/appenumeration/app/source/AndroidManifest-queriesNothing-targetsQ.xml
new file mode 100644
index 0000000..2810c87
--- /dev/null
+++ b/tests/tests/appenumeration/app/source/AndroidManifest-queriesNothing-targetsQ.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.appenumeration.queries.nothing.q">
+    <uses-sdk android:targetSdkVersion="29" />
+    <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/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/AndroidManifest-queriesPackage.xml b/tests/tests/appenumeration/app/source/AndroidManifest-queriesPackage.xml
new file mode 100644
index 0000000..e4bc398
--- /dev/null
+++ b/tests/tests/appenumeration/app/source/AndroidManifest-queriesPackage.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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.appenumeration.queries.pkg">
+
+    <queries>
+        <package android:name="android.appenumeration.noapi" />
+    </queries>
+
+    <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/AndroidManifest-queriesProviderAuthority.xml b/tests/tests/appenumeration/app/source/AndroidManifest-queriesProviderAuthority.xml
new file mode 100644
index 0000000..6ec5a96
--- /dev/null
+++ b/tests/tests/appenumeration/app/source/AndroidManifest-queriesProviderAuthority.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.queries.provider.authority">
+
+    <queries>
+        <intent>
+            <data android:scheme="content" android:host="android.appenumeration.testapp" />
+        </intent>
+    </queries>
+
+    <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/AndroidManifest-queriesServiceAction.xml b/tests/tests/appenumeration/app/source/AndroidManifest-queriesServiceAction.xml
new file mode 100644
index 0000000..b451455
--- /dev/null
+++ b/tests/tests/appenumeration/app/source/AndroidManifest-queriesServiceAction.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.queries.service.action">
+
+    <queries>
+        <intent>
+            <action android:name="android.appenumeration.action.SERVICE" />
+        </intent>
+    </queries>
+
+    <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..03b288d
--- /dev/null
+++ b/tests/tests/appenumeration/app/source/src/android/appenumeration/cts/query/TestActivity.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 android.appenumeration.cts.query;
+
+import static android.content.Intent.EXTRA_RETURN_RESULT;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.util.Random;
+
+public class TestActivity extends Activity {
+
+    SparseArray<RemoteCallback> callbacks = new SparseArray<>();
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        handleIntent(getIntent());
+    }
+
+    private void handleIntent(Intent intent) {
+        RemoteCallback remoteCallback = intent.getParcelableExtra("remoteCallback");
+        Bundle result = new Bundle();
+        final String action = intent.getAction();
+        final String packageName = intent.getStringExtra(
+                Intent.EXTRA_PACKAGE_NAME);
+        if ("android.appenumeration.cts.action.GET_PACKAGE_INFO".equals(action)) {
+            sendPackageInfo(remoteCallback, packageName);
+        } else if ("android.appenumeration.cts.action.START_FOR_RESULT".equals(action)) {
+            int requestCode = RESULT_FIRST_USER + callbacks.size();
+            callbacks.put(requestCode, remoteCallback);
+            startActivityForResult(
+                    new Intent("android.appenumeration.cts.action.SEND_RESULT").setComponent(
+                            new ComponentName(packageName, getClass().getCanonicalName())),
+                    requestCode);
+            // don't send anything... await result callback
+        } else if ("android.appenumeration.cts.action.SEND_RESULT".equals(action)) {
+            try {
+                setResult(RESULT_OK,
+                        getIntent().putExtra(
+                                Intent.EXTRA_RETURN_RESULT,
+                                getPackageManager().getPackageInfo(getCallingPackage(), 0)));
+            } catch (PackageManager.NameNotFoundException e) {
+                setResult(RESULT_FIRST_USER, new Intent().putExtra("error", e));
+            }
+            finish();
+        } else {
+            sendError(remoteCallback, new Exception("unknown action " + action));
+        }
+    }
+
+    private void sendError(RemoteCallback remoteCallback, Exception failure) {
+        Bundle result = new Bundle();
+        result.putSerializable("error", failure);
+        remoteCallback.sendResult(result);
+        finish();
+    }
+
+    private void sendPackageInfo(RemoteCallback remoteCallback, String packageName) {
+        final PackageInfo pi;
+        try {
+            pi = getPackageManager().getPackageInfo(packageName, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            sendError(remoteCallback, e);
+            return;
+        }
+        Bundle result = new Bundle();
+        result.putParcelable(EXTRA_RETURN_RESULT, pi);
+        remoteCallback.sendResult(result);
+        finish();
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        final RemoteCallback remoteCallback = callbacks.get(requestCode);
+        if (resultCode != RESULT_OK) {
+            Exception e = (Exception) data.getSerializableExtra("error");
+            sendError(remoteCallback, e == null ? new Exception("Result was " + resultCode) : e);
+            return;
+        }
+        final Bundle result = new Bundle();
+        result.putParcelable(EXTRA_RETURN_RESULT, data.getParcelableExtra(EXTRA_RETURN_RESULT));
+        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..d5f9757
--- /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: "CtsAppEnumerationForceQueryable",
+    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: "CtsAppEnumerationFilters",
+    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: "CtsAppEnumerationNoApi",
+    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..e6e61f7
--- /dev/null
+++ b/tests/tests/appenumeration/app/target/AndroidManifest-filters.xml
@@ -0,0 +1,41 @@
+<?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"
+                  android:exported="true" />
+        <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..d3a74ea
--- /dev/null
+++ b/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java
@@ -0,0 +1,286 @@
+/*
+ * 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.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+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.net.Uri;
+import android.os.Bundle;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.RemoteCallback;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.hamcrest.core.IsNull;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+@RunWith(AndroidJUnit4.class)
+public class AppEnumerationTests {
+
+    private static final String PKG_BASE = "android.appenumeration.";
+
+    /** A package with no published API and so isn't queryable by anything but package name */
+    private static final String TARGET_NO_API = PKG_BASE + "noapi";
+    /** A package that declares itself force queryable, making it visible to all other packages */
+    private static final String TARGET_FORCEQUERYABLE = PKG_BASE + "forcequeryable";
+    /** A package that exposes itself via various intent filters (activities, services, etc.) */
+    private static final String TARGET_FILTERS = PKG_BASE + "filters";
+
+    /** A package that has no queries tag or permission to query any specific packages */
+    private static final String QUERIES_NOTHING = PKG_BASE + "queries.nothing";
+    /** A package that has no queries tag or permissions but targets Q */
+    private static final String QUERIES_NOTHING_Q = PKG_BASE + "queries.nothing.q";
+    /** A package that has no queries but gets the QUERY_ALL_PACKAGES permission */
+    private static final String QUERIES_NOTHING_PERM = PKG_BASE + "queries.nothing.haspermission";
+    /** A package that queries for the action in {@link #TARGET_FILTERS} activity filter */
+    private static final String QUERIES_ACTIVITY_ACTION = PKG_BASE + "queries.activity.action";
+    /** A package that queries for the action in {@link #TARGET_FILTERS} service filter */
+    private static final String QUERIES_SERVICE_ACTION = PKG_BASE + "queries.service.action";
+    /** A package that queries for the authority in {@link #TARGET_FILTERS} provider */
+    private static final String QUERIES_PROVIDER_AUTH = PKG_BASE + "queries.provider.authority";
+    /** A package that queries for {@link #TARGET_NO_API} package */
+    private static final String QUERIES_PACKAGE = PKG_BASE + "queries.pkg";
+
+    private static final String[] ALL_QUERIES_PACKAGES = {
+            QUERIES_NOTHING,
+            QUERIES_NOTHING_Q,
+            QUERIES_NOTHING_PERM,
+            QUERIES_ACTIVITY_ACTION,
+            QUERIES_SERVICE_ACTION,
+            QUERIES_PROVIDER_AUTH,
+            QUERIES_PACKAGE,
+    };
+
+    private static Context sContext;
+    private static Handler sResponseHandler;
+    private static HandlerThread sResponseThread;
+
+    private static boolean sGlobalFeatureEnabled;
+
+    @Rule
+    public TestName name = new TestName();
+
+    @BeforeClass
+    public static void setup() {
+        final String deviceConfigResponse =
+                SystemUtil.runShellCommand(
+                        "device_config get package_manager_service "
+                                + "package_query_filtering_enabled")
+                        .trim();
+        if ("null".equalsIgnoreCase(deviceConfigResponse) || deviceConfigResponse.isEmpty()) {
+            sGlobalFeatureEnabled = true;
+        } else {
+            sGlobalFeatureEnabled = Boolean.parseBoolean(deviceConfigResponse);
+        }
+        System.out.println("Feature enabled: " + sGlobalFeatureEnabled);
+        if (!sGlobalFeatureEnabled) return;
+
+        sContext = InstrumentationRegistry.getInstrumentation().getContext();
+        sResponseThread = new HandlerThread("response");
+        sResponseThread.start();
+        sResponseHandler = new Handler(sResponseThread.getLooper());
+    }
+
+    @Before
+    public void setupTest() {
+        if (!sGlobalFeatureEnabled) return;
+
+        setFeatureEnabledForAll(true);
+    }
+
+    @AfterClass
+    public static void tearDown() {
+        if (!sGlobalFeatureEnabled) return;
+        sResponseThread.quit();
+    }
+
+    @Test
+    public void all_canSeeForceQueryable() throws Exception {
+        assertVisible(QUERIES_NOTHING, TARGET_FORCEQUERYABLE);
+        assertVisible(QUERIES_ACTIVITY_ACTION, TARGET_FORCEQUERYABLE);
+        assertVisible(QUERIES_SERVICE_ACTION, TARGET_FORCEQUERYABLE);
+        assertVisible(QUERIES_PROVIDER_AUTH, TARGET_FORCEQUERYABLE);
+        assertVisible(QUERIES_PACKAGE, TARGET_FORCEQUERYABLE);
+    }
+
+    @Test
+    public void queriesNothing_cannotSeeNonForceQueryable() throws Exception {
+        assertNotVisible(QUERIES_NOTHING, TARGET_NO_API);
+        assertNotVisible(QUERIES_NOTHING, TARGET_FILTERS);
+    }
+
+    @Test
+    public void queriesNothing_featureOff_canSeeAll() throws Exception {
+        setFeatureEnabledForAll(QUERIES_NOTHING, false);
+        assertVisible(QUERIES_NOTHING, TARGET_NO_API);
+        assertVisible(QUERIES_NOTHING, TARGET_FILTERS);
+    }
+
+    @Test
+    public void queriesNothingTargetsQ_canSeeAll() throws Exception {
+        assertVisible(QUERIES_NOTHING_Q, TARGET_FORCEQUERYABLE);
+        assertVisible(QUERIES_NOTHING_Q, TARGET_NO_API);
+        assertVisible(QUERIES_NOTHING_Q, TARGET_FILTERS);
+    }
+
+    @Test
+    public void queriesNothingHasPermission_canSeeAll() throws Exception {
+        assertVisible(QUERIES_NOTHING_PERM, TARGET_FORCEQUERYABLE);
+        assertVisible(QUERIES_NOTHING_PERM, TARGET_NO_API);
+        assertVisible(QUERIES_NOTHING_PERM, TARGET_FILTERS);
+    }
+
+    @Test
+    public void queriesSomething_cannotSeeNoApi() throws Exception {
+        assertNotVisible(QUERIES_ACTIVITY_ACTION, TARGET_NO_API);
+        assertNotVisible(QUERIES_SERVICE_ACTION, TARGET_NO_API);
+        assertNotVisible(QUERIES_PROVIDER_AUTH, TARGET_NO_API);
+    }
+
+    @Test
+    public void queriesActivityAction_canSeeTarget() throws Exception {
+        assertVisible(QUERIES_ACTIVITY_ACTION, TARGET_FILTERS);
+    }
+
+    @Test
+    public void queriesServiceAction_canSeeTarget() throws Exception {
+        assertVisible(QUERIES_SERVICE_ACTION, TARGET_FILTERS);
+    }
+
+    @Test
+    public void queriesProviderAuthority_canSeeTarget() throws Exception {
+        assertVisible(QUERIES_PROVIDER_AUTH, TARGET_FILTERS);
+    }
+
+    @Test
+    public void queriesPackage_canSeeTarget() throws Exception {
+        assertVisible(QUERIES_PACKAGE, TARGET_NO_API);
+
+    }
+    @Test
+    public void whenStarted_canSeeCaller() throws Exception {
+        // let's first make sure that the target cannot see the caller.
+        assertNotVisible(QUERIES_NOTHING, QUERIES_NOTHING_PERM);
+        // now let's start the target and make sure that it can see the caller as part of that call
+        PackageInfo packageInfo = startForResult(QUERIES_NOTHING_PERM, QUERIES_NOTHING);
+        assertThat(packageInfo, IsNull.notNullValue());
+        assertThat(packageInfo.packageName, is(QUERIES_NOTHING_PERM));
+        // and finally let's re-run the last check to make sure that the target can still see the
+        // caller
+        assertVisible(QUERIES_NOTHING, QUERIES_NOTHING_PERM);
+    }
+
+    private void assertVisible(String sourcePackageName, String targetPackageName)
+            throws Exception {
+        if (!sGlobalFeatureEnabled) return;
+        Assert.assertNotNull(sourcePackageName + " should be able to see " + targetPackageName,
+                getPackageInfo(sourcePackageName, targetPackageName));
+    }
+
+
+    private void setFeatureEnabledForAll(Boolean enabled) {
+        for (String pkgName : ALL_QUERIES_PACKAGES) {
+            setFeatureEnabledForAll(pkgName, enabled);
+        }
+    }
+
+    private void setFeatureEnabledForAll(String packageName, Boolean enabled) {
+        SystemUtil.runShellCommand(
+                "am compat " + (enabled == null ? "reset" : enabled ? "enable" : "disable")
+                        + " 135549675 " + packageName);
+    }
+
+    private void assertNotVisible(String sourcePackageName, String targetPackageName)
+            throws Exception {
+        if (!sGlobalFeatureEnabled) return;
+        try {
+            getPackageInfo(sourcePackageName, targetPackageName);
+            fail(sourcePackageName + " should not be able to see " + targetPackageName);
+        } catch (PackageManager.NameNotFoundException ignored) {
+        }
+    }
+
+    private PackageInfo getPackageInfo(String sourcePackageName, String targetPackageName)
+            throws Exception {
+        Bundle response = sendCommand(sourcePackageName, targetPackageName,
+                PKG_BASE + "cts.action.GET_PACKAGE_INFO");
+        return response.getParcelable(Intent.EXTRA_RETURN_RESULT);
+    }
+
+    private PackageInfo startForResult(String sourcePackageName, String targetPackageName)
+            throws Exception {
+        Bundle response = sendCommand(sourcePackageName, targetPackageName,
+                PKG_BASE + "cts.action.START_FOR_RESULT");
+        return response.getParcelable(Intent.EXTRA_RETURN_RESULT);
+    }
+
+    private Bundle sendCommand(String sourcePackageName, String targetPackageName, String action)
+            throws Exception {
+        Intent intent = new Intent(action)
+                .setComponent(new ComponentName(
+                        sourcePackageName, PKG_BASE + "cts.query.TestActivity"))
+                // data uri unique to each activity start to ensure actual launch and not just
+                // redisplay
+                .setData(Uri.parse("test://" + name.getMethodName() + targetPackageName))
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
+                .putExtra(Intent.EXTRA_PACKAGE_NAME, targetPackageName);
+        final ConditionVariable latch = new ConditionVariable();
+        final AtomicReference<Bundle> resultReference = new AtomicReference<>();
+        final RemoteCallback callback = new RemoteCallback(
+                bundle -> {
+                    resultReference.set(bundle);
+                    latch.open();
+                },
+                sResponseHandler);
+        intent.putExtra("remoteCallback", callback);
+        sContext.startActivity(intent);
+        if (!latch.block(TimeUnit.SECONDS.toMillis(10))) {
+            throw new TimeoutException(
+                    "Latch timed out while awiating a response from " + targetPackageName);
+        }
+        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..9463744 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,48 @@
         "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",
+        "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..eec81df
--- /dev/null
+++ b/tests/tests/appop/src/android/app/appops/cts/AppOpsLoggingTest.kt
@@ -0,0 +1,473 @@
+/*
+ * 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 android.platform.test.annotations.AppModeFull
+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.Executor
+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
+)
+
+@AppModeFull(reason = "Test relies on other app to connect to. Instant apps can't see other apps")
+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)
+                    }
+
+                    override fun getAsyncNotedExecutor(): Executor {
+                        // Execute callbacks immediately
+                        return Executor { it.run() }
+                    }
+                })
+    }
+
+    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..62bc995 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,32 @@
 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 android.provider.DeviceConfig
 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
+
+const val PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled"
 
 @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
+
+    private var wasPermissionsHubEnabled = false
 
     // Start an activity to make sure this app counts as being in the foreground
     @Rule @JvmField
@@ -51,58 +52,57 @@
 
     @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()
+        wasPermissionsHubEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+                PROPERTY_PERMISSIONS_HUB_ENABLED, false)
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY, PROPERTY_PERMISSIONS_HUB_ENABLED,
+                true.toString(), false)
+        appOpsManager.clearHistory()
+        appOpsManager.resetHistoryParameters()
     }
 
     @After
     fun tearDownTest() {
-        appOpsManager!!.clearHistory()
-        appOpsManager!!.resetHistoryParameters()
-        val uiAutomation = getInstrumentation().getUiAutomation()
+        appOpsManager.clearHistory()
+        appOpsManager.resetHistoryParameters()
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY, PROPERTY_PERMISSIONS_HUB_ENABLED,
+                wasPermissionsHubEnabled.toString(), false)
         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 +111,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 +148,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 +167,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 +177,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 +200,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 +210,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 +225,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 +246,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 +271,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 +286,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 +336,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 +347,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 +378,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 +429,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 +445,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 +468,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 +493,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/appwidget/src/android/appwidget/cts/AppWidgetTest.java b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
index 0e84505..6a79aba 100644
--- a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
@@ -74,6 +74,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.IntConsumer;
 
 public class AppWidgetTest extends AppWidgetTestCase {
 
@@ -739,6 +740,88 @@
 
     @AppModeFull(reason = "Instant apps cannot provide or host app widgets")
     @Test
+    public void testAppWidgetRemoved() throws Exception {
+
+        // We want to bind widgets.
+        grantBindAppWidgetPermission();
+
+        final AtomicInteger onAppWidgetRemovedCounter = new AtomicInteger();
+        IntConsumer callback = mock(IntConsumer.class);
+
+        // Create a host and start listening.
+        AppWidgetHost host = new AppWidgetHost(
+            getInstrumentation().getTargetContext(), 0) {
+            @Override
+            public void onAppWidgetRemoved(int widgetId) {
+                synchronized (mLock) {
+                    onAppWidgetRemovedCounter.incrementAndGet();
+                    mLock.notifyAll();
+                    callback.accept(widgetId);
+                }
+            }
+        };
+        host.deleteHost();
+        host.startListening();
+
+        int firstAppWidgetId = 0;
+        int secondAppWidgetId = 0;
+
+        try {
+            // Grab the provider we defined to be bound.
+            AppWidgetProviderInfo firstProviderInfo = getFirstAppWidgetProviderInfo();
+            AppWidgetProviderInfo secondProviderInfo = getSecondAppWidgetProviderInfo();
+
+            // Allocate widget id to bind.
+            firstAppWidgetId = host.allocateAppWidgetId();
+            secondAppWidgetId = host.allocateAppWidgetId();
+
+            //create listeners
+            MyAppWidgetHostView.OnUpdateAppWidgetListener secondAppHostViewListener =
+                mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class);
+
+            // Bind the first app widget.
+            getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId,
+                firstProviderInfo.getProfile(), firstProviderInfo.provider, null);
+            getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId,
+                secondProviderInfo.getProfile(), secondProviderInfo.provider, null);
+
+            // Disable the first widget while host is listening
+            PackageManager packageManager = getInstrumentation().getTargetContext()
+                .getApplicationContext().getPackageManager();
+            packageManager.setComponentEnabledSetting(firstProviderInfo.provider,
+                PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                PackageManager.DONT_KILL_APP);
+
+            waitForCallCount(onAppWidgetRemovedCounter, 1);
+
+            // Disable the second widget while host is paused
+            host.stopListening();
+            packageManager.setComponentEnabledSetting(secondProviderInfo.provider,
+                PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                PackageManager.DONT_KILL_APP);
+
+
+            assertEquals(onAppWidgetRemovedCounter.get(),1);
+            verify(callback).accept(eq(firstAppWidgetId));
+
+            // resume listening
+            host.startListening();
+
+            // Wait for the package change to propagate.
+            waitForCallCount(onAppWidgetRemovedCounter, 2);
+            verify(callback).accept(eq(secondAppWidgetId));
+
+        } finally {
+            // Clean up.
+            host.deleteAppWidgetId(firstAppWidgetId);
+            host.deleteAppWidgetId(secondAppWidgetId);
+            host.deleteHost();
+            revokeBindAppWidgetPermission();
+        }
+    }
+
+    @AppModeFull(reason = "Instant apps cannot provide or host app widgets")
+    @Test
     public void testUpdateAppWidgetViaComponentName() throws Exception {
         // We want to bind widgets.
         grantBindAppWidgetPermission();
@@ -1502,7 +1585,6 @@
     private static class MyAppWidgetHostView extends AppWidgetHostView {
         private OnUpdateAppWidgetListener mOnUpdateAppWidgetListener;
 
-
         public interface OnUpdateAppWidgetListener {
             public void onUpdateAppWidget(RemoteViews remoteViews);
         }
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/service/src/android/assist/service/MainInteractionSession.java b/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java
index 7706bd4..2c96f80 100644
--- a/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java
+++ b/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java
@@ -82,7 +82,13 @@
         Log.i(TAG, "onDestroy()");
         super.onDestroy();
         if (mReceiver != null) {
-            mContext.unregisterReceiver(mReceiver);
+            try {
+                mContext.unregisterReceiver(mReceiver);
+            } catch (IllegalArgumentException e) {
+                // Ignore this exception when unregisterReceiver fails. Due to there will be timing
+                // case to destroy VoiceInteractionSessionService before VoiceInteractionSession.
+                Log.e(TAG, "Failed to unregister receiver in onDestroy.", e);
+            }
         }
     }
 
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/car/src/android/car/cts/CarUxRestrictionsManagerTest.java b/tests/tests/car/src/android/car/cts/CarUxRestrictionsManagerTest.java
index ca7364b..4f80147 100644
--- a/tests/tests/car/src/android/car/cts/CarUxRestrictionsManagerTest.java
+++ b/tests/tests/car/src/android/car/cts/CarUxRestrictionsManagerTest.java
@@ -15,9 +15,13 @@
  */
 package android.car.cts;
 
+
+import static android.car.drivingstate.CarUxRestrictionsManager.UX_RESTRICTION_MODE_BASELINE;
+
 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.car.Car;
 import android.car.drivingstate.CarUxRestrictions;
@@ -27,6 +31,8 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.google.common.collect.ImmutableList;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -87,4 +93,37 @@
         mManager.registerListener(restrictions -> {});
         mManager.unregisterListener();
     }
+
+    @Test
+    public void testSetRestrictionMode_missingPermission_throwsException() {
+        try {
+            mManager.setRestrictionMode(UX_RESTRICTION_MODE_BASELINE);
+            fail("Expected SecurityException. App does not have the change UX Restrictions "
+                    + "permission.");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testGetRestrictionMode_missingPermission_throwsException() {
+        try {
+            mManager.getRestrictionMode();
+            fail("Expected SecurityException. App does not have the change UX Restrictions "
+                    + "permission.");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testSaveUxRestrictionsConfigurationForNextBoot_missingPermission_throwsException() {
+        try {
+            mManager.saveUxRestrictionsConfigurationForNextBoot(ImmutableList.of());
+            fail("Expected SecurityException. App does not have the change UX Restrictions "
+                    + "permission.");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
 }
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
index 69b364e..e73c11a 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
@@ -481,6 +481,7 @@
             mTelephonyManager.getVoiceMailAlphaTag();
             mTelephonyManager.getForbiddenPlmns();
             mTelephonyManager.getServiceState();
+            mTelephonyManager.setForbiddenPlmns(new ArrayList<String>());
         } catch (SecurityException e) {
             failMessage();
         }
@@ -902,8 +903,6 @@
      * This test verifies that {@link TelephonyManager#setVoiceMailNumber(String, String)} correctly
      * sets the VoiceMail alpha tag and number when called.
      */
-    /* Disabling the test for now due to a bug in the code. Will re-enable it when the bug is
-       fixed.
     public void testVoiceMailNumber() {
         if (!hasCellular) return;
 
@@ -923,7 +922,7 @@
             // Reset original alpha tag and number values.
             mTelephonyManager.setVoiceMailNumber(originalAlphaTag, originalNumber);
         }
-    } */
+    }
 
     /**
      * This test verifies that {@link SubscriptionManager#createSubscriptionGroup(List)} correctly
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/ContentResolverTest.java b/tests/tests/content/src/android/content/cts/ContentResolverTest.java
index d06514f..a3f03b0 100644
--- a/tests/tests/content/src/android/content/cts/ContentResolverTest.java
+++ b/tests/tests/content/src/android/content/cts/ContentResolverTest.java
@@ -25,6 +25,8 @@
 import android.content.res.AssetFileDescriptor;
 import android.database.ContentObserver;
 import android.database.Cursor;
+import android.icu.text.Collator;
+import android.icu.util.ULocale;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.CancellationSignal;
@@ -46,6 +48,9 @@
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -427,6 +432,47 @@
         mCursor.close();
     }
 
+    public void testQuery_SqlSortingFromBundleArgs_Locale() {
+        mContentResolver.delete(TABLE1_URI, null, null);
+
+        final List<String> data = Arrays.asList(
+                "ABC", "abc", "pinyin", "가나다", "바사", "테스트", "马",
+                "嘛", "妈", "骂", "吗", "码", "玛", "麻", "中", "梵", "苹果", "久了", "伺候");
+
+        for (String s : data) {
+            final ContentValues values = new ContentValues();
+            values.put(COLUMN_KEY_NAME, s.hashCode());
+            values.put(COLUMN_VALUE_NAME, s);
+            mContentResolver.insert(TABLE1_URI, values);
+        }
+
+        String[] sortCols = new String[] { COLUMN_VALUE_NAME };
+        Bundle queryArgs = new Bundle();
+        queryArgs.putStringArray(ContentResolver.QUERY_ARG_SORT_COLUMNS, sortCols);
+
+        for (String locale : new String[] {
+                "zh",
+                "zh@collation=pinyin",
+                "zh@collation=stroke",
+                "zh@collation=zhuyin",
+        }) {
+            // Assert that sorting is identical between SQLite and ICU4J
+            queryArgs.putString(ContentResolver.QUERY_ARG_SORT_LOCALE, locale);
+            try (Cursor c = mContentResolver.query(TABLE1_URI, sortCols, queryArgs, null)) {
+                data.sort(Collator.getInstance(new ULocale(locale)));
+                assertEquals(data, collect(c));
+            }
+        }
+    }
+
+    private static List<String> collect(Cursor c) {
+        List<String> res = new ArrayList<>();
+        while (c.moveToNext()) {
+            res.add(c.getString(0));
+        }
+        return res;
+    }
+
     /**
      * Verifies that paging information is correctly relayed, and that
      * honored arguments from a supporting client are returned correctly.
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/MockContentProvider.java b/tests/tests/content/src/android/content/cts/MockContentProvider.java
index d2b613c..ee6d0be 100644
--- a/tests/tests/content/src/android/content/cts/MockContentProvider.java
+++ b/tests/tests/content/src/android/content/cts/MockContentProvider.java
@@ -22,6 +22,7 @@
 import android.annotation.Nullable;
 import android.content.ContentProvider;
 import android.content.ContentProvider.PipeDataWriter;
+import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
@@ -253,6 +254,33 @@
     }
 
     @Override
+    public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+            @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) {
+        if (queryArgs != null && queryArgs.containsKey(ContentResolver.QUERY_ARG_SORT_LOCALE)) {
+            final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+            final String locale = queryArgs.getString(ContentResolver.QUERY_ARG_SORT_LOCALE);
+            final String safeLocale = locale.replaceAll("[^a-zA-Z]", "");
+            try (Cursor c = db.rawQuery("SELECT icu_load_collation(?, ?);",
+                    new String[] { locale, safeLocale }, cancellationSignal)) {
+                while (c.moveToNext()) {
+                }
+            }
+
+            final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+            qb.setTables("TestTable1");
+            qb.setProjectionMap(CTSDBTABLE1_LIST_PROJECTION_MAP);
+
+            final String sortOrder = TextUtils.join(", ",
+                    queryArgs.getStringArray(ContentResolver.QUERY_ARG_SORT_COLUMNS));
+            return qb.query(db, projection, null, null, null, null,
+                    sortOrder + " COLLATE " + safeLocale,
+                    null, cancellationSignal);
+        } else {
+            return super.query(uri, projection, queryArgs, cancellationSignal);
+        }
+    }
+
+    @Override
     public Cursor query(Uri uri, String[] projection, String selection,
             String[] selectionArgs, String sortOrder) {
         return query(uri, projection, selection, selectionArgs, sortOrder, null);
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/cts/SyncStorageEngineTest.java b/tests/tests/content/src/android/content/cts/SyncStorageEngineTest.java
index 457c1b6..47bc102 100644
--- a/tests/tests/content/src/android/content/cts/SyncStorageEngineTest.java
+++ b/tests/tests/content/src/android/content/cts/SyncStorageEngineTest.java
@@ -17,30 +17,16 @@
 package android.content.cts;
 
 import android.accounts.Account;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.Intent;
-import android.content.PeriodicSync;
-import android.content.res.Resources;
 import android.content.SyncStatusInfo;
-import android.os.Bundle;
 import android.os.Looper;
 import android.platform.test.annotations.AppModeFull;
 import android.test.AndroidTestCase;
-import android.test.RenamingDelegatingContext;
-import android.test.mock.MockContentResolver;
-import android.test.mock.MockContext;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.util.AtomicFile;
+
 import com.android.server.content.SyncStorageEngine;
-import com.android.internal.os.AtomicFile;
 
 import java.io.File;
 import java.io.FileOutputStream;
-import java.util.List;
 
 @AppModeFull(reason = "Sync manager not supported")
 public class SyncStorageEngineTest extends AndroidTestCase {
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
index 2c4d0067..d0e24c6 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
@@ -871,7 +871,7 @@
             return;
         }
         PackageInfo packageInfo = mPackageManager.getPackageInfo(SHIM_APEX_PACKAGE_NAME,
-                PackageManager.MATCH_APEX);
+                PackageManager.MATCH_APEX | PackageManager.MATCH_FACTORY_ONLY);
         assertShimApexInfoIsCorrect(packageInfo);
     }
 
@@ -924,7 +924,7 @@
             return;
         }
         List<PackageInfo> installedPackages = mPackageManager.getInstalledPackages(
-                PackageManager.MATCH_APEX);
+                PackageManager.MATCH_APEX | PackageManager.MATCH_FACTORY_ONLY);
         List<PackageInfo> shimApex = installedPackages.stream().filter(
                 packageInfo -> packageInfo.packageName.equals(SHIM_APEX_PACKAGE_NAME)).collect(
                 Collectors.toList());
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/Android.bp b/tests/tests/graphics/Android.bp
index beed115..9ed18c2 100644
--- a/tests/tests/graphics/Android.bp
+++ b/tests/tests/graphics/Android.bp
@@ -28,6 +28,7 @@
         "ctstestrunner-axt",
         "androidx.annotation_annotation",
         "junit",
+        "junit-params",
         "testng",
         "androidx.core_core",
     ],
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..beecac0 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,51 @@
     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 void testInfo(JNIEnv* env, jclass, jobject jbitmap, jint androidBitmapFormat,
+                     jint width, jint height, jboolean hasAlpha, jboolean premultiplied) {
+    AndroidBitmapInfo info;
+    int err = AndroidBitmap_getInfo(env, jbitmap, &info);
+    ASSERT_EQ(err, ANDROID_BITMAP_RESULT_SUCCESS);
+
+    ASSERT_EQ(androidBitmapFormat, info.format);
+    ASSERT_EQ(width, info.width);
+    ASSERT_EQ(height, info.height);
+
+    int ndkAlpha = (info.flags << ANDROID_BITMAP_FLAGS_ALPHA_SHIFT)
+            & ANDROID_BITMAP_FLAGS_ALPHA_MASK;
+    if (!hasAlpha) {
+        ASSERT_EQ(ndkAlpha, ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE);
+    } else if (premultiplied) {
+        ASSERT_EQ(ndkAlpha, ANDROID_BITMAP_FLAGS_ALPHA_PREMUL);
+    } else {
+        ASSERT_EQ(ndkAlpha, ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL);
+    }
+}
+
 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 },
+    { "nTestInfo", "(Landroid/graphics/Bitmap;IIIZZ)V", (void*) testInfo },
 };
 
 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/BitmapFactoryTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
index b507bf9..1b934ac 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
@@ -36,6 +36,7 @@
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
+import android.platform.test.annotations.LargeTest;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.util.DisplayMetrics;
@@ -43,7 +44,6 @@
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.compatibility.common.util.CddTest;
 
@@ -61,29 +61,36 @@
 import java.io.RandomAccessFile;
 import java.util.concurrent.CountDownLatch;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 @SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(JUnitParamsRunner.class)
 public class BitmapFactoryTest {
     // height and width of start.jpg
     private static final int START_HEIGHT = 31;
     private static final int START_WIDTH = 31;
 
-    // The test images, including baseline JPEG, a PNG, a GIF, a BMP AND a WEBP.
-    private static final int[] RES_IDS = new int[] {
-            R.drawable.baseline_jpeg, R.drawable.png_test, R.drawable.gif_test,
-            R.drawable.bmp_test, R.drawable.webp_test
-    };
+    static class TestImage {
+        TestImage(int id, int width, int height) {
+            this.id = id;
+            this.width = width;
+            this.height = height;
+        }
+        public final int id;
+        public final int width;
+        public final int height;
+    }
 
-    // The width and height of the above image.
-    private static final int WIDTHS[] = new int[] { 1280, 640, 320, 320, 640 };
-    private static final int HEIGHTS[] = new int[] { 960, 480, 240, 240, 480 };
-
-    // Configurations for BitmapFactory.Options
-    private static final Config[] COLOR_CONFIGS = new Config[] {Config.ARGB_8888, Config.RGB_565};
-    private static final int[] COLOR_TOLS = new int[] {16, 49, 576};
-
-    private static final Config[] COLOR_CONFIGS_RGBA = new Config[] {Config.ARGB_8888};
-    private static final int[] COLOR_TOLS_RGBA = new int[] {72, 124};
+    private Object[] testImages() {
+        return new Object[] {
+                new TestImage(R.drawable.baseline_jpeg, 1280, 960),
+                new TestImage(R.drawable.png_test, 640, 480),
+                new TestImage(R.drawable.gif_test, 320, 240),
+                new TestImage(R.drawable.bmp_test, 320, 240),
+                new TestImage(R.drawable.webp_test, 640, 480),
+        };
+    }
 
     private static final int[] RAW_COLORS = new int[] {
         // raw data from R.drawable.premul_data
@@ -206,88 +213,102 @@
     }
 
     @Test
-    public void testDecodeStream3() {
-        for (int i = 0; i < RES_IDS.length; ++i) {
-            InputStream is = obtainInputStream(RES_IDS[i]);
-            Bitmap b = BitmapFactory.decodeStream(is);
-            assertNotNull(b);
-            // Test the bitmap size
-            assertEquals(WIDTHS[i], b.getWidth());
-            assertEquals(HEIGHTS[i], b.getHeight());
-        }
+    @Parameters(method = "testImages")
+    public void testDecodeStream3(TestImage testImage) {
+        InputStream is = obtainInputStream(testImage.id);
+        Bitmap b = BitmapFactory.decodeStream(is);
+        assertNotNull(b);
+        // Test the bitmap size
+        assertEquals(testImage.width, b.getWidth());
+        assertEquals(testImage.height, b.getHeight());
+    }
+
+    private Object[] paramsForWebpDecodeEncode() {
+        return new Object[] {
+                new Object[] {Config.ARGB_8888, 16},
+                new Object[] {Config.RGB_565, 49}
+        };
+    }
+
+    private Bitmap decodeOpaqueImage(int resId, BitmapFactory.Options options) {
+        return decodeOpaqueImage(obtainInputStream(resId), options);
+    }
+
+    private Bitmap decodeOpaqueImage(InputStream stream, BitmapFactory.Options options) {
+        Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
+        assertNotNull(bitmap);
+        assertFalse(bitmap.isPremultiplied());
+        assertFalse(bitmap.hasAlpha());
+        return bitmap;
     }
 
     @Test
-    public void testDecodeStream4() {
+    @Parameters(method = "paramsForWebpDecodeEncode")
+    public void testWebpStreamDecode(Config config, int tolerance) {
         BitmapFactory.Options options = new BitmapFactory.Options();
-        for (int k = 0; k < COLOR_CONFIGS.length; ++k) {
-            options.inPreferredConfig = COLOR_CONFIGS[k];
+        options.inPreferredConfig = config;
 
-            // Decode the PNG & WebP test images. The WebP test image has been encoded from PNG test
-            // image and should have same similar (within some error-tolerance) Bitmap data.
-            InputStream iStreamPng = obtainInputStream(R.drawable.png_test);
-            Bitmap bPng = BitmapFactory.decodeStream(iStreamPng, null, options);
-            assertNotNull(bPng);
-            assertEquals(bPng.getConfig(), COLOR_CONFIGS[k]);
-            assertFalse(bPng.isPremultiplied());
-            assertFalse(bPng.hasAlpha());
+        // Decode the PNG & WebP test images. The WebP test image has been encoded from PNG test
+        // image and should have same similar (within some error-tolerance) Bitmap data.
+        Bitmap bPng = decodeOpaqueImage(R.drawable.png_test, options);
+        assertEquals(bPng.getConfig(), config);
+        Bitmap bWebp = decodeOpaqueImage(R.drawable.webp_test, options);
+        compareBitmaps(bPng, bWebp, tolerance, true, bPng.isPremultiplied());
+    }
 
-            InputStream iStreamWebp1 = obtainInputStream(R.drawable.webp_test);
-            Bitmap bWebp1 = BitmapFactory.decodeStream(iStreamWebp1, null, options);
-            assertNotNull(bWebp1);
-            assertFalse(bWebp1.isPremultiplied());
-            assertFalse(bWebp1.hasAlpha());
-            compareBitmaps(bPng, bWebp1, COLOR_TOLS[k], true, bPng.isPremultiplied());
+    @Test
+    @Parameters(method = "paramsForWebpDecodeEncode")
+    public void testWebpStreamEncode(Config config, int tolerance) {
+        BitmapFactory.Options options = new BitmapFactory.Options();
+        options.inPreferredConfig = config;
 
-            // Compress the PNG image to WebP format (Quality=90) and decode it back.
-            // This will test end-to-end WebP encoding and decoding.
-            ByteArrayOutputStream oStreamWebp = new ByteArrayOutputStream();
-            assertTrue(bPng.compress(CompressFormat.WEBP, 90, oStreamWebp));
-            InputStream iStreamWebp2 = new ByteArrayInputStream(oStreamWebp.toByteArray());
-            Bitmap bWebp2 = BitmapFactory.decodeStream(iStreamWebp2, null, options);
-            assertNotNull(bWebp2);
-            assertFalse(bWebp2.isPremultiplied());
-            assertFalse(bWebp2.hasAlpha());
-            compareBitmaps(bPng, bWebp2, COLOR_TOLS[k], true, bPng.isPremultiplied());
-        }
+        Bitmap bPng = decodeOpaqueImage(R.drawable.png_test, options);
+        assertEquals(bPng.getConfig(), config);
+
+        // Compress the PNG image to WebP format (Quality=90) and decode it back.
+        // This will test end-to-end WebP encoding and decoding.
+        ByteArrayOutputStream oStreamWebp = new ByteArrayOutputStream();
+        assertTrue(bPng.compress(CompressFormat.WEBP, 90, oStreamWebp));
+        InputStream iStreamWebp = new ByteArrayInputStream(oStreamWebp.toByteArray());
+        Bitmap bWebp2 = decodeOpaqueImage(iStreamWebp, options);
+        compareBitmaps(bPng, bWebp2, tolerance, true, bPng.isPremultiplied());
     }
 
     @Test
     public void testDecodeStream5() {
+        final int tolerance = 72;
         BitmapFactory.Options options = new BitmapFactory.Options();
-        for (int k = 0; k < COLOR_CONFIGS_RGBA.length; ++k) {
-            options.inPreferredConfig = COLOR_CONFIGS_RGBA[k];
+        options.inPreferredConfig = Config.ARGB_8888;
 
-            // Decode the PNG & WebP (google_logo) images. The WebP image has
-            // been encoded from PNG image.
-            InputStream iStreamPng = obtainInputStream(R.drawable.google_logo_1);
-            Bitmap bPng = BitmapFactory.decodeStream(iStreamPng, null, options);
-            assertNotNull(bPng);
-            assertEquals(bPng.getConfig(), COLOR_CONFIGS_RGBA[k]);
-            assertTrue(bPng.isPremultiplied());
-            assertTrue(bPng.hasAlpha());
+        // Decode the PNG & WebP (google_logo) images. The WebP image has
+        // been encoded from PNG image.
+        InputStream iStreamPng = obtainInputStream(R.drawable.google_logo_1);
+        Bitmap bPng = BitmapFactory.decodeStream(iStreamPng, null, options);
+        assertNotNull(bPng);
+        assertEquals(bPng.getConfig(), Config.ARGB_8888);
+        assertTrue(bPng.isPremultiplied());
+        assertTrue(bPng.hasAlpha());
 
-            // Decode the corresponding WebP (transparent) image (google_logo_2.webp).
-            InputStream iStreamWebP1 = obtainInputStream(R.drawable.google_logo_2);
-            Bitmap bWebP1 = BitmapFactory.decodeStream(iStreamWebP1, null, options);
-            assertNotNull(bWebP1);
-            assertEquals(bWebP1.getConfig(), COLOR_CONFIGS_RGBA[k]);
-            assertTrue(bWebP1.isPremultiplied());
-            assertTrue(bWebP1.hasAlpha());
-            compareBitmaps(bPng, bWebP1, COLOR_TOLS_RGBA[k], true, bPng.isPremultiplied());
+        // Decode the corresponding WebP (transparent) image (google_logo_2.webp).
+        InputStream iStreamWebP1 = obtainInputStream(R.drawable.google_logo_2);
+        Bitmap bWebP1 = BitmapFactory.decodeStream(iStreamWebP1, null, options);
+        assertNotNull(bWebP1);
+        assertEquals(bWebP1.getConfig(), Config.ARGB_8888);
+        assertTrue(bWebP1.isPremultiplied());
+        assertTrue(bWebP1.hasAlpha());
+        compareBitmaps(bPng, bWebP1, tolerance, true, bPng.isPremultiplied());
 
-            // Compress the PNG image to WebP format (Quality=90) and decode it back.
-            // This will test end-to-end WebP encoding and decoding.
-            ByteArrayOutputStream oStreamWebp = new ByteArrayOutputStream();
-            assertTrue(bPng.compress(CompressFormat.WEBP, 90, oStreamWebp));
-            InputStream iStreamWebp2 = new ByteArrayInputStream(oStreamWebp.toByteArray());
-            Bitmap bWebP2 = BitmapFactory.decodeStream(iStreamWebp2, null, options);
-            assertNotNull(bWebP2);
-            assertEquals(bWebP2.getConfig(), COLOR_CONFIGS_RGBA[k]);
-            assertTrue(bWebP2.isPremultiplied());
-            assertTrue(bWebP2.hasAlpha());
-            compareBitmaps(bPng, bWebP2, COLOR_TOLS_RGBA[k], true, bPng.isPremultiplied());
-        }
+        // Compress the PNG image to WebP format (Quality=90) and decode it back.
+        // This will test end-to-end WebP encoding and decoding.
+        ByteArrayOutputStream oStreamWebp = new ByteArrayOutputStream();
+        assertTrue(bPng.compress(CompressFormat.WEBP, 90, oStreamWebp));
+        InputStream iStreamWebp2 = new ByteArrayInputStream(oStreamWebp.toByteArray());
+        Bitmap bWebP2 = BitmapFactory.decodeStream(iStreamWebp2, null, options);
+        assertNotNull(bWebP2);
+        assertEquals(bWebP2.getConfig(), Config.ARGB_8888);
+        assertTrue(bWebP2.isPremultiplied());
+        assertTrue(bWebP2.hasAlpha());
+        compareBitmaps(bPng, bWebP2, tolerance, true, bPng.isPremultiplied());
     }
 
     @Test
@@ -315,47 +336,48 @@
         assertEquals(START_WIDTH, b.getWidth());
     }
 
+
+    // TODO: Better parameterize this and split it up.
     @Test
-    public void testDecodeFileDescriptor3() throws IOException {
+    @Parameters(method = "testImages")
+    public void testDecodeFileDescriptor3(TestImage testImage) throws IOException {
         // Arbitrary offsets to use. If the offset of the FD matches the offset of the image,
         // decoding should succeed, but if they do not match, decoding should fail.
-        long ACTUAL_OFFSETS[] = new long[] { 0, 17 };
-        for (int RES_ID : RES_IDS) {
-            for (int j = 0; j < ACTUAL_OFFSETS.length; ++j) {
-                // FIXME: The purgeable test should attempt to purge the memory
-                // to force a re-decode.
-                for (boolean TEST_PURGEABLE : new boolean[] { false, true }) {
-                    BitmapFactory.Options opts = new BitmapFactory.Options();
-                    opts.inPurgeable = TEST_PURGEABLE;
-                    opts.inInputShareable = TEST_PURGEABLE;
+        final long[] actual_offsets = new long[] { 0, 17 };
+        for (int j = 0; j < actual_offsets.length; ++j) {
+            // FIXME: The purgeable test should attempt to purge the memory
+            // to force a re-decode.
+            for (boolean purgeable : new boolean[] { false, true }) {
+                BitmapFactory.Options opts = new BitmapFactory.Options();
+                opts.inPurgeable = purgeable;
+                opts.inInputShareable = purgeable;
 
-                    long actualOffset = ACTUAL_OFFSETS[j];
-                    String path = obtainPath(RES_ID, actualOffset);
-                    RandomAccessFile file = new RandomAccessFile(path, "r");
-                    FileDescriptor fd = file.getFD();
-                    assertTrue(fd.valid());
+                long actualOffset = actual_offsets[j];
+                String path = obtainPath(testImage.id, actualOffset);
+                RandomAccessFile file = new RandomAccessFile(path, "r");
+                FileDescriptor fd = file.getFD();
+                assertTrue(fd.valid());
 
-                    // Set the offset to ACTUAL_OFFSET
-                    file.seek(actualOffset);
-                    assertEquals(file.getFilePointer(), actualOffset);
+                // Set the offset to ACTUAL_OFFSET
+                file.seek(actualOffset);
+                assertEquals(file.getFilePointer(), actualOffset);
 
-                    // Now decode. This should be successful and leave the offset
-                    // unchanged.
-                    Bitmap b = BitmapFactory.decodeFileDescriptor(fd, null, opts);
-                    assertNotNull(b);
-                    assertEquals(file.getFilePointer(), actualOffset);
+                // Now decode. This should be successful and leave the offset
+                // unchanged.
+                Bitmap b = BitmapFactory.decodeFileDescriptor(fd, null, opts);
+                assertNotNull(b);
+                assertEquals(file.getFilePointer(), actualOffset);
 
-                    // Now use the other offset. It should fail to decode, and
-                    // the offset should remain unchanged.
-                    long otherOffset = ACTUAL_OFFSETS[(j + 1) % ACTUAL_OFFSETS.length];
-                    assertFalse(otherOffset == actualOffset);
-                    file.seek(otherOffset);
-                    assertEquals(file.getFilePointer(), otherOffset);
+                // Now use the other offset. It should fail to decode, and
+                // the offset should remain unchanged.
+                long otherOffset = actual_offsets[(j + 1) % actual_offsets.length];
+                assertFalse(otherOffset == actualOffset);
+                file.seek(otherOffset);
+                assertEquals(file.getFilePointer(), otherOffset);
 
-                    b = BitmapFactory.decodeFileDescriptor(fd, null, opts);
-                    assertNull(b);
-                    assertEquals(file.getFilePointer(), otherOffset);
-                }
+                b = BitmapFactory.decodeFileDescriptor(fd, null, opts);
+                assertNull(b);
+                assertEquals(file.getFilePointer(), otherOffset);
             }
         }
     }
@@ -493,18 +515,17 @@
     }
 
     @Test
-    public void testDecodeReuseFormats() {
+    @Parameters(method = "testImages")
+    public void testDecodeReuseFormats(TestImage testImage) {
         // reuse should support all image formats
-        for (int i = 0; i < RES_IDS.length; ++i) {
-            Bitmap reuseBuffer = Bitmap.createBitmap(1000000, 1, Bitmap.Config.ALPHA_8);
+        Bitmap reuseBuffer = Bitmap.createBitmap(1000000, 1, Bitmap.Config.ALPHA_8);
 
-            BitmapFactory.Options options = new BitmapFactory.Options();
-            options.inBitmap = reuseBuffer;
-            options.inSampleSize = 4;
-            options.inScaled = false;
-            Bitmap decoded = BitmapFactory.decodeResource(mRes, RES_IDS[i], options);
-            assertSame(reuseBuffer, decoded);
-        }
+        BitmapFactory.Options options = new BitmapFactory.Options();
+        options.inBitmap = reuseBuffer;
+        options.inSampleSize = 4;
+        options.inScaled = false;
+        Bitmap decoded = BitmapFactory.decodeResource(mRes, testImage.id, options);
+        assertSame(reuseBuffer, decoded);
     }
 
     @Test
@@ -792,27 +813,29 @@
 
     @Test
     @CddTest(requirement = "5.1.5/C-0-6")
-    public void testDng() {
-        DNG[] dngs = new DNG[]{
-            new DNG(R.raw.sample_1mp, 600, 338),
-            new DNG(R.raw.sample_arw, 1616, 1080),
-            new DNG(R.raw.sample_cr2, 2304, 1536),
-            new DNG(R.raw.sample_nef, 4608, 3072),
-            new DNG(R.raw.sample_nrw, 4000, 3000),
-            new DNG(R.raw.sample_orf, 3200, 2400),
-            new DNG(R.raw.sample_pef, 4928, 3264),
-            new DNG(R.raw.sample_raf, 2048, 1536),
-            new DNG(R.raw.sample_rw2, 1920, 1440),
-            new DNG(R.raw.sample_srw, 5472, 3648),
-        };
+    @Parameters(method = "parametersForTestDng")
+    @LargeTest
+    public void testDng(DNG dng) {
+        // No scaling
+        Bitmap bm = BitmapFactory.decodeResource(mRes, dng.resId, mOpt1);
+        assertNotNull(bm);
+        assertEquals(dng.width, bm.getWidth());
+        assertEquals(dng.height, bm.getHeight());
+    }
 
-        for (DNG dng : dngs) {
-            // No scaling
-            Bitmap bm = BitmapFactory.decodeResource(mRes, dng.resId, mOpt1);
-            assertNotNull(bm);
-            assertEquals(dng.width, bm.getWidth());
-            assertEquals(dng.height, bm.getHeight());
-        }
+    private Object[] parametersForTestDng() {
+        return new Object[]{
+                new DNG(R.raw.sample_1mp, 600, 338),
+                new DNG(R.raw.sample_arw, 1616, 1080),
+                new DNG(R.raw.sample_cr2, 2304, 1536),
+                new DNG(R.raw.sample_nef, 4608, 3072),
+                new DNG(R.raw.sample_nrw, 4000, 3000),
+                new DNG(R.raw.sample_orf, 3200, 2400),
+                new DNG(R.raw.sample_pef, 4928, 3264),
+                new DNG(R.raw.sample_raf, 2048, 1536),
+                new DNG(R.raw.sample_rw2, 1920, 1440),
+                new DNG(R.raw.sample_srw, 5472, 3648),
+        };
     }
 
     @Test
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
index a2025bd..fc510a7 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
@@ -46,7 +46,6 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.compatibility.common.util.ColorUtils;
 import com.android.compatibility.common.util.WidgetTestUtils;
@@ -66,8 +65,11 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 @SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(JUnitParamsRunner.class)
 public class BitmapTest {
     // small alpha values cause color values to be pre-multiplied down, losing accuracy
     private static final int PREMUL_COLOR = Color.argb(2, 255, 254, 253);
@@ -1959,7 +1961,7 @@
         nValidateBitmapInfo(bitmap, 10, 20, true);
         bitmap.recycle();
         nValidateBitmapInfo(bitmap, 10, 20, true);
-        nValidateNdkAccessAfterRecycle(bitmap);
+        nValidateNdkAccessFails(bitmap);
     }
 
     @Test
@@ -2022,15 +2024,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 +2163,83 @@
         }
     }
 
+    @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 Object[] parametersForTestNdkInfo() {
+        return new Object[] {
+            new Object[] { Config.ALPHA_8,   8 /* ANDROID_BITMAP_FORMAT_A_8 */ },
+            new Object[] { Config.ARGB_8888, 1 /* ANDROID_BITMAP_FORMAT_RGBA_8888 */ },
+            new Object[] { Config.RGB_565,   4 /* ANDROID_BITMAP_FORMAT_RGB_565 */ },
+            new Object[] { Config.RGBA_F16,  9 /* ANDROID_BITMAP_FORMAT_RGBA_F16*/ },
+        };
+    }
+
+    @Test
+    @Parameters(method = "parametersForTestNdkInfo")
+    public void testNdkInfo(Config config, int androidBitmapFormat) {
+        // Arbitrary width and height.
+        final int width = 13;
+        final int height = 7;
+        boolean[] trueFalse = new boolean[] { true, false };
+        for (boolean hasAlpha : trueFalse) {
+            for (boolean premultiplied : trueFalse) {
+                Bitmap bm = Bitmap.createBitmap(width, height, config, hasAlpha);
+                bm.setPremultiplied(premultiplied);
+                nTestInfo(bm, androidBitmapFormat, width, height, bm.hasAlpha(),
+                        bm.isPremultiplied());
+                bm = bm.copy(Bitmap.Config.HARDWARE, false);
+                if (config == Bitmap.Config.ALPHA_8) {
+                    // ALPHA_8 is not supported in HARDWARE. b/141480329
+                    assertNull(bm);
+                } else {
+                    assertNotNull(bm);
+                    nTestInfo(bm, androidBitmapFormat, width, height, bm.hasAlpha(),
+                            bm.isPremultiplied());
+                }
+            }
+        }
+    }
+
     private void strictModeTest(Runnable runnable) {
         StrictMode.ThreadPolicy originalPolicy = StrictMode.getThreadPolicy();
         StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
@@ -2177,13 +2256,39 @@
 
     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;
+    static final int ANDROID_BITMAP_FORMAT_RGBA_8888 = 1;
     private static final int ANDROID_BITMAP_FORMAT_RGB_565 = 4;
-    private static native int nGetFormat(Bitmap bitmap);
+    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),
+    };
+
+    static native int nGetFormat(Bitmap bitmap);
+
+    private static native void nTestInfo(Bitmap bm, int androidBitmapFormat, int width, int height,
+            boolean hasAlpha, boolean premultiplied);
 
     private static HardwareBuffer createTestBuffer(int width, int height, boolean cpuAccess) {
         long usage = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE;
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..4591268 100644
--- a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
@@ -51,11 +51,9 @@
 import androidx.core.content.FileProvider;
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.compatibility.common.util.BitmapUtils;
 
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -71,11 +69,11 @@
 import java.util.function.Supplier;
 import java.util.function.ToIntFunction;
 
-@RunWith(AndroidJUnit4.class)
-public class ImageDecoderTest {
-    private Resources mRes;
-    private ContentResolver mContentResolver;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
 
+@RunWith(JUnitParamsRunner.class)
+public class ImageDecoderTest {
     private static final class Record {
         public final int resId;
         public final int width;
@@ -94,17 +92,19 @@
 
     private static final ColorSpace sSRGB = ColorSpace.get(ColorSpace.Named.SRGB);
 
-    private static final Record[] RECORDS = new Record[] {
-        new Record(R.drawable.baseline_jpeg, 1280, 960, "image/jpeg", sSRGB),
-        new Record(R.drawable.png_test, 640, 480, "image/png", sSRGB),
-        new Record(R.drawable.gif_test, 320, 240, "image/gif", sSRGB),
-        new Record(R.drawable.bmp_test, 320, 240, "image/bmp", sSRGB),
-        new Record(R.drawable.webp_test, 640, 480, "image/webp", sSRGB),
-        new Record(R.drawable.google_chrome, 256, 256, "image/x-ico", sSRGB),
-        new Record(R.drawable.color_wheel, 128, 128, "image/x-ico", sSRGB),
-        new Record(R.raw.sample_1mp, 600, 338, "image/x-adobe-dng", sSRGB),
-        new Record(R.raw.heifwriter_input, 1920, 1080, "image/heif", sSRGB),
-    };
+    private Object[] getRecords() {
+        return new Record[] {
+            new Record(R.drawable.baseline_jpeg, 1280, 960, "image/jpeg", sSRGB),
+            new Record(R.drawable.png_test, 640, 480, "image/png", sSRGB),
+            new Record(R.drawable.gif_test, 320, 240, "image/gif", sSRGB),
+            new Record(R.drawable.bmp_test, 320, 240, "image/bmp", sSRGB),
+            new Record(R.drawable.webp_test, 640, 480, "image/webp", sSRGB),
+            new Record(R.drawable.google_chrome, 256, 256, "image/x-ico", sSRGB),
+            new Record(R.drawable.color_wheel, 128, 128, "image/x-ico", sSRGB),
+            new Record(R.raw.sample_1mp, 600, 338, "image/x-adobe-dng", sSRGB),
+            new Record(R.raw.heifwriter_input, 1920, 1080, "image/heif", sSRGB),
+        };
+    }
 
     // offset is how many bytes to offset the beginning of the image.
     // extra is how many bytes to append at the end.
@@ -115,7 +115,7 @@
     }
 
     private void writeToStream(OutputStream output, int resId, int offset, int extra) {
-        InputStream input = mRes.openRawResource(resId);
+        InputStream input = getResources().openRawResource(resId);
         byte[] buffer = new byte[4096];
         int bytesRead;
         try {
@@ -194,11 +194,12 @@
     }
 
     private Uri getAsResourceUri(int resId) {
+        Resources res = getResources();
         return new Uri.Builder()
                 .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
-                .authority(mRes.getResourcePackageName(resId))
-                .appendPath(mRes.getResourceTypeName(resId))
-                .appendPath(mRes.getResourceEntryName(resId))
+                .authority(res.getResourcePackageName(resId))
+                .appendPath(res.getResourceTypeName(resId))
+                .appendPath(res.getResourceEntryName(resId))
                 .build();
     }
 
@@ -229,102 +230,105 @@
     };
 
     @Test
-    public void testUris() {
-        for (Record record : RECORDS) {
-            int resId = record.resId;
-            String name = mRes.getResourceEntryName(resId);
-            for (UriCreator f : mUriCreators) {
-                ImageDecoder.Source src = null;
-                Uri uri = f.apply(resId);
-                String fullName = name + ": " + uri.toString();
-                src = ImageDecoder.createSource(mContentResolver, uri);
+    @Parameters(method = "getRecords")
+    public void testUris(Record record) {
+        int resId = record.resId;
+        String name = getResources().getResourceEntryName(resId);
+        for (UriCreator f : mUriCreators) {
+            ImageDecoder.Source src = null;
+            Uri uri = f.apply(resId);
+            String fullName = name + ": " + uri.toString();
+            src = ImageDecoder.createSource(getContentResolver(), uri);
 
-                assertNotNull("failed to create Source for " + fullName, src);
-                try {
-                    Drawable d = ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
-                        decoder.setOnPartialImageListener((e) -> {
-                            fail("error for image " + fullName + ":\n" + e);
-                            return false;
-                        });
+            assertNotNull("failed to create Source for " + fullName, src);
+            try {
+                Drawable d = ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
+                    decoder.setOnPartialImageListener((e) -> {
+                        fail("error for image " + fullName + ":\n" + e);
+                        return false;
                     });
-                    assertNotNull("failed to create drawable for " + fullName, d);
-                } catch (IOException e) {
-                    fail("exception for image " + fullName + ":\n" + e);
-                }
+                });
+                assertNotNull("failed to create drawable for " + fullName, d);
+            } catch (IOException e) {
+                fail("exception for image " + fullName + ":\n" + e);
             }
         }
     }
 
-    @Before
-    public void setup() {
-        mRes = InstrumentationRegistry.getTargetContext().getResources();
-        mContentResolver = InstrumentationRegistry.getTargetContext().getContentResolver();
+    private Resources getResources() {
+        return InstrumentationRegistry.getTargetContext().getResources();
+    }
+
+    private ContentResolver getContentResolver() {
+        return InstrumentationRegistry.getTargetContext().getContentResolver();
     }
 
     @Test
-    public void testInfo() {
-        for (Record record : RECORDS) {
-            for (SourceCreator f : mCreators) {
-                ImageDecoder.Source src = f.apply(record.resId);
-                assertNotNull(src);
-                try {
-                    ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
-                        assertEquals(record.width,  info.getSize().getWidth());
-                        assertEquals(record.height, info.getSize().getHeight());
-                        assertEquals(record.mimeType, info.getMimeType());
-                        assertSame(record.colorSpace, info.getColorSpace());
-                    });
-                } catch (IOException e) {
-                    fail("Failed " + getAsResourceUri(record.resId) + " with exception " + e);
-                }
+    @Parameters(method = "getRecords")
+    public void testInfo(Record record) {
+        for (SourceCreator f : mCreators) {
+            ImageDecoder.Source src = f.apply(record.resId);
+            assertNotNull(src);
+            try {
+                ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
+                    assertEquals(record.width,  info.getSize().getWidth());
+                    assertEquals(record.height, info.getSize().getHeight());
+                    assertEquals(record.mimeType, info.getMimeType());
+                    assertSame(record.colorSpace, info.getColorSpace());
+                });
+            } catch (IOException e) {
+                fail("Failed " + getAsResourceUri(record.resId) + " with exception " + e);
             }
         }
     }
 
     @Test
-    public void testDecodeDrawable() {
-        for (Record record : RECORDS) {
-            for (SourceCreator f : mCreators) {
-                ImageDecoder.Source src = f.apply(record.resId);
-                assertNotNull(src);
+    @Parameters(method = "getRecords")
+    public void testDecodeDrawable(Record record) {
+        for (SourceCreator f : mCreators) {
+            ImageDecoder.Source src = f.apply(record.resId);
+            assertNotNull(src);
 
-                try {
-                    Drawable drawable = ImageDecoder.decodeDrawable(src);
-                    assertNotNull(drawable);
-                    assertEquals(record.width,  drawable.getIntrinsicWidth());
-                    assertEquals(record.height, drawable.getIntrinsicHeight());
-                } catch (IOException e) {
-                    fail("Failed with exception " + e);
-                }
+            try {
+                Drawable drawable = ImageDecoder.decodeDrawable(src);
+                assertNotNull(drawable);
+                assertEquals(record.width,  drawable.getIntrinsicWidth());
+                assertEquals(record.height, drawable.getIntrinsicHeight());
+            } catch (IOException e) {
+                fail("Failed with exception " + e);
             }
         }
     }
 
     @Test
-    public void testDecodeBitmap() {
-        for (Record record : RECORDS) {
-            for (SourceCreator f : mCreators) {
-                ImageDecoder.Source src = f.apply(record.resId);
-                assertNotNull(src);
+    @Parameters(method = "getRecords")
+    public void testDecodeBitmap(Record record) {
+        for (SourceCreator f : mCreators) {
+            ImageDecoder.Source src = f.apply(record.resId);
+            assertNotNull(src);
 
-                try {
-                    Bitmap bm = ImageDecoder.decodeBitmap(src);
-                    assertNotNull(bm);
-                    assertEquals(record.width, bm.getWidth());
-                    assertEquals(record.height, bm.getHeight());
-                    assertFalse(bm.isMutable());
-                    // FIXME: This may change for small resources, etc.
-                    assertEquals(Bitmap.Config.HARDWARE, bm.getConfig());
-                } catch (IOException e) {
-                    fail("Failed with exception " + e);
-                }
+            try {
+                Bitmap bm = ImageDecoder.decodeBitmap(src);
+                assertNotNull(bm);
+                assertEquals(record.width, bm.getWidth());
+                assertEquals(record.height, bm.getHeight());
+                assertFalse(bm.isMutable());
+                // FIXME: This may change for small resources, etc.
+                assertEquals(Bitmap.Config.HARDWARE, bm.getConfig());
+            } catch (IOException e) {
+                fail("Failed with exception " + e);
             }
         }
     }
 
+    // Return a single Record for simple tests.
+    private Record getRecord() {
+        return ((Record[]) getRecords())[0];
+    }
+
     @Test(expected = IllegalArgumentException.class)
     public void testSetBogusAllocator() {
-        ImageDecoder.Source src = mCreators[0].apply(RECORDS[0].resId);
+        ImageDecoder.Source src = mCreators[0].apply(getRecord().resId);
         try {
             ImageDecoder.decodeBitmap(src, (decoder, info, s) -> decoder.setAllocator(15));
         } catch (IOException e) {
@@ -341,7 +345,7 @@
 
     @Test
     public void testGetAllocator() {
-        final int resId = RECORDS[0].resId;
+        final int resId = getRecord().resId;
         ImageDecoder.Source src = mCreators[0].apply(resId);
         try {
             ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
@@ -357,7 +361,8 @@
     }
 
     @Test
-    public void testSetAllocatorDecodeBitmap() {
+    @Parameters(method = "getRecords")
+    public void testSetAllocatorDecodeBitmap(Record record) {
         class Listener implements ImageDecoder.OnHeaderDecodedListener {
             public int allocator;
             public boolean doCrop;
@@ -378,48 +383,45 @@
         Listener l = new Listener();
 
         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);
+        Resources res = getResources();
+        ImageDecoder.Source src = ImageDecoder.createSource(res, 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);
+                    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());
+
+                            if (!doScale && !doCrop) {
+                                Bitmap reference = BitmapFactory.decodeResource(res,
+                                        record.resId, null);
+                                assertNotNull(reference);
+                                BitmapUtils.compareBitmaps(bm, reference);
                             }
-                            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());
-
-                                    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;
-                            }
-                        }
+                            break;
+                        default:
+                            String name = getAsResourceUri(record.resId).toString();
+                            assertEquals("image " + name + "; allocator: " + allocator,
+                                         Bitmap.Config.HARDWARE, bm.getConfig());
+                            break;
                     }
                 }
             }
@@ -428,7 +430,7 @@
 
     @Test
     public void testGetUnpremul() {
-        final int resId = RECORDS[0].resId;
+        final int resId = getRecord().resId;
         ImageDecoder.Source src = mCreators[0].apply(resId);
         try {
             ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
@@ -484,7 +486,7 @@
                 (canvas) -> PixelFormat.UNKNOWN,
                 null,
         };
-        final int resId = RECORDS[0].resId;
+        final int resId = getRecord().resId;
         ImageDecoder.Source src = mCreators[0].apply(resId);
         try {
             ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
@@ -501,7 +503,8 @@
     }
 
     @Test
-    public void testPostProcessor() {
+    @Parameters(method = "getRecords")
+    public void testPostProcessor(Record record) {
         class Listener implements ImageDecoder.OnHeaderDecodedListener {
             public boolean requireSoftware;
             @Override
@@ -518,44 +521,41 @@
         };
         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(getResources(), 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);
-                            }
-                        }
-                    } else {
-                        assertEquals(bitmap.getConfig(), Bitmap.Config.HARDWARE);
+            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);
             }
         }
     }
 
     @Test
     public void testNinepatchWithDensityNone() {
+        Resources res = getResources();
         TypedValue value = new TypedValue();
-        InputStream is = mRes.openRawResource(R.drawable.ninepatch_nodpi, value);
+        InputStream is = res.openRawResource(R.drawable.ninepatch_nodpi, value);
         // This does not call ImageDecoder directly because this entry point is not public.
-        Drawable dr = Drawable.createFromResourceStream(mRes, value, is, null, null);
+        Drawable dr = Drawable.createFromResourceStream(res, value, is, null, null);
         assertNotNull(dr);
         assertEquals(5, dr.getIntrinsicWidth());
         assertEquals(5, dr.getIntrinsicHeight());
@@ -632,7 +632,8 @@
     }
 
     @Test
-    public void testPostProcessorAndAddedTransparency() {
+    @Parameters(method = "getRecords")
+    public void testPostProcessorAndAddedTransparency(Record record) {
         class Listener implements ImageDecoder.OnHeaderDecodedListener {
             public boolean requireSoftware;
             @Override
@@ -646,18 +647,16 @@
         };
         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);
-                    try {
-                        Bitmap bm = ImageDecoder.decodeBitmap(src, l);
-                        assertTrue(bm.hasAlpha());
-                        assertTrue(bm.isPremultiplied());
-                    } catch (IOException e) {
-                        fail("Failed with exception " + e);
-                    }
+        for (SourceCreator f : mCreators) {
+            for (boolean requireSoftware : trueFalse) {
+                l.requireSoftware = requireSoftware;
+                ImageDecoder.Source src = f.apply(record.resId);
+                try {
+                    Bitmap bm = ImageDecoder.decodeBitmap(src, l);
+                    assertTrue(bm.hasAlpha());
+                    assertTrue(bm.isPremultiplied());
+                } catch (IOException e) {
+                    fail("Failed with exception " + e);
                 }
             }
         }
@@ -677,7 +676,7 @@
 
     @Test(expected = IllegalArgumentException.class)
     public void testPostProcessorInvalidReturn() {
-        ImageDecoder.Source src = mCreators[0].apply(RECORDS[0].resId);
+        ImageDecoder.Source src = mCreators[0].apply(getRecord().resId);
         try {
             ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
                 decoder.setPostProcessor((c) -> 42);
@@ -689,7 +688,7 @@
 
     @Test(expected = IllegalStateException.class)
     public void testPostProcessorAndUnpremul() {
-        ImageDecoder.Source src = mCreators[0].apply(RECORDS[0].resId);
+        ImageDecoder.Source src = mCreators[0].apply(getRecord().resId);
         try {
             ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
                 decoder.setUnpremultipliedRequired(true);
@@ -701,7 +700,8 @@
     }
 
     @Test
-    public void testPostProcessorAndScale() {
+    @Parameters(method = "getRecords")
+    public void testPostProcessorAndScale(Record record) {
         class PostProcessorWithSize implements PostProcessor {
             public int width;
             public int height;
@@ -713,21 +713,19 @@
             };
         };
         final PostProcessorWithSize pp = new PostProcessorWithSize();
-        for (Record record : RECORDS) {
-            pp.width =  record.width  / 2;
-            pp.height = record.height / 2;
-            for (SourceCreator f : mCreators) {
-                ImageDecoder.Source src = f.apply(record.resId);
-                try {
-                    Drawable drawable = ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
-                        decoder.setTargetSize(pp.width, pp.height);
-                        decoder.setPostProcessor(pp);
-                    });
-                    assertEquals(pp.width,  drawable.getIntrinsicWidth());
-                    assertEquals(pp.height, drawable.getIntrinsicHeight());
-                } catch (IOException e) {
-                    fail("Failed " + getAsResourceUri(record.resId) + " with exception " + e);
-                }
+        pp.width =  record.width  / 2;
+        pp.height = record.height / 2;
+        for (SourceCreator f : mCreators) {
+            ImageDecoder.Source src = f.apply(record.resId);
+            try {
+                Drawable drawable = ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
+                    decoder.setTargetSize(pp.width, pp.height);
+                    decoder.setPostProcessor(pp);
+                });
+                assertEquals(pp.width,  drawable.getIntrinsicWidth());
+                assertEquals(pp.height, drawable.getIntrinsicHeight());
+            } catch (IOException e) {
+                fail("Failed " + getAsResourceUri(record.resId) + " with exception " + e);
             }
         }
     }
@@ -748,21 +746,20 @@
     }
 
     @Test
-    public void testSampleSize() {
-        for (Record record : RECORDS) {
-            final String name = getAsResourceUri(record.resId).toString();
-            for (int sampleSize : new int[] { 2, 3, 4, 8, 32 }) {
-                ImageDecoder.Source src = mCreators[0].apply(record.resId);
-                try {
-                    Drawable dr = ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
-                        decoder.setTargetSampleSize(sampleSize);
-                    });
+    @Parameters(method = "getRecords")
+    public void testSampleSize(Record record) {
+        final String name = getAsResourceUri(record.resId).toString();
+        for (int sampleSize : new int[] { 2, 3, 4, 8, 32 }) {
+            ImageDecoder.Source src = mCreators[0].apply(record.resId);
+            try {
+                Drawable dr = ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
+                    decoder.setTargetSampleSize(sampleSize);
+                });
 
-                    checkSampleSize(name, record.width, sampleSize, dr.getIntrinsicWidth());
-                    checkSampleSize(name, record.height, sampleSize, dr.getIntrinsicHeight());
-                } catch (IOException e) {
-                    fail("Failed " + name + " with exception " + e);
-                }
+                checkSampleSize(name, record.width, sampleSize, dr.getIntrinsicWidth());
+                checkSampleSize(name, record.height, sampleSize, dr.getIntrinsicHeight());
+            } catch (IOException e) {
+                fail("Failed " + name + " with exception " + e);
             }
         }
     }
@@ -770,25 +767,23 @@
     private interface SampleSizeSupplier extends ToIntFunction<Size> {};
 
     @Test
-    public void testLargeSampleSize() {
-        for (Record record : RECORDS) {
-            for (SourceCreator f : mCreators) {
-                for (SampleSizeSupplier supplySampleSize : new SampleSizeSupplier[] {
-                        (size) -> size.getWidth(),
-                        (size) -> size.getWidth() + 5,
-                        (size) -> size.getWidth() * 5,
-                }) {
-                    ImageDecoder.Source src = f.apply(record.resId);
-                    try {
-                        Drawable dr = ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
-                            int sampleSize = supplySampleSize.applyAsInt(info.getSize());
-                            decoder.setTargetSampleSize(sampleSize);
-                        });
-                        assertEquals(1, dr.getIntrinsicWidth());
-                    } catch (IOException e) {
-                        fail("Failed with exception " + e);
-                    }
-                }
+    @Parameters(method = "getRecords")
+    public void testLargeSampleSize(Record record) {
+        ImageDecoder.Source src = mCreators[0].apply(record.resId);
+        for (SampleSizeSupplier supplySampleSize : new SampleSizeSupplier[] {
+                (size) -> size.getWidth(),
+                (size) -> size.getWidth() + 5,
+                (size) -> size.getWidth() * 5,
+        }) {
+            try {
+                Drawable dr = ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
+                    int sampleSize = supplySampleSize.applyAsInt(info.getSize());
+                    decoder.setTargetSampleSize(sampleSize);
+                });
+                assertEquals(1, dr.getIntrinsicWidth());
+            } catch (Exception e) {
+                String file = getAsResourceUri(record.resId).toString();
+                fail("Failed to decode " + file + " with exception " + e);
             }
         }
     }
@@ -851,7 +846,7 @@
                 null,
         };
 
-        final int resId = RECORDS[0].resId;
+        final int resId = getRecord().resId;
         ImageDecoder.Source src = mCreators[0].apply(resId);
         try {
             ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
@@ -897,7 +892,7 @@
         int mPosition;
 
         ExceptionStream(int resId, int exceptionPosition) {
-            mInputStream = mRes.openRawResource(resId);
+            mInputStream = getResources().openRawResource(resId);
             mExceptionPosition = exceptionPosition;
             mPosition = 0;
         }
@@ -930,7 +925,8 @@
     @Test
     public void testExceptionInStream() throws Throwable {
         InputStream is = new ExceptionStream(R.drawable.animated, 27570);
-        ImageDecoder.Source src = ImageDecoder.createSource(mRes, is, Bitmap.DENSITY_NONE);
+        ImageDecoder.Source src = ImageDecoder.createSource(getResources(), is,
+                Bitmap.DENSITY_NONE);
         Drawable dr = null;
         try {
             dr = ImageDecoder.decodeDrawable(src);
@@ -947,7 +943,8 @@
     }
 
     @Test
-    public void testOnPartialImage() {
+    @Parameters(method = "getRecords")
+    public void testOnPartialImage(Record record) {
         class PartialImageCallback implements OnPartialImageListener {
             public boolean wasCalled;
             public boolean returnDrawable;
@@ -962,46 +959,44 @@
         };
         final PartialImageCallback callback = new PartialImageCallback();
         boolean abortDecode[] = new boolean[] { true, false };
-        for (Record record : RECORDS) {
-            byte[] bytes = getAsByteArray(record.resId);
-            int truncatedLength = bytes.length / 2;
-            if (record.mimeType.equals("image/x-ico")
-                    || record.mimeType.equals("image/x-adobe-dng")
-                    || record.mimeType.equals("image/heif")) {
-                // FIXME (scroggo): Some codecs currently do not support incomplete images.
-                continue;
-            }
-            for (boolean abort : abortDecode) {
-                ImageDecoder.Source src = ImageDecoder.createSource(
-                        ByteBuffer.wrap(bytes, 0, truncatedLength));
-                callback.wasCalled = false;
-                callback.returnDrawable = !abort;
-                callback.source = src;
-                try {
-                    Drawable drawable = ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
-                        decoder.setOnPartialImageListener(callback);
-                    });
-                    assertFalse(abort);
-                    assertNotNull(drawable);
-                    assertEquals(record.width,  drawable.getIntrinsicWidth());
-                    assertEquals(record.height, drawable.getIntrinsicHeight());
-                } catch (IOException e) {
-                    assertTrue(abort);
-                }
-                assertTrue(callback.wasCalled);
-            }
-
-            // null listener behaves as if onPartialImage returned false.
+        byte[] bytes = getAsByteArray(record.resId);
+        int truncatedLength = bytes.length / 2;
+        if (record.mimeType.equals("image/x-ico")
+                || record.mimeType.equals("image/x-adobe-dng")
+                || record.mimeType.equals("image/heif")) {
+            // FIXME (scroggo): Some codecs currently do not support incomplete images.
+            return;
+        }
+        for (boolean abort : abortDecode) {
             ImageDecoder.Source src = ImageDecoder.createSource(
                     ByteBuffer.wrap(bytes, 0, truncatedLength));
+            callback.wasCalled = false;
+            callback.returnDrawable = !abort;
+            callback.source = src;
             try {
-                ImageDecoder.decodeDrawable(src);
-                fail("Should have thrown an exception!");
-            } catch (DecodeException incomplete) {
-                // This is the correct behavior.
+                Drawable drawable = ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
+                    decoder.setOnPartialImageListener(callback);
+                });
+                assertFalse(abort);
+                assertNotNull(drawable);
+                assertEquals(record.width,  drawable.getIntrinsicWidth());
+                assertEquals(record.height, drawable.getIntrinsicHeight());
             } catch (IOException e) {
-                fail("Failed with exception " + e);
+                assertTrue(abort);
             }
+            assertTrue(callback.wasCalled);
+        }
+
+        // null listener behaves as if onPartialImage returned false.
+        ImageDecoder.Source src = ImageDecoder.createSource(
+                ByteBuffer.wrap(bytes, 0, truncatedLength));
+        try {
+            ImageDecoder.decodeDrawable(src);
+            fail("Should have thrown an exception!");
+        } catch (DecodeException incomplete) {
+            // This is the correct behavior.
+        } catch (IOException e) {
+            fail("Failed with exception " + e);
         }
     }
 
@@ -1061,7 +1056,7 @@
 
     @Test
     public void testGetMutable() {
-        final int resId = RECORDS[0].resId;
+        final int resId = getRecord().resId;
         ImageDecoder.Source src = mCreators[0].apply(resId);
         try {
             ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
@@ -1079,7 +1074,8 @@
     }
 
     @Test
-    public void testMutable() {
+    @Parameters(method = "getRecords")
+    public void testMutable(Record record) {
         int allocators[] = new int[] { ImageDecoder.ALLOCATOR_DEFAULT,
                                        ImageDecoder.ALLOCATOR_SOFTWARE,
                                        ImageDecoder.ALLOCATOR_SHARED_MEMORY };
@@ -1099,22 +1095,19 @@
         };
         HeaderListener l = new HeaderListener();
         boolean trueFalse[] = new boolean[] { true, false };
-        for (Record record : RECORDS) {
-            for (SourceCreator f : mCreators) {
-                for (boolean postProcess : trueFalse) {
-                    for (int allocator : allocators) {
-                        l.allocator = allocator;
-                        l.postProcess = postProcess;
+        ImageDecoder.Source src = mCreators[0].apply(record.resId);
+        for (boolean postProcess : trueFalse) {
+            for (int allocator : allocators) {
+                l.allocator = allocator;
+                l.postProcess = postProcess;
 
-                        ImageDecoder.Source src = f.apply(record.resId);
-                        try {
-                            Bitmap bm = ImageDecoder.decodeBitmap(src, l);
-                            assertTrue(bm.isMutable());
-                            assertNotEquals(Bitmap.Config.HARDWARE, bm.getConfig());
-                        } catch (IOException e) {
-                            fail("Failed with exception " + e);
-                        }
-                    }
+                try {
+                    Bitmap bm = ImageDecoder.decodeBitmap(src, l);
+                    assertTrue(bm.isMutable());
+                    assertNotEquals(Bitmap.Config.HARDWARE, bm.getConfig());
+                } catch (Exception e) {
+                    String file = getAsResourceUri(record.resId).toString();
+                    fail("Failed to decode " + file + " with exception " + e);
                 }
             }
         }
@@ -1122,7 +1115,7 @@
 
     @Test(expected = IllegalStateException.class)
     public void testMutableHardware() {
-        ImageDecoder.Source src = mCreators[0].apply(RECORDS[0].resId);
+        ImageDecoder.Source src = mCreators[0].apply(getRecord().resId);
         try {
             ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
                 decoder.setMutableRequired(true);
@@ -1135,7 +1128,7 @@
 
     @Test(expected = IllegalStateException.class)
     public void testMutableDrawable() {
-        ImageDecoder.Source src = mCreators[0].apply(RECORDS[0].resId);
+        ImageDecoder.Source src = mCreators[0].apply(getRecord().resId);
         try {
             ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
                 decoder.setMutableRequired(true);
@@ -1205,7 +1198,8 @@
     }
 
     @Test
-    public void testTargetSize() {
+    @Parameters(method = "getRecords")
+    public void testTargetSize(Record record) {
         class ResizeListener implements ImageDecoder.OnHeaderDecodedListener {
             public int width;
             public int height;
@@ -1218,49 +1212,41 @@
         ResizeListener l = new ResizeListener();
 
         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 = 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);
 
-                    ImageDecoder.Source src = f.apply(record.resId);
+            try {
+                Drawable drawable = ImageDecoder.decodeDrawable(src, l);
+                assertEquals(l.width,  drawable.getIntrinsicWidth());
+                assertEquals(l.height, drawable.getIntrinsicHeight());
 
-                    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);
-                    }
-                }
-
-                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());
-
-                    // 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());
-                } catch (IOException e) {
-                    fail("Failed with exception " + e);
-                }
+                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);
             }
         }
+
+        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);
+        }
     }
 
     @Test
@@ -1332,7 +1318,7 @@
 
     @Test
     public void testGetCrop() {
-        final int resId = RECORDS[0].resId;
+        final int resId = getRecord().resId;
         ImageDecoder.Source src = mCreators[0].apply(resId);
         try {
             ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
@@ -1352,7 +1338,8 @@
     }
 
     @Test
-    public void testCrop() {
+    @Parameters(method = "getRecords")
+    public void testCrop(Record record) {
         class Listener implements ImageDecoder.OnHeaderDecodedListener {
             public boolean doScale;
             public boolean requireSoftware;
@@ -1381,22 +1368,20 @@
         };
         Listener l = new Listener();
         boolean trueFalse[] = new boolean[] { true, false };
-        for (Record record : RECORDS) {
-            for (SourceCreator f : mCreators) {
-                for (boolean doScale : trueFalse) {
-                    l.doScale = doScale;
-                    for (boolean requireSoftware : trueFalse) {
-                        l.requireSoftware = requireSoftware;
-                        ImageDecoder.Source src = f.apply(record.resId);
+        for (SourceCreator f : mCreators) {
+            for (boolean doScale : trueFalse) {
+                l.doScale = doScale;
+                for (boolean requireSoftware : trueFalse) {
+                    l.requireSoftware = requireSoftware;
+                    ImageDecoder.Source src = f.apply(record.resId);
 
-                        try {
-                            Drawable drawable = ImageDecoder.decodeDrawable(src, l);
-                            assertEquals(l.cropRect.width(),  drawable.getIntrinsicWidth());
-                            assertEquals(l.cropRect.height(), drawable.getIntrinsicHeight());
-                        } catch (IOException e) {
-                            fail("Failed " + getAsResourceUri(record.resId) +
-                                    " with exception " + e);
-                        }
+                    try {
+                        Drawable drawable = ImageDecoder.decodeDrawable(src, l);
+                        assertEquals(l.cropRect.width(),  drawable.getIntrinsicWidth());
+                        assertEquals(l.cropRect.height(), drawable.getIntrinsicHeight());
+                    } catch (IOException e) {
+                        fail("Failed " + getAsResourceUri(record.resId)
+                                + " with exception " + e);
                     }
                 }
             }
@@ -1753,7 +1738,7 @@
 
     @Test
     public void testGetConserveMemory() {
-        final int resId = RECORDS[0].resId;
+        final int resId = getRecord().resId;
         ImageDecoder.Source src = mCreators[0].apply(resId);
         try {
             ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
@@ -1914,7 +1899,7 @@
                 reference = null;
             }
             Uri uri = getAsResourceUri(resId);
-            ImageDecoder.Source src = ImageDecoder.createSource(mContentResolver, uri);
+            ImageDecoder.Source src = ImageDecoder.createSource(getContentResolver(), uri);
             try {
                 Bitmap bm = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
                     // Use software allocator so we can compare.
@@ -1945,107 +1930,105 @@
     private interface ByteBufferSupplier extends Supplier<ByteBuffer> {};
 
     @Test
-    public void testOffsetByteArray() {
-        for (Record record : RECORDS) {
-            int offset = 10;
-            int extra = 15;
-            byte[] array = getAsByteArray(record.resId, offset, extra);
-            int length = array.length - extra - offset;
-            // Used for SourceCreators that set both a position and an offset.
-            int myOffset = 3;
-            int myPosition = 7;
-            assertEquals(offset, myOffset + myPosition);
+    @Parameters(method = "getRecords")
+    public void testOffsetByteArray(Record record) {
+        int offset = 10;
+        int extra = 15;
+        byte[] array = getAsByteArray(record.resId, offset, extra);
+        int length = array.length - extra - offset;
+        // Used for SourceCreators that set both a position and an offset.
+        int myOffset = 3;
+        int myPosition = 7;
+        assertEquals(offset, myOffset + myPosition);
 
-            ByteBufferSupplier[] suppliers = new ByteBufferSupplier[] {
-                    // Internally, this gives the buffer a position, but not an offset.
-                    () -> ByteBuffer.wrap(array, offset, length),
+        ByteBufferSupplier[] suppliers = new ByteBufferSupplier[] {
+                // Internally, this gives the buffer a position, but not an offset.
+                () -> ByteBuffer.wrap(array, offset, length),
+                // Same, but make it readOnly to ensure that we test the
+                // ByteBufferSource rather than the ByteArraySource.
+                () -> ByteBuffer.wrap(array, offset, length).asReadOnlyBuffer(),
+                () -> {
+                    // slice() to give the buffer an offset.
+                    ByteBuffer buf = ByteBuffer.wrap(array, 0, array.length - extra);
+                    buf.position(offset);
+                    return buf.slice();
+                },
+                () -> {
                     // Same, but make it readOnly to ensure that we test the
                     // ByteBufferSource rather than the ByteArraySource.
-                    () -> ByteBuffer.wrap(array, offset, length).asReadOnlyBuffer(),
-                    () -> {
-                        // slice() to give the buffer an offset.
-                        ByteBuffer buf = ByteBuffer.wrap(array, 0, array.length - extra);
-                        buf.position(offset);
-                        return buf.slice();
-                    },
-                    () -> {
-                        // Same, but make it readOnly to ensure that we test the
-                        // ByteBufferSource rather than the ByteArraySource.
-                        ByteBuffer buf = ByteBuffer.wrap(array, 0, array.length - extra);
-                        buf.position(offset);
-                        return buf.slice().asReadOnlyBuffer();
-                    },
-                    () -> {
-                        // Use both a position and an offset.
-                        ByteBuffer buf = ByteBuffer.wrap(array, myOffset,
+                    ByteBuffer buf = ByteBuffer.wrap(array, 0, array.length - extra);
+                    buf.position(offset);
+                    return buf.slice().asReadOnlyBuffer();
+                },
+                () -> {
+                    // Use both a position and an offset.
+                    ByteBuffer buf = ByteBuffer.wrap(array, myOffset,
                             array.length - extra - myOffset);
-                        buf = buf.slice();
-                        buf.position(myPosition);
-                        return buf;
-                    },
-                    () -> {
-                        // Same, as readOnly.
-                        ByteBuffer buf = ByteBuffer.wrap(array, myOffset,
-                                array.length - extra - myOffset);
-                        buf = buf.slice();
-                        buf.position(myPosition);
-                        return buf.asReadOnlyBuffer();
-                    },
-                    () -> {
-                        // Direct ByteBuffer with a position.
-                        ByteBuffer buf = ByteBuffer.allocateDirect(array.length);
-                        buf.put(array);
-                        buf.position(offset);
-                        return buf;
-                    },
-                    () -> {
-                        // Sliced direct ByteBuffer, for an offset.
-                        ByteBuffer buf = ByteBuffer.allocateDirect(array.length);
-                        buf.put(array);
-                        buf.position(offset);
-                        return buf.slice();
-                    },
-                    () -> {
-                        // Direct ByteBuffer with position and offset.
-                        ByteBuffer buf = ByteBuffer.allocateDirect(array.length);
-                        buf.put(array);
-                        buf.position(myOffset);
-                        buf = buf.slice();
-                        buf.position(myPosition);
-                        return buf;
-                    },
-            };
-            for (int i = 0; i < suppliers.length; ++i) {
-                ByteBuffer buffer = suppliers[i].get();
-                final int position = buffer.position();
-                ImageDecoder.Source src = ImageDecoder.createSource(buffer);
-                try {
-                    Drawable drawable = ImageDecoder.decodeDrawable(src);
-                    assertNotNull(drawable);
-                } catch (IOException e) {
-                    fail("Failed with exception " + e);
-                }
-                assertEquals("Mismatch for supplier " + i,
-                        position, buffer.position());
-            }
-        }
-    }
-
-    @Test
-    public void testResourceSource() {
-        for (Record record : RECORDS) {
-            ImageDecoder.Source src = ImageDecoder.createSource(mRes, record.resId);
+                    buf = buf.slice();
+                    buf.position(myPosition);
+                    return buf;
+                },
+                () -> {
+                    // Same, as readOnly.
+                    ByteBuffer buf = ByteBuffer.wrap(array, myOffset,
+                            array.length - extra - myOffset);
+                    buf = buf.slice();
+                    buf.position(myPosition);
+                    return buf.asReadOnlyBuffer();
+                },
+                () -> {
+                    // Direct ByteBuffer with a position.
+                    ByteBuffer buf = ByteBuffer.allocateDirect(array.length);
+                    buf.put(array);
+                    buf.position(offset);
+                    return buf;
+                },
+                () -> {
+                    // Sliced direct ByteBuffer, for an offset.
+                    ByteBuffer buf = ByteBuffer.allocateDirect(array.length);
+                    buf.put(array);
+                    buf.position(offset);
+                    return buf.slice();
+                },
+                () -> {
+                    // Direct ByteBuffer with position and offset.
+                    ByteBuffer buf = ByteBuffer.allocateDirect(array.length);
+                    buf.put(array);
+                    buf.position(myOffset);
+                    buf = buf.slice();
+                    buf.position(myPosition);
+                    return buf;
+                },
+        };
+        for (int i = 0; i < suppliers.length; ++i) {
+            ByteBuffer buffer = suppliers[i].get();
+            final int position = buffer.position();
+            ImageDecoder.Source src = ImageDecoder.createSource(buffer);
             try {
                 Drawable drawable = ImageDecoder.decodeDrawable(src);
                 assertNotNull(drawable);
             } catch (IOException e) {
-                fail("Failed " + getAsResourceUri(record.resId) + " with " + e);
+                fail("Failed with exception " + e);
             }
+            assertEquals("Mismatch for supplier " + i,
+                    position, buffer.position());
+        }
+    }
+
+    @Test
+    @Parameters(method = "getRecords")
+    public void testResourceSource(Record record) {
+        ImageDecoder.Source src = ImageDecoder.createSource(getResources(), record.resId);
+        try {
+            Drawable drawable = ImageDecoder.decodeDrawable(src);
+            assertNotNull(drawable);
+        } catch (IOException e) {
+            fail("Failed " + getAsResourceUri(record.resId) + " with " + e);
         }
     }
 
     private BitmapDrawable decodeBitmapDrawable(int resId) {
-        ImageDecoder.Source src = ImageDecoder.createSource(mRes, resId);
+        ImageDecoder.Source src = ImageDecoder.createSource(getResources(), resId);
         try {
             Drawable drawable = ImageDecoder.decodeDrawable(src);
             assertNotNull(drawable);
@@ -2058,54 +2041,54 @@
     }
 
     @Test
-    public void testUpscale() {
-        final int originalDensity = mRes.getDisplayMetrics().densityDpi;
+    @Parameters(method = "getRecords")
+    public void testUpscale(Record record) {
+        Resources res = getResources();
+        final int originalDensity = res.getDisplayMetrics().densityDpi;
 
         try {
-            for (Record record : RECORDS) {
-                final int resId = record.resId;
+            final int resId = record.resId;
 
-                // Set a high density. This will result in a larger drawable, but
-                // not a larger Bitmap.
-                mRes.getDisplayMetrics().densityDpi = DisplayMetrics.DENSITY_XXXHIGH;
-                BitmapDrawable drawable = decodeBitmapDrawable(resId);
+            // Set a high density. This will result in a larger drawable, but
+            // not a larger Bitmap.
+            res.getDisplayMetrics().densityDpi = DisplayMetrics.DENSITY_XXXHIGH;
+            BitmapDrawable drawable = decodeBitmapDrawable(resId);
 
-                Bitmap bm = drawable.getBitmap();
-                assertEquals(record.width, bm.getWidth());
-                assertEquals(record.height, bm.getHeight());
+            Bitmap bm = drawable.getBitmap();
+            assertEquals(record.width, bm.getWidth());
+            assertEquals(record.height, bm.getHeight());
 
-                assertTrue(drawable.getIntrinsicWidth() > record.width);
-                assertTrue(drawable.getIntrinsicHeight() > record.height);
+            assertTrue(drawable.getIntrinsicWidth() > record.width);
+            assertTrue(drawable.getIntrinsicHeight() > record.height);
 
-                // Set a low density. This will result in a smaller drawable and
-                // Bitmap, unless the true density is DENSITY_MEDIUM, which matches
-                // the density of the asset.
-                mRes.getDisplayMetrics().densityDpi = DisplayMetrics.DENSITY_LOW;
-                drawable = decodeBitmapDrawable(resId);
-                bm = drawable.getBitmap();
+            // Set a low density. This will result in a smaller drawable and
+            // Bitmap, unless the true density is DENSITY_MEDIUM, which matches
+            // the density of the asset.
+            res.getDisplayMetrics().densityDpi = DisplayMetrics.DENSITY_LOW;
+            drawable = decodeBitmapDrawable(resId);
+            bm = drawable.getBitmap();
 
-                if (originalDensity == DisplayMetrics.DENSITY_MEDIUM) {
-                    // Although we've modified |densityDpi|, ImageDecoder knows the
-                    // true density matches the asset, so it will not downscale at
-                    // decode time.
-                    assertEquals(bm.getWidth(), record.width);
-                    assertEquals(bm.getHeight(), record.height);
+            if (originalDensity == DisplayMetrics.DENSITY_MEDIUM) {
+                // Although we've modified |densityDpi|, ImageDecoder knows the
+                // true density matches the asset, so it will not downscale at
+                // decode time.
+                assertEquals(bm.getWidth(), record.width);
+                assertEquals(bm.getHeight(), record.height);
 
-                    // The drawable should still be smaller.
-                    assertTrue(bm.getWidth() > drawable.getIntrinsicWidth());
-                    assertTrue(bm.getHeight() > drawable.getIntrinsicHeight());
-                } else {
-                    // The bitmap is scaled down at decode time, so it matches the
-                    // drawable size, and is smaller than the original.
-                    assertTrue(bm.getWidth() < record.width);
-                    assertTrue(bm.getHeight() < record.height);
+                // The drawable should still be smaller.
+                assertTrue(bm.getWidth() > drawable.getIntrinsicWidth());
+                assertTrue(bm.getHeight() > drawable.getIntrinsicHeight());
+            } else {
+                // The bitmap is scaled down at decode time, so it matches the
+                // drawable size, and is smaller than the original.
+                assertTrue(bm.getWidth() < record.width);
+                assertTrue(bm.getHeight() < record.height);
 
-                    assertEquals(bm.getWidth(), drawable.getIntrinsicWidth());
-                    assertEquals(bm.getHeight(), drawable.getIntrinsicHeight());
-                }
+                assertEquals(bm.getWidth(), drawable.getIntrinsicWidth());
+                assertEquals(bm.getHeight(), drawable.getIntrinsicHeight());
             }
         } finally {
-            mRes.getDisplayMetrics().densityDpi = originalDensity;
+            res.getDisplayMetrics().densityDpi = originalDensity;
         }
     }
 
@@ -2148,7 +2131,8 @@
         }
     }
 
-    private static final AssetRecord[] ASSETS = new AssetRecord[] {
+    private Object [] getAssetRecords() {
+        return new Object [] {
             // A null ColorSpace means that the color space is "Unknown".
             new AssetRecord("almost-red-adobe.png", 1, 1, false, false, null),
             new AssetRecord("green-p3.png", 64, 64, false, false,
@@ -2166,51 +2150,82 @@
                     ColorSpace.get(ColorSpace.Named.DISPLAY_P3)),
             new AssetRecord("grayscale-linearSrgb.png", 32, 32, false, true,
                     ColorSpace.get(ColorSpace.Named.LINEAR_SRGB)),
-    };
+        };
+    }
 
     @Test
-    public void testAssetSource() {
-        AssetManager assets = mRes.getAssets();
-        for (AssetRecord record : ASSETS) {
-            ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
+    @Parameters(method = "getAssetRecords")
+    public void testAssetSource(AssetRecord record) {
+        AssetManager assets = getResources().getAssets();
+        ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
+        try {
+            Bitmap bm = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
+                if (record.isF16) {
+                    // CTS infrastructure fails to create F16 HARDWARE Bitmaps, so this
+                    // switches to using software.
+                    decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+                }
+
+                record.checkColorSpace(null, info.getColorSpace());
+            });
+            assertEquals(record.name, record.width, bm.getWidth());
+            assertEquals(record.name, record.height, bm.getHeight());
+            record.checkColorSpace(null, bm.getColorSpace());
+        } catch (IOException e) {
+            fail("Failed to decode asset " + record.name + " with " + e);
+        }
+    }
+
+    @Test
+    @Parameters(method = "getAssetRecords")
+    public void testTargetColorSpace(AssetRecord record) {
+        AssetManager assets = getResources().getAssets();
+        ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
+        for (ColorSpace cs : BitmapTest.getRgbColorSpaces()) {
             try {
                 Bitmap bm = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
-                    if (record.isF16) {
-                        // CTS infrastructure fails to create F16 HARDWARE Bitmaps, so this
-                        // switches to using software.
+                    if (record.isF16 || isExtended(cs)) {
+                        // CTS infrastructure and some devices fail to create F16
+                        // HARDWARE Bitmaps, so this switches to using software.
                         decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
                     }
-
-                    record.checkColorSpace(null, info.getColorSpace());
+                    decoder.setTargetColorSpace(cs);
                 });
-                assertEquals(record.name, record.width, bm.getWidth());
-                assertEquals(record.name, record.height, bm.getHeight());
-                record.checkColorSpace(null, bm.getColorSpace());
+                record.checkColorSpace(cs, bm.getColorSpace());
             } catch (IOException e) {
-                fail("Failed to decode asset " + record.name + " with " + e);
+                fail("Failed to decode asset " + record.name + " to " + cs + " with " + e);
             }
         }
     }
 
     @Test
-    public void testTargetColorSpace() {
-        AssetManager assets = mRes.getAssets();
-        for (AssetRecord record : ASSETS) {
-            ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
-            for (ColorSpace cs : BitmapTest.getRgbColorSpaces()) {
-                try {
-                    Bitmap bm = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
-                        if (record.isF16) {
-                            // CTS infrastructure fails to create F16 HARDWARE Bitmaps, so this
-                            // switches to using software.
-                            decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
-                        }
-                        decoder.setTargetColorSpace(cs);
-                    });
-                    record.checkColorSpace(cs, bm.getColorSpace());
-                } catch (IOException e) {
-                    fail("Failed to decode asset " + record.name + " to " + cs + " with " + e);
+    @Parameters(method = "getAssetRecords")
+    public void testTargetColorSpaceNoF16HARDWARE(AssetRecord record) {
+        final ColorSpace EXTENDED_SRGB = ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB);
+        final ColorSpace LINEAR_EXTENDED_SRGB = ColorSpace.get(
+                ColorSpace.Named.LINEAR_EXTENDED_SRGB);
+        AssetManager assets = getResources().getAssets();
+        ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
+        for (ColorSpace cs : new ColorSpace[] { EXTENDED_SRGB, LINEAR_EXTENDED_SRGB }) {
+            try {
+                Bitmap bm = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
+                    decoder.setTargetColorSpace(cs);
+                });
+                // If the ColorSpace does not match the request, it should be because
+                // F16 + HARDWARE is not supported. In that case, it should match the non-
+                // EXTENDED variant.
+                ColorSpace actual = bm.getColorSpace();
+                if (actual != cs) {
+                    assertEquals(BitmapTest.ANDROID_BITMAP_FORMAT_RGBA_8888,
+                                 BitmapTest.nGetFormat(bm));
+                    if (cs == EXTENDED_SRGB) {
+                        assertSame(ColorSpace.get(ColorSpace.Named.SRGB), actual);
+                    } else {
+                        assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_SRGB), actual);
+                    }
                 }
+            } catch (IOException e) {
+                fail("Failed to decode asset " + record.name + " to " + cs + " with " + e);
             }
         }
     }
@@ -2221,84 +2236,83 @@
     }
 
     @Test
-    public void testTargetColorSpaceUpconvert() {
+    @Parameters(method = "getAssetRecords")
+    public void testTargetColorSpaceUpconvert(AssetRecord record) {
         // Verify that decoding an asset to EXTENDED upconverts to F16.
-        AssetManager assets = mRes.getAssets();
+        AssetManager assets = getResources().getAssets();
         boolean[] trueFalse = new boolean[] { true, false };
         final ColorSpace linearExtended = ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB);
         final ColorSpace linearSrgb = ColorSpace.get(ColorSpace.Named.LINEAR_SRGB);
 
-        for (AssetRecord record : ASSETS) {
-            if (record.isF16) {
-                // These assets decode to F16 by default.
-                continue;
-            }
-            ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
-            for (ColorSpace cs : BitmapTest.getRgbColorSpaces()) {
-                for (boolean alphaMask : trueFalse) {
-                    try {
-                        Bitmap bm = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
-                            // Force software so we can check the Config.
-                            decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
-                            decoder.setTargetColorSpace(cs);
-                            // This has no effect on non-gray assets.
-                            decoder.setDecodeAsAlphaMaskEnabled(alphaMask);
-                        });
+        if (record.isF16) {
+            // These assets decode to F16 by default.
+            return;
+        }
+        ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
+        for (ColorSpace cs : BitmapTest.getRgbColorSpaces()) {
+            for (boolean alphaMask : trueFalse) {
+                try {
+                    Bitmap bm = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
+                        // Force software so we can check the Config.
+                        decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+                        decoder.setTargetColorSpace(cs);
+                        // This has no effect on non-gray assets.
+                        decoder.setDecodeAsAlphaMaskEnabled(alphaMask);
+                    });
 
-                        if (record.isGray && alphaMask) {
-                            assertSame(Bitmap.Config.ALPHA_8, bm.getConfig());
-                            assertNull(bm.getColorSpace());
+                    if (record.isGray && alphaMask) {
+                        assertSame(Bitmap.Config.ALPHA_8, bm.getConfig());
+                        assertNull(bm.getColorSpace());
+                    } else {
+                        assertSame(cs, bm.getColorSpace());
+                        if (isExtended(cs)) {
+                            assertSame(Bitmap.Config.RGBA_F16, bm.getConfig());
                         } else {
-                            assertSame(cs, bm.getColorSpace());
-                            if (isExtended(cs)) {
-                                assertSame(Bitmap.Config.RGBA_F16, bm.getConfig());
+                            assertSame(Bitmap.Config.ARGB_8888, bm.getConfig());
+                        }
+                    }
+                } catch (IOException e) {
+                    fail("Failed to decode asset " + record.name + " to " + cs + " with " + e);
+                }
+
+                // Using MEMORY_POLICY_LOW_RAM prevents upconverting.
+                try {
+                    Bitmap bm = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
+                        // Force software so we can check the Config.
+                        decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+                        decoder.setTargetColorSpace(cs);
+                        decoder.setMemorySizePolicy(ImageDecoder.MEMORY_POLICY_LOW_RAM);
+                        // This has no effect on non-gray assets.
+                        decoder.setDecodeAsAlphaMaskEnabled(alphaMask);
+                    });
+
+                    assertNotEquals(Bitmap.Config.RGBA_F16, bm.getConfig());
+
+                    if (record.isGray && alphaMask) {
+                        assertSame(Bitmap.Config.ALPHA_8, bm.getConfig());
+                        assertNull(bm.getColorSpace());
+                    } else {
+                        ColorSpace actual = bm.getColorSpace();
+                        if (isExtended(cs)) {
+                            if (cs == ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)) {
+                                assertSame(ColorSpace.get(ColorSpace.Named.SRGB), actual);
+                            } else if (cs == linearExtended) {
+                                assertSame(linearSrgb, actual);
                             } else {
+                                fail("Test error: did isExtended() change?");
+                            }
+                        } else {
+                            assertSame(cs, actual);
+                            if (bm.hasAlpha()) {
                                 assertSame(Bitmap.Config.ARGB_8888, bm.getConfig());
-                            }
-                        }
-                    } catch (IOException e) {
-                        fail("Failed to decode asset " + record.name + " to " + cs + " with " + e);
-                    }
-
-                    // Using MEMORY_POLICY_LOW_RAM prevents upconverting.
-                    try {
-                        Bitmap bm = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
-                            // Force software so we can check the Config.
-                            decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
-                            decoder.setTargetColorSpace(cs);
-                            decoder.setMemorySizePolicy(ImageDecoder.MEMORY_POLICY_LOW_RAM);
-                            // This has no effect on non-gray assets.
-                            decoder.setDecodeAsAlphaMaskEnabled(alphaMask);
-                        });
-
-                        assertNotEquals(Bitmap.Config.RGBA_F16, bm.getConfig());
-
-                        if (record.isGray && alphaMask) {
-                            assertSame(Bitmap.Config.ALPHA_8, bm.getConfig());
-                            assertNull(bm.getColorSpace());
-                        } else {
-                            ColorSpace actual = bm.getColorSpace();
-                            if (isExtended(cs)) {
-                                if (cs == ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)) {
-                                    assertSame(ColorSpace.get(ColorSpace.Named.SRGB), actual);
-                                } else if (cs == linearExtended) {
-                                    assertSame(linearSrgb, actual);
-                                } else {
-                                    fail("Test error: did isExtended() change?");
-                                }
                             } else {
-                                assertSame(cs, actual);
-                                if (bm.hasAlpha()) {
-                                    assertSame(Bitmap.Config.ARGB_8888, bm.getConfig());
-                                } else {
-                                    assertSame(Bitmap.Config.RGB_565, bm.getConfig());
-                                }
+                                assertSame(Bitmap.Config.RGB_565, bm.getConfig());
                             }
                         }
-                    } catch (IOException e) {
-                        fail("Failed to decode asset " + record.name
-                                + " with MEMORY_POLICY_LOW_RAM to " + cs + " with " + e);
                     }
+                } catch (IOException e) {
+                    fail("Failed to decode asset " + record.name
+                            + " with MEMORY_POLICY_LOW_RAM to " + cs + " with " + e);
                 }
             }
         }
@@ -2380,52 +2394,92 @@
         }
     }
 
+
+    private Object[] crossProduct(Object[] a, Object[] b) {
+        final int length = a.length * b.length;
+        Object[] ret = new Object[length];
+        for (int i = 0; i < a.length; i++) {
+            for (int j = 0; j < b.length; j++) {
+                int index = i * b.length + j;
+                assertNull(ret[index]);
+                ret[index] = new Object[] { a[i], b[j] };
+            }
+        }
+        return ret;
+    }
+
+    private Object[] getRecordsAsSources() {
+        return crossProduct(getRecords(), mCreators);
+    }
+
+
     @Test
     @LargeTest
-    public void testReuse() {
-        for (Record record : RECORDS) {
-            if (record.mimeType.equals("image/heif")) {
-                // This image takes too long for this test.
-                continue;
-            }
-
-            String name = getAsResourceUri(record.resId).toString();
-            for (SourceCreator f : mCreators) {
-                ImageDecoder.Source src = f.apply(record.resId);
-                testReuse(src, name);
-            }
-
-            {
-                ImageDecoder.Source src = ImageDecoder.createSource(mRes, record.resId);
-                testReuse(src, name);
-            }
-
-            for (UriCreator f : mUriCreators) {
-                Uri uri = f.apply(record.resId);
-                ImageDecoder.Source src = ImageDecoder.createSource(mContentResolver, uri);
-                testReuse(src, uri.toString());
-            }
-
-            {
-                ImageDecoder.Source src = ImageDecoder.createSource(getAsFile(record.resId));
-                testReuse(src, name);
-            }
+    @Parameters(method = "getRecordsAsSources")
+    public void testReuse(Record record, SourceCreator f) {
+        if (record.mimeType.equals("image/heif")) {
+            // This image takes too long for this test.
+            return;
         }
 
-        AssetManager assets = mRes.getAssets();
-        for (AssetRecord record : ASSETS) {
-            ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
-            testReuse(src, record.name);
+        String name = getAsResourceUri(record.resId).toString();
+        ImageDecoder.Source src = f.apply(record.resId);
+        testReuse(src, name);
+    }
+
+    @Test
+    @Parameters(method = "getRecords")
+    public void testReuse2(Record record) {
+        if (record.mimeType.equals("image/heif")) {
+            // This image takes too long for this test.
+            return;
         }
 
+        String name = getAsResourceUri(record.resId).toString();
+        ImageDecoder.Source src = ImageDecoder.createSource(getResources(), record.resId);
+        testReuse(src, name);
 
+        src = ImageDecoder.createSource(getAsFile(record.resId));
+        testReuse(src, name);
+    }
+
+    private Object[] getRecordsAsUris() {
+        return crossProduct(getRecords(), mUriCreators);
+    }
+
+
+    @Test
+    @Parameters(method = "getRecordsAsUris")
+    public void testReuseUri(Record record, UriCreator f) {
+        if (record.mimeType.equals("image/heif")) {
+            // This image takes too long for this test.
+            return;
+        }
+
+        Uri uri = f.apply(record.resId);
+        ImageDecoder.Source src = ImageDecoder.createSource(getContentResolver(), uri);
+        testReuse(src, uri.toString());
+    }
+
+    @Test
+    @Parameters(method = "getAssetRecords")
+    public void testReuseAssetRecords(AssetRecord record) {
+        AssetManager assets = getResources().getAssets();
+        ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
+        testReuse(src, record.name);
+    }
+
+
+    @Test
+    public void testReuseAnimated() {
         ImageDecoder.Source src = mCreators[0].apply(R.drawable.animated);
         testReuse(src, "animated.gif");
     }
 
     @Test
     public void testIsMimeTypeSupported() {
-        for (Record record : RECORDS) {
+        for (Object r : getRecords()) {
+            Record record = (Record) r;
             assertTrue(record.mimeType, ImageDecoder.isMimeTypeSupported(record.mimeType));
         }
 
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
index 3f5c692..24f44f2 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
@@ -56,7 +56,10 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
 import java.util.function.BiFunction;
 
 @RunWith(AndroidJUnit4.class)
@@ -670,4 +673,91 @@
         aid = (AnimatedImageDrawable) drawable;
         assertEquals(AnimatedImageDrawable.REPEAT_INFINITE, aid.getRepeatCount());
     }
+
+    // Verify that decoding on the AnimatedImageThread works.
+    private void decodeInBackground(AnimatedImageDrawable drawable) throws Throwable {
+        final Callback cb = new Callback(drawable);
+        mActivityRule.runOnUiThread(() -> {
+            setContentView(drawable);
+            drawable.registerAnimationCallback(cb);
+            drawable.start();
+        });
+
+        // The first frame was decoded in the thread that created the
+        // AnimatedImageDrawable. Wait long enough to decode further threads on
+        // the AnimatedImageThread, which was not created with a JNI interface
+        // pointer.
+        cb.waitForStart();
+        cb.waitForEnd(DURATION * 2);
+    }
+
+    @Test
+    public void testInputStream() throws Throwable {
+        try (InputStream in = mRes.openRawResource(R.drawable.animated)) {
+            ImageDecoder.Source src =
+                    ImageDecoder.createSource(mRes, in, Bitmap.DENSITY_NONE);
+            AnimatedImageDrawable drawable =
+                    (AnimatedImageDrawable) ImageDecoder.decodeDrawable(src);
+            decodeInBackground(drawable);
+        }
+
+    }
+
+    private byte[] getAsByteArray() {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        try (InputStream in = mRes.openRawResource(RES_ID)) {
+            byte[] buf = new byte[4096];
+            int bytesRead;
+            while ((bytesRead = in.read(buf)) != -1) {
+                outputStream.write(buf, 0, bytesRead);
+            }
+        } catch (IOException e) {
+            fail("Failed to read resource: " + e);
+        }
+
+        return outputStream.toByteArray();
+    }
+
+    private ByteBuffer getAsDirectByteBuffer() {
+        byte[] array = getAsByteArray();
+        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(array.length);
+        byteBuffer.put(array);
+        byteBuffer.position(0);
+        return byteBuffer;
+    }
+
+    private AnimatedImageDrawable createFromByteBuffer(ByteBuffer byteBuffer) {
+        ImageDecoder.Source src = ImageDecoder.createSource(byteBuffer);
+        try {
+            return (AnimatedImageDrawable) ImageDecoder.decodeDrawable(src);
+        } catch (IOException e) {
+            fail("Failed to create decoder: " + e);
+            return null;
+        }
+    }
+
+    @Test
+    public void testByteBuffer() throws Throwable {
+        // Natively, this tests ByteArrayStream.
+        byte[] array = getAsByteArray();
+        ByteBuffer byteBuffer = ByteBuffer.wrap(array);
+        final AnimatedImageDrawable drawable = createFromByteBuffer(byteBuffer);
+        decodeInBackground(drawable);
+    }
+
+    @Test
+    public void testReadOnlyByteBuffer() throws Throwable {
+        // Natively, this tests ByteBufferStream.
+        byte[] array = getAsByteArray();
+        ByteBuffer byteBuffer = ByteBuffer.wrap(array).asReadOnlyBuffer();
+        final AnimatedImageDrawable drawable = createFromByteBuffer(byteBuffer);
+        decodeInBackground(drawable);
+    }
+
+    @Test
+    public void testDirectByteBuffer() throws Throwable {
+        ByteBuffer byteBuffer = getAsDirectByteBuffer();
+        final AnimatedImageDrawable drawable = createFromByteBuffer(byteBuffer);
+        decodeInBackground(drawable);
+    }
 }
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..eb92789 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
@@ -48,6 +48,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.xmlpull.v1.XmlPullParser;
@@ -750,6 +751,28 @@
         assertEquals(Orientation.TOP_BOTTOM, drawable.getOrientation());
     }
 
+
+    @Ignore("Disabling temporarily while actual fix to maintain behavioral differences of "
+            + "orientation xml and programmatically defined GradientDrawables")
+    @Test
+    public void testGradientNoAngle() {
+        // Verify that the default orientation for a GradientDrawable defined in xml is
+        // LEFT_RIGHT. This differs from the default behavior of programmatically defined
+        // GradientDrawables
+        final Context context = InstrumentationRegistry.getTargetContext();
+        GradientDrawable drawable = (GradientDrawable)
+                context.getDrawable(R.drawable.gradientdrawable_no_angle);
+        assertEquals(Orientation.LEFT_RIGHT, drawable.getOrientation());
+    }
+
+    @Test
+    public void testDynamicGradientDefaultOrientation() {
+        // Verify that the default orientation for a programmatically defined GradientDrawable is
+        // TOP_BOTTOM. This differs from the default behavior of xml inflated GradientDrawables
+        // that default to LEFT_RIGHT
+        assertEquals(Orientation.TOP_BOTTOM, new GradientDrawable().getOrientation());
+    }
+
     @Test
     public void testGradientDrawableOrientationConstructor() {
         GradientDrawable drawable = new GradientDrawable(Orientation.TOP_BOTTOM, null);
diff --git a/tests/tests/hardware/res/raw/asus_gamepad_register.json b/tests/tests/hardware/res/raw/asus_gamepad_register.json
index cfd71eb..dd422a4 100644
--- a/tests/tests/hardware/res/raw/asus_gamepad_register.json
+++ b/tests/tests/hardware/res/raw/asus_gamepad_register.json
@@ -1,9 +1,9 @@
 {
   "id": 1,
   "command": "register",
-  "name": "Odie (Test)",
-  "vid": 0x18d1,
-  "pid": 0x2c40,
+  "name": "Asus Gamepad (Test)",
+  "vid": 0x0b05,
+  "pid": 0x4500,
   "descriptor": [0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x05, 0x09, 0x0a, 0x01, 0x00,
     0x0a, 0x02, 0x00, 0x0a, 0x04, 0x00, 0x0a, 0x05, 0x00, 0x0a, 0x07, 0x00, 0x0a, 0x08, 0x00,
     0x0a, 0x0e, 0x00, 0x0a, 0x0f, 0x00, 0x0a, 0x0d, 0x00, 0x05, 0x0c, 0x0a, 0x24, 0x02, 0x0a,
diff --git a/tests/tests/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/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/keystore/src/android/server/am/ActivityManagerState.java b/tests/tests/keystore/src/android/server/am/ActivityManagerState.java
index debe8fc..b56d745 100644
--- a/tests/tests/keystore/src/android/server/am/ActivityManagerState.java
+++ b/tests/tests/keystore/src/android/server/am/ActivityManagerState.java
@@ -341,7 +341,7 @@
         int procId = -1;
 
         Activity(ActivityRecordProto proto) {
-            super(proto.configurationContainer);
+            super(proto.appWindowToken.windowToken.windowContainer.configurationContainer);
             name = proto.identifier.title;
             state = proto.state;
             visible = proto.visible;
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 99902c5..0d2f5b9 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/res/values/exifinterface.xml b/tests/tests/media/res/values/exifinterface.xml
index 11c2efd..25a42b1 100644
--- a/tests/tests/media/res/values/exifinterface.xml
+++ b/tests/tests/media/res/values/exifinterface.xml
@@ -53,6 +53,44 @@
         <item />
         <item />
     </array>
+    <array name="exifbyteorderii_standalone">
+        <item>true</item>
+        <item>3494</item>
+        <item>6265</item>
+        <item>512</item>
+        <item>288</item>
+        <item>true</item>
+        <item>false</item>
+        <item>0</item>
+        <item>0</item>
+        <item>0.0</item>
+        <item>0.0</item>
+        <item>0.0</item>
+        <item>SAMSUNG</item>
+        <item>SM-N900S</item>
+        <item>2.200</item>
+        <item>2016:01:29 18:32:27</item>
+        <item>0.033</item>
+        <item>0</item>
+        <item>413/100</item>
+        <item />
+        <item />
+        <item />
+        <item />
+        <item />
+        <item />
+        <item />
+        <item />
+        <item />
+        <item>480</item>
+        <item>640</item>
+        <item>50</item>
+        <item>6</item>
+        <item>0</item>
+        <item>false</item>
+        <item>0</item>
+        <item>0</item>
+    </array>
     <array name="exifbyteordermm_jpg">
         <item>false</item>
         <item />
@@ -91,6 +129,44 @@
         <item />
         <item />
     </array>
+    <array name="exifbyteordermm_standalone">
+        <item>false</item>
+        <item>0</item>
+        <item>0</item>
+        <item>0</item>
+        <item>0</item>
+        <item>false</item>
+        <item>true</item>
+        <item>572</item>
+        <item>24</item>
+        <item>0.0</item>
+        <item>0.0</item>
+        <item>0.0</item>
+        <item>LGE</item>
+        <item>Nexus 5</item>
+        <item>2.400</item>
+        <item>2016:01:29 15:44:58</item>
+        <item>0.017</item>
+        <item>0</item>
+        <item>3970/1000</item>
+        <item>0/1000</item>
+        <item>0</item>
+        <item>1970:01:01</item>
+        <item>0/1,0/1,0/10000</item>
+        <item>N</item>
+        <item>0/1,0/1,0/10000</item>
+        <item>E</item>
+        <item>GPS</item>
+        <item>00:00:00</item>
+        <item>0</item>
+        <item>0</item>
+        <item>146</item>
+        <item>0</item>
+        <item>0</item>
+        <item>false</item>
+        <item>0</item>
+        <item>0</item>
+    </array>
     <array name="lg_g4_iso_800_dng">
         <item>true</item>
         <item>0</item>
diff --git a/tests/tests/media/src/android/media/cts/.goutputstream-9KZYJZ b/tests/tests/media/src/android/media/cts/.goutputstream-9KZYJZ
deleted file mode 100644
index c1769ac..0000000
--- a/tests/tests/media/src/android/media/cts/.goutputstream-9KZYJZ
+++ /dev/null
@@ -1,814 +0,0 @@
-/*
- * Copyright 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.media.cts;
-
-import android.media.BufferingParams;
-import android.media.DataSourceDesc;
-import android.media.MediaFormat;
-import android.media.MediaPlayer2;
-import android.media.MediaPlayer2.TrackInfo;
-import android.media.TimedMetaData;
-import android.media.cts.TestUtils.Monitor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Looper;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.platform.test.annotations.AppModeFull;
-import android.test.InstrumentationTestRunner;
-import android.util.Log;
-import android.webkit.cts.CtsTestServer;
-
-import com.android.compatibility.common.util.DynamicConfigDeviceSide;
-import com.android.compatibility.common.util.MediaUtils;
-
-import java.net.HttpCookie;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * Tests of MediaPlayer2 streaming capabilities.
- */
-@AppModeFull(reason = "TODO: evaluate and port to instant")
-public class StreamingMediaPlayer2Test extends MediaPlayer2TestBase {
-    // TODO: remove this flag to enable tests.
-    private static final boolean IGNORE_TESTS = true;
-
-    private static final String TAG = "StreamingMediaPlayer2Test";
-
-    private static final String HTTP_H263_AMR_VIDEO_1_KEY =
-            "streaming_media_player_test_http_h263_amr_video1";
-    private static final String HTTP_H263_AMR_VIDEO_2_KEY =
-            "streaming_media_player_test_http_h263_amr_video2";
-    private static final String HTTP_H264_BASE_AAC_VIDEO_1_KEY =
-            "streaming_media_player_test_http_h264_base_aac_video1";
-    private static final String HTTP_H264_BASE_AAC_VIDEO_2_KEY =
-            "streaming_media_player_test_http_h264_base_aac_video2";
-    private static final String HTTP_MPEG4_SP_AAC_VIDEO_1_KEY =
-            "streaming_media_player_test_http_mpeg4_sp_aac_video1";
-    private static final String HTTP_MPEG4_SP_AAC_VIDEO_2_KEY =
-            "streaming_media_player_test_http_mpeg4_sp_aac_video2";
-    private static final String MODULE_NAME = "CtsMediaTestCases";
-    private DynamicConfigDeviceSide dynamicConfig;
-
-    private CtsTestServer mServer;
-
-    private String mInputUrl;
-
-    @Override
-    protected void setUp() throws Exception {
-        // if launched with InstrumentationTestRunner to pass a command line argument
-        if (getInstrumentation() instanceof InstrumentationTestRunner) {
-            InstrumentationTestRunner testRunner =
-                    (InstrumentationTestRunner)getInstrumentation();
-
-            Bundle arguments = testRunner.getArguments();
-            mInputUrl = arguments.getString("url");
-            Log.v(TAG, "setUp: arguments: " + arguments);
-            if (mInputUrl != null) {
-                Log.v(TAG, "setUp: arguments[url] " + mInputUrl);
-            }
-        }
-
-        super.setUp();
-        dynamicConfig = new DynamicConfigDeviceSide(MODULE_NAME);
-    }
-
-/* RTSP tests are more flaky and vulnerable to network condition.
-   Disable until better solution is available
-    // Streaming RTSP video from YouTube
-    public void testRTSP_H263_AMR_Video1() throws Exception {
-        playVideoTest("rtsp://v2.cache7.c.youtube.com/video.3gp?cid=0x271de9756065677e"
-                + "&fmt=13&user=android-device-test", 176, 144);
-    }
-    public void testRTSP_H263_AMR_Video2() throws Exception {
-        playVideoTest("rtsp://v2.cache7.c.youtube.com/video.3gp?cid=0xc80658495af60617"
-                + "&fmt=13&user=android-device-test", 176, 144);
-    }
-
-    public void testRTSP_MPEG4SP_AAC_Video1() throws Exception {
-        playVideoTest("rtsp://v2.cache7.c.youtube.com/video.3gp?cid=0x271de9756065677e"
-                + "&fmt=17&user=android-device-test", 176, 144);
-    }
-    public void testRTSP_MPEG4SP_AAC_Video2() throws Exception {
-        playVideoTest("rtsp://v2.cache7.c.youtube.com/video.3gp?cid=0xc80658495af60617"
-                + "&fmt=17&user=android-device-test", 176, 144);
-    }
-
-    public void testRTSP_H264Base_AAC_Video1() throws Exception {
-        playVideoTest("rtsp://v2.cache7.c.youtube.com/video.3gp?cid=0x271de9756065677e"
-                + "&fmt=18&user=android-device-test", 480, 270);
-    }
-    public void testRTSP_H264Base_AAC_Video2() throws Exception {
-        playVideoTest("rtsp://v2.cache7.c.youtube.com/video.3gp?cid=0xc80658495af60617"
-                + "&fmt=18&user=android-device-test", 480, 270);
-    }
-*/
-    // Streaming HTTP video from YouTube
-    public void testHTTP_H263_AMR_Video1() throws Exception {
-        if (IGNORE_TESTS) {
-            return;
-        }
-        if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_H263,
-                  MediaFormat.MIMETYPE_AUDIO_AMR_NB)) {
-            return; // skip
-        }
-
-        String urlString = dynamicConfig.getValue(HTTP_H263_AMR_VIDEO_1_KEY);
-        playVideoTest(urlString, 176, 144);
-    }
-
-    public void testHTTP_H263_AMR_Video2() throws Exception {
-        if (IGNORE_TESTS) {
-            return;
-        }
-        if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_H263,
-                  MediaFormat.MIMETYPE_AUDIO_AMR_NB)) {
-            return; // skip
-        }
-
-        String urlString = dynamicConfig.getValue(HTTP_H263_AMR_VIDEO_2_KEY);
-        playVideoTest(urlString, 176, 144);
-    }
-
-    public void testHTTP_MPEG4SP_AAC_Video1() throws Exception {
-        if (IGNORE_TESTS) {
-            return;
-        }
-        if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_MPEG4)) {
-            return; // skip
-        }
-
-        String urlString = dynamicConfig.getValue(HTTP_MPEG4_SP_AAC_VIDEO_1_KEY);
-        playVideoTest(urlString, 176, 144);
-    }
-
-    public void testHTTP_MPEG4SP_AAC_Video2() throws Exception {
-        if (IGNORE_TESTS) {
-            return;
-        }
-        if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_MPEG4)) {
-            return; // skip
-        }
-
-        String urlString = dynamicConfig.getValue(HTTP_MPEG4_SP_AAC_VIDEO_2_KEY);
-        playVideoTest(urlString, 176, 144);
-    }
-
-    public void testHTTP_H264Base_AAC_Video1() throws Exception {
-        if (IGNORE_TESTS) {
-            return;
-        }
-        if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
-            return; // skip
-        }
-
-        String urlString = dynamicConfig.getValue(HTTP_H264_BASE_AAC_VIDEO_1_KEY);
-        playVideoTest(urlString, 640, 360);
-    }
-
-    public void testHTTP_H264Base_AAC_Video2() throws Exception {
-        if (IGNORE_TESTS) {
-            return;
-        }
-        if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
-            return; // skip
-        }
-
-        String urlString = dynamicConfig.getValue(HTTP_H264_BASE_AAC_VIDEO_2_KEY);
-        playVideoTest(urlString, 640, 360);
-    }
-
-    // Streaming HLS video from YouTube
-    public void testHLS() throws Exception {
-        if (IGNORE_TESTS) {
-            return;
-        }
-        if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
-            return; // skip
-        }
-
-        // Play stream for 60 seconds
-        playLiveVideoTest("http://www.youtube.com/api/manifest/hls_variant/id/"
-                + "0168724d02bd9945/itag/5/source/youtube/playlist_type/DVR/ip/"
-                + "0.0.0.0/ipbits/0/expire/19000000000/sparams/ip,ipbits,expire"
-                + ",id,itag,source,playlist_type/signature/773AB8ACC68A96E5AA48"
-                + "1996AD6A1BBCB70DCB87.95733B544ACC5F01A1223A837D2CF04DF85A336"
-                + "0/key/ik0/file/m3u8", 60 * 1000);
-    }
-
-    public void testHlsWithHeadersCookies() throws Exception {
-        if (IGNORE_TESTS) {
-            return;
-        }
-        if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
-            return; // skip
-        }
-
-        final Uri uri = Uri.parse(
-                "http://www.youtube.com/api/manifest/hls_variant/id/"
-                + "0168724d02bd9945/itag/5/source/youtube/playlist_type/DVR/ip/"
-                + "0.0.0.0/ipbits/0/expire/19000000000/sparams/ip,ipbits,expire"
-                + ",id,itag,source,playlist_type/signature/773AB8ACC68A96E5AA48"
-                + "1996AD6A1BBCB70DCB87.95733B544ACC5F01A1223A837D2CF04DF85A336"
-                + "0/key/ik0/file/m3u8");
-
-        // TODO: dummy values for headers/cookies till we find a server that actually needs them
-        HashMap<String, String> headers = new HashMap<>();
-        headers.put("header0", "value0");
-        headers.put("header1", "value1");
-
-        String cookieName = "auth_1234567";
-        String cookieValue = "0123456789ABCDEF0123456789ABCDEF";
-        HttpCookie cookie = new HttpCookie(cookieName, cookieValue);
-        cookie.setHttpOnly(true);
-        cookie.setDomain("www.youtube.com");
-        cookie.setPath("/");        // all paths
-        cookie.setSecure(false);
-        cookie.setDiscard(false);
-        cookie.setMaxAge(24 * 3600);  // 24hrs
-
-        java.util.Vector<HttpCookie> cookies = new java.util.Vector<HttpCookie>();
-        cookies.add(cookie);
-
-        // Play stream for 60 seconds
-        playLiveVideoTest(uri, headers, cookies, 60 * 1000);
-    }
-
-    public void testHlsSampleAes_bbb_audio_only_overridable() throws Exception {
-        if (IGNORE_TESTS) {
-            return;
-        }
-        if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
-            return; // skip
-        }
-
-        String defaultUrl = "http://storage.googleapis.com/wvmedia/cenc/hls/sample_aes/" +
-                            "bbb_1080p_30fps_11min/audio_only/prog_index.m3u8";
-
-        // if url override provided
-        String testUrl = (mInputUrl != null) ? mInputUrl : defaultUrl;
-
-        // Play stream for 60 seconds
-        playLiveAudioOnlyTest(
-                testUrl,
-                60 * 1000);
-    }
-
-    public void testHlsSampleAes_bbb_unmuxed_1500k() throws Exception {
-        if (IGNORE_TESTS) {
-            return;
-        }
-        if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
-            return; // skip
-        }
-
-        // Play stream for 60 seconds
-        playLiveVideoTest(
-                "http://storage.googleapis.com/wvmedia/cenc/hls/sample_aes/" +
-                "bbb_1080p_30fps_11min/unmuxed_1500k/prog_index.m3u8",
-                60 * 1000);
-    }
-
-
-    // Streaming audio from local HTTP server
-    public void testPlayMp3Stream1() throws Throwable {
-        if (IGNORE_TESTS) {
-            return;
-        }
-        localHttpAudioStreamTest("ringer.mp3", false, false);
-    }
-    public void testPlayMp3Stream2() throws Throwable {
-        if (IGNORE_TESTS) {
-            return;
-        }
-        localHttpAudioStreamTest("ringer.mp3", false, false);
-    }
-    public void testPlayMp3StreamRedirect() throws Throwable {
-        if (IGNORE_TESTS) {
-            return;
-        }
-        localHttpAudioStreamTest("ringer.mp3", true, false);
-    }
-    public void testPlayMp3StreamNoLength() throws Throwable {
-        if (IGNORE_TESTS) {
-            return;
-        }
-        localHttpAudioStreamTest("noiseandchirps.mp3", false, true);
-    }
-    public void testPlayOggStream() throws Throwable {
-        if (IGNORE_TESTS) {
-            return;
-        }
-        localHttpAudioStreamTest("noiseandchirps.ogg", false, false);
-    }
-    public void testPlayOggStreamRedirect() throws Throwable {
-        if (IGNORE_TESTS) {
-            return;
-        }
-        localHttpAudioStreamTest("noiseandchirps.ogg", true, false);
-    }
-    public void testPlayOggStreamNoLength() throws Throwable {
-        if (IGNORE_TESTS) {
-            return;
-        }
-        localHttpAudioStreamTest("noiseandchirps.ogg", false, true);
-    }
-    public void testPlayMp3Stream1Ssl() throws Throwable {
-        if (IGNORE_TESTS) {
-            return;
-        }
-        localHttpsAudioStreamTest("ringer.mp3", false, false);
-    }
-
-    private void localHttpAudioStreamTest(final String name, boolean redirect, boolean nolength)
-            throws Throwable {
-        mServer = new CtsTestServer(mContext);
-        try {
-            String stream_url = null;
-            if (redirect) {
-                // Stagefright doesn't have a limit, but we can't test support of infinite redirects
-                // Up to 4 redirects seems reasonable though.
-                stream_url = mServer.getRedirectingAssetUrl(name, 4);
-            } else {
-                stream_url = mServer.getAssetUrl(name);
-            }
-            if (nolength) {
-                stream_url = stream_url + "?" + CtsTestServer.NOLENGTH_POSTFIX;
-            }
-
-            if (!MediaUtils.checkCodecsForPath(mContext, stream_url)) {
-                return; // skip
-            }
-
-            final Uri uri = Uri.parse(stream_url);
-            mPlayer.setDataSource(new DataSourceDesc.Builder()
-                    .setDataSource(mContext, uri)
-                    .build());
-
-            mPlayer.setDisplay(getActivity().getSurfaceHolder());
-            mPlayer.setScreenOnWhilePlaying(true);
-
-            mOnBufferingUpdateCalled.reset();
-            MediaPlayer2.MediaPlayer2EventCallback ecb =
-                new MediaPlayer2.MediaPlayer2EventCallback() {
-                    @Override
-                    public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
-                        fail("Media player had error " + what + " playing " + name);
-                    }
-
-                    @Override
-                    public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
-                        if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
-                            mOnPrepareCalled.signal();
-                        } else if (what == MediaPlayer2.MEDIA_INFO_BUFFERING_UPDATE) {
-                            mOnBufferingUpdateCalled.signal();
-                        }
-                    }
-                };
-            mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
-
-            assertFalse(mOnBufferingUpdateCalled.isSignalled());
-
-            mPlayer.prepare();
-            mOnPrepareCalled.waitForSignal();
-
-            if (nolength) {
-                mPlayer.play();
-                Thread.sleep(LONG_SLEEP_TIME);
-                assertFalse(mPlayer.isPlaying());
-            } else {
-                mOnBufferingUpdateCalled.waitForSignal();
-                mPlayer.play();
-                Thread.sleep(SLEEP_TIME);
-            }
-            mPlayer.stop();
-            mPlayer.reset();
-        } finally {
-            mServer.shutdown();
-        }
-    }
-    private void localHttpsAudioStreamTest(final String name, boolean redirect, boolean nolength)
-            throws Throwable {
-        mServer = new CtsTestServer(mContext, true);
-        try {
-            String stream_url = null;
-            if (redirect) {
-                // Stagefright doesn't have a limit, but we can't test support of infinite redirects
-                // Up to 4 redirects seems reasonable though.
-                stream_url = mServer.getRedirectingAssetUrl(name, 4);
-            } else {
-                stream_url = mServer.getAssetUrl(name);
-            }
-            if (nolength) {
-                stream_url = stream_url + "?" + CtsTestServer.NOLENGTH_POSTFIX;
-            }
-
-            final Uri uri = Uri.parse(stream_url);
-            mPlayer.setDataSource(new DataSourceDesc.Builder()
-                    .setDataSource(mContext, uri)
-                    .build());
-
-            mPlayer.setDisplay(getActivity().getSurfaceHolder());
-            mPlayer.setScreenOnWhilePlaying(true);
-
-            mOnBufferingUpdateCalled.reset();
-
-            MediaPlayer2.MediaPlayer2EventCallback ecb =
-                new MediaPlayer2.MediaPlayer2EventCallback() {
-                    @Override
-                    public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
-                        mOnErrorCalled.signal();
-                    }
-
-                    @Override
-                    public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
-                        if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
-                            mOnPrepareCalled.signal();
-                        } else if (what == MediaPlayer2.MEDIA_INFO_BUFFERING_UPDATE) {
-                            mOnBufferingUpdateCalled.signal();
-                        }
-                    }
-                };
-            mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
-
-            assertFalse(mOnBufferingUpdateCalled.isSignalled());
-            try {
-                mPlayer.prepare();
-                mOnErrorCalled.waitForSignal();
-            } catch (Exception ex) {
-                return;
-            }
-        } finally {
-            mServer.shutdown();
-        }
-    }
-
-    // TODO: unhide this test when we sort out how to expose buffering control API.
-    private void doTestBuffering() throws Throwable {
-        final String name = "ringer.mp3";
-        mServer = new CtsTestServer(mContext);
-        try {
-            String stream_url = mServer.getAssetUrl(name);
-
-            if (!MediaUtils.checkCodecsForPath(mContext, stream_url)) {
-                Log.w(TAG, "can not find stream " + stream_url + ", skipping test");
-                return; // skip
-            }
-
-            Monitor onSetBufferingParamsCalled = new Monitor();
-            MediaPlayer2.MediaPlayer2EventCallback ecb =
-                new MediaPlayer2.MediaPlayer2EventCallback() {
-                    @Override
-                    public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
-                        fail("Media player had error " + what + " playing " + name);
-                    }
-                    @Override
-                    public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
-                        if (what == MediaPlayer2.MEDIA_INFO_BUFFERING_UPDATE) {
-                            mOnBufferingUpdateCalled.signal();
-                        }
-                    }
-                    @Override
-                    public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd,
-                            int what, int status) {
-                        if (what == MediaPlayer2.CALL_COMPLETED_SET_BUFFERING_PARAMS) {
-                            mCallStatus = status;
-                            onSetBufferingParamsCalled.signal();
-                        }
-                    }
-                };
-            mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
-
-            // getBufferingParams should be called after setDataSource.
-            try {
-                BufferingParams params = mPlayer.getBufferingParams();
-                fail("MediaPlayer2 failed to check state for getBufferingParams");
-            } catch (IllegalStateException e) {
-                // expected
-            }
-
-            // setBufferingParams should be called after setDataSource.
-            BufferingParams params = new BufferingParams.Builder()
-                    .setInitialMarkMs(2)
-                    .setResumePlaybackMarkMs(3)
-                    .build();
-            mCallStatus = MediaPlayer2.CALL_STATUS_NO_ERROR;
-            onSetBufferingParamsCalled.reset();
-            mPlayer.setBufferingParams(params);
-            onSetBufferingParamsCalled.waitForSignal();
-            assertTrue(mCallStatus != MediaPlayer2.CALL_STATUS_NO_ERROR);
-
-            final Uri uri = Uri.parse(stream_url);
-            mPlayer.setDataSource(new DataSourceDesc.Builder()
-                    .setDataSource(mContext, uri)
-                    .build());
-
-            mPlayer.setDisplay(getActivity().getSurfaceHolder());
-            mPlayer.setScreenOnWhilePlaying(true);
-
-            mOnBufferingUpdateCalled.reset();
-
-            assertFalse(mOnBufferingUpdateCalled.isSignalled());
-
-            params = mPlayer.getBufferingParams();
-
-            int newMark = params.getInitialMarkMs() + 2;
-            BufferingParams newParams =
-                    new BufferingParams.Builder(params).setInitialMarkMs(newMark).build();
-
-            onSetBufferingParamsCalled.reset();
-            mPlayer.setBufferingParams(newParams);
-            onSetBufferingParamsCalled.waitForSignal();
-
-            int checkMark = -1;
-            BufferingParams checkParams = mPlayer.getBufferingParams();
-            checkMark = checkParams.getInitialMarkMs();
-            assertEquals("marks do not match", newMark, checkMark);
-
-            // TODO: add more dynamic checking, e.g., buffering shall not exceed pre-set mark.
-
-            mPlayer.reset();
-        } finally {
-            mServer.shutdown();
-        }
-    }
-
-    public void testPlayHlsStream() throws Throwable {
-        if (IGNORE_TESTS) {
-            return;
-        }
-        if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
-            return; // skip
-        }
-        localHlsTest("hls.m3u8", false, false);
-    }
-
-    public void testPlayHlsStreamWithQueryString() throws Throwable {
-        if (IGNORE_TESTS) {
-            return;
-        }
-        if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
-            return; // skip
-        }
-        localHlsTest("hls.m3u8", true, false);
-    }
-
-    public void testPlayHlsStreamWithRedirect() throws Throwable {
-        if (IGNORE_TESTS) {
-            return;
-        }
-        if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
-            return; // skip
-        }
-        localHlsTest("hls.m3u8", false, true);
-    }
-
-    public void testPlayHlsStreamWithTimedId3() throws Throwable {
-        if (IGNORE_TESTS) {
-            return;
-        }
-        if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
-            Log.d(TAG, "Device doesn't have video codec, skipping test");
-            return;
-        }
-
-        mServer = new CtsTestServer(mContext);
-        try {
-            // counter must be final if we want to access it inside onTimedMetaData;
-            // use AtomicInteger so we can have a final counter object with mutable integer value.
-            final AtomicInteger counter = new AtomicInteger();
-            String stream_url = mServer.getAssetUrl("prog_index.m3u8");
-            final Uri uri = Uri.parse(stream_url);
-            mPlayer.setDataSource(new DataSourceDesc.Builder()
-                    .setDataSource(mContext, uri)
-                    .build());
-            mPlayer.setDisplay(getActivity().getSurfaceHolder());
-            mPlayer.setScreenOnWhilePlaying(true);
-            mPlayer.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
-
-            final Object completion = new Object();
-            MediaPlayer2.MediaPlayer2EventCallback ecb =
-                new MediaPlayer2.MediaPlayer2EventCallback() {
-                    int run;
-                    @Override
-                    public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
-                        if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
-                            mOnPrepareCalled.signal();
-                        } else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
-                            if (run++ == 0) {
-                                mPlayer.seekTo(0, MediaPlayer2.SEEK_PREVIOUS_SYNC);
-                                mPlayer.play();
-                            } else {
-                                mPlayer.stop();
-                                synchronized (completion) {
-                                    completion.notify();
-                                }
-                            }
-                        }
-                    }
-
-                @Override
-                public void onTimedMetaDataAvailable(MediaPlayer2 mp, DataSourceDesc dsd,
-                        TimedMetaData md) {
-                    counter.incrementAndGet();
-                    long pos = mp.getCurrentPosition();
-                    long timeUs = md.getTimestamp();
-                    byte[] rawData = md.getMetaData();
-                    // Raw data contains an id3 tag holding the decimal string representation of
-                    // the associated time stamp rounded to the closest half second.
-
-                    int offset = 0;
-                    offset += 3; // "ID3"
-                    offset += 2; // version
-                    offset += 1; // flags
-                    offset += 4; // size
-                    offset += 4; // "TXXX"
-                    offset += 4; // frame size
-                    offset += 2; // frame flags
-                    offset += 1; // "\x03" : UTF-8 encoded Unicode
-                    offset += 1; // "\x00" : null-terminated empty description
-
-                    int length = rawData.length;
-                    length -= offset;
-                    length -= 1; // "\x00" : terminating null
-
-                    String data = new String(rawData, offset, length);
-                    int dataTimeUs = Integer.parseInt(data);
-                    assertTrue("Timed ID3 timestamp does not match content",
-                            Math.abs(dataTimeUs - timeUs) < 500000);
-                    assertTrue("Timed ID3 arrives after timestamp", pos * 1000 < timeUs);
-                }
-
-                @Override
-                public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd,
-                        int what, int status) {
-                    if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
-                        mOnPlayCalled.signal();
-                    }
-                }
-            };
-            mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
-
-            mPlayer.prepare();
-            mOnPrepareCalled.waitForSignal();
-
-            mOnPlayCalled.reset();
-            mPlayer.play();
-            mOnPlayCalled.waitForSignal();
-            assertTrue("MediaPlayer2 not playing", mPlayer.isPlaying());
-
-            int i = -1;
-            List<TrackInfo> trackInfos = mPlayer.getTrackInfo();
-            for (i = 0; i < trackInfos.size(); i++) {
-                TrackInfo trackInfo = trackInfos.get(i);
-                if (trackInfo.getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_METADATA) {
-                    break;
-                }
-            }
-            assertTrue("Stream has no timed ID3 track", i >= 0);
-            mPlayer.selectTrack(i);
-
-            synchronized (completion) {
-                completion.wait();
-            }
-
-            // There are a total of 19 metadata access units in the test stream; every one of them
-            // should be received twice: once before the seek and once after.
-            assertTrue("Incorrect number of timed ID3s recieved", counter.get() == 38);
-        } finally {
-            mServer.shutdown();
-        }
-    }
-
-    private static class WorkerWithPlayer implements Runnable {
-        private final Object mLock = new Object();
-        private Looper mLooper;
-        private MediaPlayer2 mPlayer;
-
-        /**
-         * Creates a worker thread with the given name. The thread
-         * then runs a {@link android.os.Looper}.
-         * @param name A name for the new thread
-         */
-        WorkerWithPlayer(String name) {
-            Thread t = new Thread(null, this, name);
-            t.setPriority(Thread.MIN_PRIORITY);
-            t.start();
-            synchronized (mLock) {
-                while (mLooper == null) {
-                    try {
-                        mLock.wait();
-                    } catch (InterruptedException ex) {
-                    }
-                }
-            }
-        }
-
-        public MediaPlayer2 getPlayer() {
-            return mPlayer;
-        }
-
-        @Override
-        public void run() {
-            synchronized (mLock) {
-                Looper.prepare();
-                mLooper = Looper.myLooper();
-                mPlayer = MediaPlayer2.create();
-                mLock.notifyAll();
-            }
-            Looper.loop();
-        }
-
-        public void quit() {
-            mLooper.quit();
-            mPlayer.close();
-        }
-    }
-
-    public void testBlockingReadRelease() throws Throwable {
-        if (IGNORE_TESTS) {
-            return;
-        }
-
-        mServer = new CtsTestServer(mContext);
-
-        WorkerWithPlayer worker = new WorkerWithPlayer("player");
-        final MediaPlayer2 mp = worker.getPlayer();
-
-        try {
-            String path = mServer.getDelayedAssetUrl("noiseandchirps.ogg", 15000);
-            final Uri uri = Uri.parse(path);
-            mp.setDataSource(new DataSourceDesc.Builder()
-                    .setDataSource(mContext, uri)
-                    .build());
-
-            MediaPlayer2.MediaPlayer2EventCallback ecb =
-                new MediaPlayer2.MediaPlayer2EventCallback() {
-                    @Override
-                    public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
-                        if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
-                            fail("prepare should not succeed");
-                        }
-                    }
-                };
-            mp.setMediaPlayer2EventCallback(mExecutor, ecb);
-
-            mp.prepare();
-            Thread.sleep(1000);
-            long start = SystemClock.elapsedRealtime();
-            mp.close();
-            long end = SystemClock.elapsedRealtime();
-            long releaseDuration = (end - start);
-            assertTrue("release took too long: " + releaseDuration, releaseDuration < 1000);
-        } catch (IllegalArgumentException e) {
-            fail(e.getMessage());
-        } catch (SecurityException e) {
-            fail(e.getMessage());
-        } catch (IllegalStateException e) {
-            fail(e.getMessage());
-        } catch (InterruptedException e) {
-            fail(e.getMessage());
-        } finally {
-            mServer.shutdown();
-        }
-
-        // give the worker a bit of time to start processing the message before shutting it down
-        Thread.sleep(5000);
-        worker.quit();
-    }
-
-    private void localHlsTest(final String name, boolean appendQueryString, boolean redirect)
-            throws Throwable {
-        mServer = new CtsTestServer(mContext);
-        try {
-            String stream_url = null;
-            if (redirect) {
-                stream_url = mServer.getQueryRedirectingAssetUrl(name);
-            } else {
-                stream_url = mServer.getAssetUrl(name);
-            }
-            if (appendQueryString) {
-                stream_url += "?foo=bar/baz";
-            }
-
-            playLiveVideoTest(stream_url, 10);
-        } finally {
-            mServer.shutdown();
-        }
-    }
-}
diff --git a/tests/tests/media/src/android/media/cts/AudioManagerTest.java b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
index 47b3337..1316db6 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -189,13 +189,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;
-        }
-
         if (!mDoNotCheckUnmute) {
             final MyBlockingIntentReceiver receiver = new MyBlockingIntentReceiver(
                     AudioManager.ACTION_MICROPHONE_MUTE_CHANGED);
@@ -1283,6 +1276,41 @@
                     mAudioManager.isStreamMute(AudioManager.STREAM_SYSTEM));
             assertTrue("Alarm stream should be muted",
                     mAudioManager.isStreamMute(AudioManager.STREAM_ALARM));
+            assertFalse("Ringer stream should not be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_RING));
+        } finally {
+            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+        }
+    }
+
+    public void testPriorityOnlySystemDisallowedWithRingerMuted() throws Exception {
+        if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+            return;
+        }
+
+        Utils.toggleNotificationPolicyAccess(
+                mContext.getPackageName(), getInstrumentation(), true);
+        try {
+            // ensure volume is not muted/0 to start test, but then mute ringer
+            mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_SYSTEM, 1, 0);
+            mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 0, 0);
+            mAudioManager.setRingerMode(RINGER_MODE_SILENT);
+
+            // allow only system in priority only
+            mNm.setNotificationPolicy(new NotificationManager.Policy(
+                    NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM, 0, 0));
+            setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
+            // delay for streams to get into correct mute states
+            Thread.sleep(ASYNC_TIMING_TOLERANCE_MS);
+
+            assertTrue("Music (media) stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC));
+            assertTrue("System stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_SYSTEM));
+            assertTrue("Alarm stream should be muted",
+                    mAudioManager.isStreamMute(AudioManager.STREAM_ALARM));
 
             // Test requires that the phone's default state has no channels that can bypass dnd
             assertTrue("Ringer stream should be muted",
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..a38f8a7 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
@@ -35,12 +35,14 @@
 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;
 import android.os.Looper;
 import android.os.Message;
 import android.os.PersistableBundle;
+import android.os.Process;
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 import android.util.Log;
@@ -630,12 +632,14 @@
         }
 
         AudioRecord recorder = null;
+        String packageName = InstrumentationRegistry.getTargetContext().getPackageName();
+        int currentUserId = Process.myUserHandle().getIdentifier();
 
         // We will record audio for 20 sec from active and idle state expecting
         // the recording from active state to have data while from idle silence.
         try {
             // Ensure no race and UID active
-            makeMyUidStateActive();
+            makeMyUidStateActive(packageName, currentUserId);
 
             // Setup a recorder
             final AudioRecord candidateRecorder = new AudioRecord.Builder()
@@ -669,7 +673,7 @@
             // Start clean
             buffer.clear();
             // Force idle the package
-            makeMyUidStateIdle();
+            makeMyUidStateIdle(packageName, currentUserId);
             // Read five seconds of data
             readDataTimed(recorder, 5000, buffer);
             // Ensure we read empty bytes
@@ -678,7 +682,7 @@
             // Start clean
             buffer.clear();
             // Reset to active
-            makeMyUidStateActive();
+            makeMyUidStateActive(packageName, currentUserId);
             // Read five seconds of data
             readDataTimed(recorder, 5000, buffer);
             // Ensure we read non-empty bytes
@@ -688,7 +692,7 @@
                 recorder.stop();
                 recorder.release();
             }
-            resetMyUidState();
+            resetMyUidState(packageName, currentUserId);
         }
     }
 
@@ -1628,25 +1632,67 @@
         return totalSilenceCount > valueCount / 2;
     }
 
-    private static void makeMyUidStateActive() throws IOException {
-        final String command = "cmd media.audio_policy set-uid-state "
-                + InstrumentationRegistry.getTargetContext().getPackageName() + " active";
+    private static void makeMyUidStateActive(String packageName, int userId) throws IOException {
+        final String command = String.format(
+                "cmd media.audio_policy set-uid-state %s active --user %d", packageName, userId);
         SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
     }
 
-    private static void makeMyUidStateIdle() throws IOException {
-        final String command = "cmd media.audio_policy set-uid-state "
-                + InstrumentationRegistry.getTargetContext().getPackageName() + " idle";
+    private static void makeMyUidStateIdle(String packageName, int userId) throws IOException {
+        final String command = String.format(
+                "cmd media.audio_policy set-uid-state %s idle --user %d", packageName, userId);
         SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
     }
 
-    private static void resetMyUidState() throws IOException {
-        final String command = "cmd media.audio_policy reset-uid-state "
-                +  InstrumentationRegistry.getTargetContext().getPackageName();
+    private static void resetMyUidState(String packageName, int userId) throws IOException {
+        final String command = String.format(
+                "cmd media.audio_policy reset-uid-state %s --user %d", packageName, userId);
         SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
     }
 
     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..ba4836b 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;
@@ -32,6 +33,7 @@
 
 import java.io.BufferedInputStream;
 import java.io.ByteArrayInputStream;
+import java.io.EOFException;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -358,6 +360,30 @@
         }
     }
 
+    private void testExifInterfaceForStandalone(String fileName, int typedArrayResourceId)
+            throws IOException {
+        ExpectedValue expectedValue = new ExpectedValue(
+                getContext().getResources().obtainTypedArray(typedArrayResourceId));
+
+        // Test for reading from external data storage.
+        fileName = EXTERNAL_BASE_DIRECTORY + fileName;
+
+        File imageFile = new File(Environment.getExternalStorageDirectory(), fileName);
+        String verboseTag = imageFile.getName();
+
+        FileInputStream fis = new FileInputStream(imageFile);
+        // Skip the following marker bytes (0xff, 0xd8, 0xff, 0xe1)
+        fis.skip(4);
+        // Read the value of the length of the exif data
+        short length = readShort(fis);
+        byte[] exifBytes = new byte[length];
+        fis.read(exifBytes);
+
+        ByteArrayInputStream bin = new ByteArrayInputStream(exifBytes);
+        ExifInterface exifInterface = ExifInterface.fromStandalone(bin);
+        compareWithExpectedValue(exifInterface, expectedValue, verboseTag, true);
+    }
+
     private void testExifInterfaceCommon(String fileName, ExpectedValue expectedValue)
             throws IOException {
         File imageFile = new File(Environment.getExternalStorageDirectory(), fileName);
@@ -481,6 +507,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);
     }
@@ -552,6 +588,11 @@
         testExifInterfaceForRaw(SAMSUNG_NX3000_SRW, R.array.samsung_nx3000_srw);
     }
 
+    public void testReadExifDataFromStandaloneData() throws Throwable {
+        testExifInterfaceForStandalone(EXIF_BYTE_ORDER_II_JPEG, R.array.exifbyteorderii_standalone);
+        testExifInterfaceForStandalone(EXIF_BYTE_ORDER_MM_JPEG, R.array.exifbyteordermm_standalone);
+    }
+
     public void testSetDateTime() throws IOException {
         final String dateTimeValue = "2017:02:02 22:22:22";
         final String dateTimeOriginalValue = "2017:01:01 11:11:11";
@@ -594,4 +635,13 @@
         FileUtils.copyFileOrThrow(original, cloned);
         return cloned;
     }
+
+    private short readShort(InputStream is) throws IOException {
+        int ch1 = is.read();
+        int ch2 = is.read();
+        if ((ch1 | ch2) < 0) {
+            throw new EOFException();
+        }
+        return (short) ((ch1 << 8) + (ch2));
+    }
 }
diff --git a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
index 94f334c..8b5d628 100644
--- a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
@@ -36,11 +36,13 @@
 import android.platform.test.annotations.AppModeFull;
 import android.platform.test.annotations.RequiresDevice;
 import android.test.AndroidTestCase;
+import android.util.Log;
 
 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;
@@ -137,10 +139,25 @@
                 "Test album",
                 mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM));
 
+        assertNull("Album artist was unexpectedly present",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST));
+
+        assertNull("Author was unexpectedly present",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_AUTHOR));
+
+        assertNull("Composer was unexpectedly present",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_COMPOSER));
+
         assertEquals("Track number was other than expected",
                 "10",
                 mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER));
 
+        assertNull("Disc number was unexpectedly present",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER));
+
+        assertNull("Compilation was unexpectedly present",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_COMPILATION));
+
         assertEquals("Year was other than expected",
                 "2013", mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_YEAR));
 
@@ -148,7 +165,59 @@
                 "19040101T000000.000Z",
                 mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE));
 
-        assertNull("Writer was unexpected present",
+        assertEquals("Bitrate was other than expected",
+                "365018",  // = 504045 (file size in byte) * 8e6 / 11047000 (duration in us)
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_BITRATE));
+
+        assertNull("Capture frame rate was unexpectedly present",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_CAPTURE_FRAMERATE));
+
+        assertEquals("Duration was other than expected",
+                "11047",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION));
+
+        assertEquals("Number of tracks was other than expected",
+                "4",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS));
+
+        assertEquals("Has audio was other than expected",
+                "yes",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_AUDIO));
+
+        assertEquals("Has video was other than expected",
+                "yes",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO));
+
+        assertEquals("Video frame count was other than expected",
+                "172",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_FRAME_COUNT));
+
+        assertEquals("Video height was other than expected",
+                "288",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT));
+
+        assertEquals("Video width was other than expected",
+                "352",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH));
+
+        assertEquals("Video rotation was other than expected",
+                "0",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION));
+
+        assertEquals("Mime type was other than expected",
+                "video/mp4",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_MIMETYPE));
+
+        assertNull("Location was unexpectedly present",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION));
+
+        assertNull("EXIF length was unexpectedly present",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_EXIF_LENGTH));
+
+        assertNull("EXIF offset was unexpectedly present",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_EXIF_OFFSET));
+
+        assertNull("Writer was unexpectedly present",
                 mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_WRITER));
     }
 
@@ -166,10 +235,25 @@
                 "Test album",
                 mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM));
 
+        assertNull("Album artist was unexpectedly present",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST));
+
+        assertNull("Author was unexpectedly present",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_AUTHOR));
+
+        assertNull("Composer was unexpectedly present",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_COMPOSER));
+
         assertEquals("Track number was other than expected",
                 "10",
                 mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER));
 
+        assertNull("Disc number was unexpectedly present",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER));
+
+        assertNull("Compilation was unexpectedly present",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_COMPILATION));
+
         assertEquals("Year was other than expected",
                 "2013", mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_YEAR));
 
@@ -177,6 +261,58 @@
                 "19700101T000000.000Z",
                 mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE));
 
+        assertEquals("Bitrate was other than expected",
+                "499895",  // = 624869 (file size in byte) * 8e6 / 10000000 (duration in us)
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_BITRATE));
+
+        assertNull("Capture frame rate was unexpectedly present",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_CAPTURE_FRAMERATE));
+
+        assertEquals("Duration was other than expected",
+                "10000",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION));
+
+        assertEquals("Number of tracks was other than expected",
+                "2",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS));
+
+        assertEquals("Has audio was other than expected",
+                "yes",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_AUDIO));
+
+        assertEquals("Has video was other than expected",
+                "yes",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO));
+
+        assertEquals("Video frame count was other than expected",
+                "240",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_FRAME_COUNT));
+
+        assertEquals("Video height was other than expected",
+                "360",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT));
+
+        assertEquals("Video width was other than expected",
+                "480",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH));
+
+        assertEquals("Video rotation was other than expected",
+                "0",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION));
+
+        assertEquals("Mime type was other than expected",
+                "video/mp4",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_MIMETYPE));
+
+        assertNull("Location was unexpectedly present",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION));
+
+        assertNull("EXIF length was unexpectedly present",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_EXIF_LENGTH));
+
+        assertNull("EXIF offset was unexpectedly present",
+                mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_EXIF_OFFSET));
+
         assertNull("Writer was unexpectedly present",
                 mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_WRITER));
     }
@@ -194,7 +330,7 @@
 
     }
 
-    public void testLargeAlbumArt() {
+    public void testGetEmbeddedPicture() {
         setDataSourceFd(R.raw.largealbumart);
 
         assertNotNull("couldn't retrieve album art", mRetriever.getEmbeddedPicture());
@@ -246,24 +382,44 @@
     }
 
     private void testThumbnail(int resId) {
+        testThumbnail(resId, null /*outPath*/);
+    }
+
+    private void testThumbnail(int resId, String outPath) {
+        Stopwatch timer = new Stopwatch();
+
         if (!MediaUtils.hasCodecForResourceAndDomain(getContext(), resId, "video/")) {
             MediaUtils.skipTest("no video codecs for resource");
             return;
         }
 
-        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
-        Resources resources = getContext().getResources();
-        AssetFileDescriptor afd = resources.openRawResourceFd(resId);
+        setDataSourceFd(resId);
 
-        retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
+        timer.start();
+        Bitmap thumbnail = mRetriever.getFrameAtTime(-1 /* timeUs (any) */);
+        timer.end();
+        timer.printDuration("getFrameAtTime");
 
-        try {
-            afd.close();
-        } catch (IOException e) {
-            fail("Unable to open file");
+        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");
+            }
         }
-
-        assertNotNull(retriever.getFrameAtTime(-1 /* timeUs (any) */));
     }
 
     public void testThumbnailH264() {
@@ -290,6 +446,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.
@@ -365,10 +532,15 @@
     }
 
     private void testGetFrameAtTimeEditList(int option, int[][] testCases) {
+        MediaMetadataRetriever.BitmapParams params = new MediaMetadataRetriever.BitmapParams();
+        params.setPreferredConfig(Bitmap.Config.ARGB_8888);
+
         testGetFrameAtEditList(testCases, (r) -> {
             List<Bitmap> bitmaps = new ArrayList<>();
             for (int i = 0; i < testCases.length; i++) {
-                bitmaps.add(r.getFrameAtTime(testCases[i][0], option));
+                Bitmap bitmap = r.getFrameAtTime(testCases[i][0], option, params);
+                assertEquals(Bitmap.Config.ARGB_8888, params.getActualConfig());
+                bitmaps.add(bitmap);
             }
             return bitmaps;
         });
@@ -422,56 +594,30 @@
 
     private void testGetFrameAt(int[][] testCases,
             Function<MediaMetadataRetriever, List<Bitmap> > bitmapRetriever) {
-        int resId = R.raw.binary_counter_320x240_30fps_600frames;
-        if (!MediaUtils.hasCodecForResourceAndDomain(getContext(), resId, "video/")
-            && mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
-            MediaUtils.skipTest("no video codecs for resource on watch");
-            return;
-        }
-
-        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
-        Resources resources = getContext().getResources();
-        AssetFileDescriptor afd = resources.openRawResourceFd(resId);
-
-        retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
-        try {
-            afd.close();
-        } catch (IOException e) {
-            fail("Unable to close file");
-        }
-
-        List<Bitmap> bitmaps = bitmapRetriever.apply(retriever);
-
-        for (int i = 0; i < testCases.length; i++) {
-            verifyVideoFrame(bitmaps.get(i), testCases[i]);
-        }
-        retriever.release();
+        testGetFrameAt(R.raw.binary_counter_320x240_30fps_600frames,
+                testCases, bitmapRetriever);
     }
 
     private void testGetFrameAtEditList(int[][] testCases,
             Function<MediaMetadataRetriever, List<Bitmap> > bitmapRetriever) {
-        int resId = R.raw.binary_counter_320x240_30fps_600frames_editlist;
+        testGetFrameAt(R.raw.binary_counter_320x240_30fps_600frames_editlist,
+                testCases, bitmapRetriever);
+    }
+
+    private void testGetFrameAt(int resId, int[][] testCases,
+            Function<MediaMetadataRetriever, List<Bitmap> > bitmapRetriever) {
         if (!MediaUtils.hasCodecForResourceAndDomain(getContext(), resId, "video/")
             && mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
             MediaUtils.skipTest("no video codecs for resource on watch");
             return;
         }
 
-        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
-        Resources resources = getContext().getResources();
-        AssetFileDescriptor afd = resources.openRawResourceFd(resId);
-        retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
-        try {
-            afd.close();
-        } catch (IOException e) {
-            fail("Unable to close file");
-        }
+        setDataSourceFd(resId);
 
-        List<Bitmap> bitmaps = bitmapRetriever.apply(retriever);
+        List<Bitmap> bitmaps = bitmapRetriever.apply(mRetriever);
         for (int i = 0; i < testCases.length; i++) {
             verifyVideoFrame(bitmaps.get(i), testCases[i]);
         }
-        retriever.release();
     }
 
     private void verifyVideoFrame(Bitmap bitmap, int[] testCase) {
@@ -491,6 +637,74 @@
     /**
      * The following tests verifies MediaMetadataRetriever.getScaledFrameAtTime behavior.
      */
+    public void testGetScaledFrameAtTimeWithInvalidResolutions() {
+        int[] resIds = {R.raw.binary_counter_320x240_30fps_600frames,
+                R.raw.binary_counter_320x240_30fps_600frames_editlist,
+                R.raw.bbb_s4_1280x720_mp4_h264_mp31_8mbps_30fps_aac_he_mono_40kbps_44100hz,
+                R.raw.video_176x144_3gp_h263_56kbps_12fps_aac_mono_24kbps_11025hz,
+                R.raw.video_1280x720_mp4_mpeg4_1000kbps_25fps_aac_stereo_128kbps_44100hz,
+                R.raw.bbb_s1_640x360_webm_vp8_2mbps_30fps_vorbis_5ch_320kbps_48000hz,
+                R.raw.bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz,
+                R.raw.bbb_s1_720x480_mp4_hevc_mp3_1600kbps_30fps_aac_he_6ch_240kbps_48000hz,
+                R.raw.video_1280x720_vp9_hdr_static_3mbps,
+                R.raw.video_1280x720_av1_hdr_static_3mbps,
+                R.raw.video_1280x720_hevc_hdr10_static_3mbps};
+        int[][] resolutions = {{0, 120}, {-1, 0}, {-1, 120}, {140, -1}, {-1, -1}};
+        int[] options =
+                {OPTION_CLOSEST, OPTION_CLOSEST_SYNC, OPTION_NEXT_SYNC, OPTION_PREVIOUS_SYNC};
+
+        for (int resId : resIds) {
+            if (!MediaUtils.hasCodecForResourceAndDomain(getContext(), resId, "video/")
+                    && mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+                MediaUtils.skipTest("no video codecs for resource on watch");
+                continue;
+            }
+
+            setDataSourceFd(resId);
+
+            for (int i = 0; i < resolutions.length; i++) {
+                int width = resolutions[i][0];
+                int height = resolutions[i][1];
+                for (int option : options) {
+                    try {
+                        Bitmap bitmap = mRetriever.getScaledFrameAtTime(
+                                2066666 /*timeUs*/, option, width, height);
+                        fail("Failed to receive exception");
+                    } catch (IllegalArgumentException e) {
+                        // Expect exception
+                    }
+                }
+            }
+        }
+    }
+
+    private void testGetScaledFrameAtTime(int scaleToWidth, int scaleToHeight,
+            int expectedWidth, int expectedHeight, Bitmap.Config config) {
+        MediaMetadataRetriever.BitmapParams params = null;
+        Bitmap bitmap = null;
+        if (config != null) {
+            params = new MediaMetadataRetriever.BitmapParams();
+            params.setPreferredConfig(config);
+            bitmap = mRetriever.getScaledFrameAtTime(
+                    2066666 /*timeUs */, OPTION_CLOSEST, scaleToWidth, scaleToHeight, params);
+        } else {
+            bitmap = mRetriever.getScaledFrameAtTime(
+                    2066666 /*timeUs */, OPTION_CLOSEST, scaleToWidth, scaleToHeight);
+        }
+        if (bitmap == null) {
+            fail("Failed to get scaled bitmap");
+        }
+        if (SAVE_BITMAP_OUTPUT) {
+            CodecUtils.saveBitmapToFile(bitmap, String.format("test_%dx%d.jpg",
+                    expectedWidth, expectedHeight));
+        }
+        if (config != null) {
+            assertEquals("Actual config is wrong", config, params.getActualConfig());
+        }
+        assertEquals("Bitmap width is wrong", expectedWidth, bitmap.getWidth());
+        assertEquals("Bitmap height is wrong", expectedHeight, bitmap.getHeight());
+    }
+
     public void testGetScaledFrameAtTime() {
         int resId = R.raw.binary_counter_320x240_30fps_600frames;
         if (!MediaUtils.hasCodecForResourceAndDomain(getContext(), resId, "video/")
@@ -499,175 +713,26 @@
             return;
         }
 
-        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
-        Resources resources = getContext().getResources();
-        AssetFileDescriptor afd = resources.openRawResourceFd(resId);
-
-        retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
-        try {
-            afd.close();
-        } catch (IOException e) {
-            fail("Unable to close file");
-        }
-
-        try {
-            Bitmap bitmap = retriever.getScaledFrameAtTime(
-                    2066666 /*timeUs*/ , OPTION_CLOSEST, 0 /*width*/, 120 /*height*/);
-            fail("Failed to receive exception");
-        } catch (IllegalArgumentException e) {
-            // Expect exception
-        }
-
-        try {
-            Bitmap bitmap = retriever.getScaledFrameAtTime(
-                    2066666 /*timeUs*/ , OPTION_CLOSEST, -1 /*width*/, 0 /*height*/);
-            fail("Failed to receive exception");
-        } catch (IllegalArgumentException e) {
-            // Expect exception
-        }
-
-        try {
-            Bitmap bitmap = retriever.getScaledFrameAtTime(
-                    2066666 /*timeUs*/ , OPTION_CLOSEST, -1 /*width*/, 120 /*height*/);
-            fail("Failed to receive exception");
-        } catch (IllegalArgumentException e) {
-            // Expect exception
-        }
-
-        try {
-            Bitmap bitmap = retriever.getScaledFrameAtTime(
-                2066666 /*timeUs */, OPTION_CLOSEST, 140 /*width*/, -1 /*height*/);
-            fail("Failed to receive exception");
-        } catch (IllegalArgumentException e) {
-            // Expect exception
-        }
-
-        try {
-            Bitmap bitmap = retriever.getScaledFrameAtTime(
-                2066666 /*timeUs */, OPTION_CLOSEST, -1 /*width*/, -1 /*height*/);
-            fail("Failed to receive exception");
-        } catch (IllegalArgumentException e) {
-            // Expect exception
-        }
+        setDataSourceFd(resId);
+        MediaMetadataRetriever.BitmapParams params = new MediaMetadataRetriever.BitmapParams();
 
         // Test desided size of 160 x 120. Return should be 160 x 120
-        try {
-            Bitmap bitmap = retriever.getScaledFrameAtTime(
-                2066666 /*timeUs */, OPTION_CLOSEST, 160 /*width*/, 120 /*height*/);
-            if (bitmap == null) {
-                fail("Failed to get scaled bitmap");
-            }
-            if (SAVE_BITMAP_OUTPUT) {
-                CodecUtils.saveBitmapToFile(bitmap, "test_160x120" + ".jpg");
-            }
-
-            if (bitmap.getWidth() != 160 /* width */) {
-                fail("Bitmap width is " + bitmap.getWidth() + "Expect: 160");
-            }
-            if (bitmap.getHeight() != 120 /* height */) {
-                fail("Bitmap height is " + bitmap.getHeight() + "Expect: 120");
-            }
-
-        } catch (Exception e) {
-            fail("Exception getting bitmap: " + e);
-        }
+        testGetScaledFrameAtTime(160, 120, 160, 120, Bitmap.Config.ARGB_8888);
 
         // Test scaled up bitmap to 640 x 480. Return should be 640 x 480
-        try {
-            Bitmap bitmap = retriever.getScaledFrameAtTime(
-                2066666 /*timeUs */, OPTION_CLOSEST, 640 /*width*/, 480 /*height*/);
-            if (bitmap == null) {
-                fail("Failed to get scaled bitmap");
-            }
-            if (SAVE_BITMAP_OUTPUT) {
-                CodecUtils.saveBitmapToFile(bitmap, "test_640x480" + ".jpg");
-            }
-
-            if (bitmap.getWidth() != 640 /* width */) {
-                fail("Bitmap width is " + bitmap.getWidth() + "Expect: 640");
-            }
-            if (bitmap.getHeight() != 480 /* height */) {
-                fail("Bitmap height is " + bitmap.getHeight() + "Expect: 480");
-            }
-
-        } catch (Exception e) {
-            fail("Exception getting bitmap: " + e);
-        }
+        testGetScaledFrameAtTime(640, 480, 640, 480, Bitmap.Config.ARGB_8888);
 
         // Test scaled up bitmap to 320 x 120. Return should be 160 x 120
-        try {
-            Bitmap bitmap = retriever.getScaledFrameAtTime(
-                2066666 /*timeUs */, OPTION_CLOSEST, 320 /*width*/, 120 /*height*/);
-            if (bitmap == null) {
-                fail("Failed to get scaled bitmap");
-            }
-            if (SAVE_BITMAP_OUTPUT) {
-                CodecUtils.saveBitmapToFile(bitmap, "test_320x120" + ".jpg");
-            }
-
-            if (bitmap.getWidth() != 160 /* width */) {
-                fail("Bitmap width is " + bitmap.getWidth() + "Expect: 160");
-            }
-            if (bitmap.getHeight() != 120 /* height */) {
-                fail("Bitmap height is " + bitmap.getHeight() + "Expect: 120");
-            }
-
-        } catch (Exception e) {
-            fail("Exception getting bitmap: " + e);
-        }
+        testGetScaledFrameAtTime(320, 120, 160, 120, Bitmap.Config.RGB_565);
 
         // Test scaled up bitmap to 160 x 240. Return should be 160 x 120
-        try {
-            Bitmap bitmap = retriever.getScaledFrameAtTime(
-                2066666 /*timeUs */, OPTION_CLOSEST, 160 /*width*/, 240 /*height*/);
-            if (bitmap == null) {
-                fail("Failed to get scaled bitmap");
-            }
-            if (SAVE_BITMAP_OUTPUT) {
-                CodecUtils.saveBitmapToFile(bitmap, "test_160x240" + ".jpg");
-            }
-
-            if (bitmap.getWidth() != 160 /* width */) {
-                fail("Bitmap width is " + bitmap.getWidth() + "Expect: 160");
-            }
-            if (bitmap.getHeight() != 120 /* height */) {
-                fail("Bitmap height is " + bitmap.getHeight() + "Expect: 120");
-            }
-
-        } catch (Exception e) {
-            fail("Exception getting bitmap: " + e);
-        }
+        testGetScaledFrameAtTime(160, 240, 160, 120, Bitmap.Config.RGB_565);
 
         // Test scaled the video with aspect ratio
         resId = R.raw.binary_counter_320x240_720x240_30fps_600frames;
-        afd = resources.openRawResourceFd(resId);
+        setDataSourceFd(resId);
 
-        retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
-        try {
-            afd.close();
-        } catch (IOException e) {
-            fail("Unable to close file");
-        }
-        try {
-            Bitmap bitmap = retriever.getScaledFrameAtTime(
-                2066666 /*timeUs */, OPTION_CLOSEST, 330 /*width*/, 240 /*height*/);
-            if (bitmap == null) {
-                fail("Failed to get scaled bitmap");
-            }
-            if (SAVE_BITMAP_OUTPUT) {
-                CodecUtils.saveBitmapToFile(bitmap, "test_330x240" + ".jpg");
-            }
-
-            if (bitmap.getWidth() != 330 /* width */) {
-                fail("Bitmap width is " + bitmap.getWidth() + "Expect: 330");
-            }
-            if (bitmap.getHeight() != 110 /* height */) {
-                fail("Bitmap height is " + bitmap.getHeight() + "Expect: 110");
-            }
-
-        } catch (Exception e) {
-            fail("Exception getting bitmap: " + e);
-        }
+        testGetScaledFrameAtTime(330, 240, 330, 110, null);
     }
 
     public void testGetImageAtIndex() throws Exception {
@@ -701,37 +766,32 @@
             int resId, int width, int height, int rotation,
             int imageCount, int primary, boolean useGrid, boolean checkColor)
                     throws Exception {
-        MediaMetadataRetriever retriever = null;
+        Stopwatch timer = new Stopwatch();
         MediaExtractor extractor = null;
         AssetFileDescriptor afd = null;
         InputStream inputStream = null;
 
         try {
-            retriever = new MediaMetadataRetriever();
-
-            Resources resources = getContext().getResources();
-            afd = resources.openRawResourceFd(resId);
-
-            retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
+            setDataSourceFd(resId);
 
             // Verify image related meta keys.
-            String hasImage = retriever.extractMetadata(
+            String hasImage = mRetriever.extractMetadata(
                     MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE);
             assertTrue("No images found in resId " + resId, "yes".equals(hasImage));
             assertEquals("Wrong width", width,
-                    Integer.parseInt(retriever.extractMetadata(
+                    Integer.parseInt(mRetriever.extractMetadata(
                             MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH)));
             assertEquals("Wrong height", height,
-                    Integer.parseInt(retriever.extractMetadata(
+                    Integer.parseInt(mRetriever.extractMetadata(
                             MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT)));
             assertEquals("Wrong rotation", rotation,
-                    Integer.parseInt(retriever.extractMetadata(
+                    Integer.parseInt(mRetriever.extractMetadata(
                             MediaMetadataRetriever.METADATA_KEY_IMAGE_ROTATION)));
             assertEquals("Wrong image count", imageCount,
-                    Integer.parseInt(retriever.extractMetadata(
+                    Integer.parseInt(mRetriever.extractMetadata(
                             MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT)));
             assertEquals("Wrong primary index", primary,
-                    Integer.parseInt(retriever.extractMetadata(
+                    Integer.parseInt(mRetriever.extractMetadata(
                             MediaMetadataRetriever.METADATA_KEY_IMAGE_PRIMARY)));
 
             if (checkColor) {
@@ -740,7 +800,10 @@
                 // Also check the position of the color block, which should move left-to-right
                 // with the index.
                 for (int imageIndex = 0; imageIndex < imageCount; imageIndex++) {
-                    bitmap = retriever.getImageAtIndex(imageIndex);
+                    timer.start();
+                    bitmap = mRetriever.getImageAtIndex(imageIndex);
+                    timer.end();
+                    timer.printDuration("getImageAtIndex");
 
                     for (int barIndex = 0; barIndex < COLOR_BARS.length; barIndex++) {
                         Rect r = getColorBarRect(barIndex, width, height);
@@ -759,7 +822,12 @@
 
                 // Check the color block position on the primary image.
                 Rect r = getColorBlockRect(primary, width, height);
-                bitmap = retriever.getPrimaryImage();
+
+                timer.start();
+                bitmap = mRetriever.getPrimaryImage();
+                timer.end();
+                timer.printDuration("getPrimaryImage");
+
                 assertTrue("Color block for primary image doesn't match",
                         approxEquals(COLOR_BLOCK, Color.valueOf(
                                 bitmap.getPixel(r.centerX(), height - r.centerY()))));
@@ -778,6 +846,8 @@
             // Check the grid configuration related keys.
             if (useGrid) {
                 extractor = new MediaExtractor();
+                Resources resources = getContext().getResources();
+                afd = resources.openRawResourceFd(resId);
                 extractor.setDataSource(
                         afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
                 MediaFormat format = extractor.getTrackFormat(0);
@@ -793,9 +863,6 @@
         } catch (IOException e) {
             fail("Unable to open file");
         } finally {
-            if (retriever != null) {
-                retriever.release();
-            }
             if (extractor != null) {
                 extractor.release();
             }
@@ -807,4 +874,34 @@
             }
         }
     }
+
+    private class Stopwatch {
+        private long startTimeMs;
+        private long endTimeMs;
+        private boolean isStartCalled;
+
+        public Stopwatch() {
+            startTimeMs = endTimeMs = 0;
+            isStartCalled = false;
+        }
+
+        public void start() {
+            startTimeMs = System.currentTimeMillis();
+            isStartCalled = true;
+        }
+
+        public void end() {
+            endTimeMs = System.currentTimeMillis();
+            if (!isStartCalled) {
+                Log.e(TAG, "Error: end() must be called after start()!");
+                return;
+            }
+            isStartCalled = false;
+        }
+
+        public void printDuration(String functionName) {
+            long duration = endTimeMs - startTimeMs;
+            Log.i(TAG, String.format("%s() took %d ms.", functionName, duration));
+        }
+    }
 }
diff --git a/tests/tests/media/src/android/media/cts/MediaMuxerTest.java b/tests/tests/media/src/android/media/cts/MediaMuxerTest.java
index c873691..670bb16 100644
--- a/tests/tests/media/src/android/media/cts/MediaMuxerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaMuxerTest.java
@@ -314,7 +314,8 @@
             // No start offsets for any track.
             cloneMediaWithSamplesDropAndStartOffsets(sourceId, outputFilePath,
                 MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, null, null);
-            verifyTimestampsWithSamplesDropSet(sourceId, outputFilePath, null, null);
+            verifyTSWithSamplesDropAndStartOffset(
+                    sourceId, true /* has B frames */, outputFilePath, null, null);
         } finally {
             new File(outputFilePath).delete();
         }
@@ -335,7 +336,8 @@
             // No start offsets for any track.
             cloneMediaWithSamplesDropAndStartOffsets(sourceId, outputFilePath,
                 MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, samplesDropSet, null);
-            verifyTimestampsWithSamplesDropSet(sourceId, outputFilePath, samplesDropSet, null);
+            verifyTSWithSamplesDropAndStartOffset(
+                    sourceId, true /* has B frames */, outputFilePath, samplesDropSet, null);
         } finally {
             new File(outputFilePath).delete();
         }
@@ -358,7 +360,8 @@
             // No start offsets for any track.
             cloneMediaWithSamplesDropAndStartOffsets(sourceId, outputFilePath,
                 MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, samplesDropSet, null);
-            verifyTimestampsWithSamplesDropSet(sourceId, outputFilePath, samplesDropSet, null);
+            verifyTSWithSamplesDropAndStartOffset(
+                    sourceId, true /* has B frames */, outputFilePath, samplesDropSet, null);
         } finally {
             new File(outputFilePath).delete();
         }
@@ -379,7 +382,8 @@
             // No start offsets for any track.
             cloneMediaWithSamplesDropAndStartOffsets(sourceId, outputFilePath,
                 MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, samplesDropSet, null);
-            verifyTimestampsWithSamplesDropSet(sourceId, outputFilePath, samplesDropSet, null);
+            verifyTSWithSamplesDropAndStartOffset(
+                    sourceId, true /* has B frames */, outputFilePath, samplesDropSet, null);
         } finally {
             new File(outputFilePath).delete();
         }
@@ -402,7 +406,8 @@
             // No start offsets for any track.
             cloneMediaWithSamplesDropAndStartOffsets(sourceId, outputFilePath,
                 MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, samplesDropSet, null);
-            verifyTimestampsWithSamplesDropSet(sourceId, outputFilePath, samplesDropSet, null);
+            verifyTSWithSamplesDropAndStartOffset(
+                    sourceId, true /* has B frames */, outputFilePath, samplesDropSet, null);
         } finally {
             new File(outputFilePath).delete();
         }
@@ -424,7 +429,8 @@
             startOffsetUsVect.add(0);
             cloneMediaWithSamplesDropAndStartOffsets(sourceId, outputFilePath,
                 MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, null, startOffsetUsVect);
-            verifyTimestampsWithSamplesDropSet(sourceId, outputFilePath, null, startOffsetUsVect);
+            verifyTSWithSamplesDropAndStartOffset(
+                    sourceId, true /* has B frames */, outputFilePath, null, startOffsetUsVect);
         } finally {
             new File(outputFilePath).delete();
         }
@@ -446,7 +452,104 @@
             startOffsetUsVect.add(400000);
             cloneMediaWithSamplesDropAndStartOffsets(sourceId, outputFilePath,
                 MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, null, startOffsetUsVect);
-            verifyTimestampsWithSamplesDropSet(sourceId, outputFilePath, null, startOffsetUsVect);
+            verifyTSWithSamplesDropAndStartOffset(
+                    sourceId, true /* has B frames */, outputFilePath, null, startOffsetUsVect);
+        } finally {
+            new File(outputFilePath).delete();
+        }
+    }
+
+    /**
+     * Test: make sure if audio/video muxing using MPEG4Writer works when audio
+     * samples start later than video.
+     */
+    public void testTimestampsStartOffsetAudio() throws Exception {
+        int sourceId = R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz;
+        String outputFilePath =
+                File.createTempFile("MediaMuxerTest_testTimestampsStartOffsetAudio", ".mp4")
+                        .getAbsolutePath();
+        try {
+            Vector<Integer> startOffsetUsVect = new Vector<Integer>();
+            // Video starts at 0us.
+            startOffsetUsVect.add(0);
+            // Audio starts at 50000us.
+            startOffsetUsVect.add(50000);
+            cloneMediaWithSamplesDropAndStartOffsets(sourceId, outputFilePath,
+                    MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, null, startOffsetUsVect);
+            verifyTSWithSamplesDropAndStartOffset(
+                    sourceId, false /* no B frames */, outputFilePath, null, startOffsetUsVect);
+        } finally {
+            new File(outputFilePath).delete();
+        }
+    }
+
+    /**
+     * Test: makes sure if audio/video muxing using MPEG4Writer works when video
+     * samples start later than audio.
+     */
+    public void testTimestampsStartOffsetVideo() throws Exception {
+        int sourceId = R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz;
+        String outputFilePath =
+                File.createTempFile("MediaMuxerTest_testTimestampsStartOffsetVideo", ".mp4")
+                        .getAbsolutePath();
+        try {
+            Vector<Integer> startOffsetUsVect = new Vector<Integer>();
+            // Video starts at 500000us.
+            startOffsetUsVect.add(500000);
+            // Audio starts at 0us.
+            startOffsetUsVect.add(0);
+            cloneMediaWithSamplesDropAndStartOffsets(sourceId, outputFilePath,
+                    MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, null, startOffsetUsVect);
+            verifyTSWithSamplesDropAndStartOffset(
+                    sourceId, false /* no B frames */, outputFilePath, null, startOffsetUsVect);
+        } finally {
+            new File(outputFilePath).delete();
+        }
+    }
+
+    /**
+     * Test: makes sure if audio/video muxing using MPEG4Writer works when audio and video
+     * tracks start at non-zero start offset and audio samples start later than video.
+     */
+    public void testTimestampsStartOffsetVideoAudio() throws Exception {
+        int sourceId = R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz;
+        String outputFilePath =
+                File.createTempFile("MediaMuxerTest_testTimestampsStartOffsetAudio", ".mp4")
+                        .getAbsolutePath();
+        try {
+            Vector<Integer> startOffsetUsVect = new Vector<Integer>();
+            // Video starts at 250000us.
+            startOffsetUsVect.add(250000);
+            // Audio starts at 500000us.
+            startOffsetUsVect.add(500000);
+            cloneMediaWithSamplesDropAndStartOffsets(sourceId, outputFilePath,
+                    MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, null, startOffsetUsVect);
+            verifyTSWithSamplesDropAndStartOffset(
+                    sourceId, false /* no B frames */, outputFilePath, null, startOffsetUsVect);
+        } finally {
+            new File(outputFilePath).delete();
+        }
+    }
+
+    /**
+     * Test: makes sure if audio/video muxing using MPEG4Writer works when audio and video
+     * tracks start at non-zero start offset and video samples start later than audio.
+     */
+    public void testTimestampsStartOffsetAudioVideo() throws Exception {
+        int sourceId = R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz;
+        String outputFilePath =
+                File.createTempFile("MediaMuxerTest_testTimestampsStartOffsetVideo", ".mp4")
+                        .getAbsolutePath();
+        try {
+            Vector<Integer> startOffsetUsVect = new Vector<Integer>();
+            // Video starts at 500000us.
+            startOffsetUsVect.add(500000);
+            // Audio starts at 250000us.
+            startOffsetUsVect.add(250000);
+            cloneMediaWithSamplesDropAndStartOffsets(sourceId, outputFilePath,
+                    MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, null, startOffsetUsVect);
+            verifyTSWithSamplesDropAndStartOffset(
+                    sourceId, false /* no B frames */, outputFilePath, null, startOffsetUsVect);
         } finally {
             new File(outputFilePath).delete();
         }
@@ -467,7 +570,8 @@
                 verifyLocationInFile(outputMediaFile);
             }
             // Verify timestamp of all samples.
-            verifyTimestampsWithSamplesDropSet(srcMedia, outputMediaFile, null, null);
+            verifyTSWithSamplesDropAndStartOffset(
+                    srcMedia, false /* no B frames */,outputMediaFile, null, null);
         } finally {
             new File(outputMediaFile).delete();
         }
@@ -781,7 +885,7 @@
                 if (trackIndex == 0) {
                     ++videoSampleCount;
                     if (VERBOSE) {
-                        Log.i(TAG, "videoSampleCount : " + videoSampleCount);
+                        Log.v(TAG, "videoSampleCount : " + videoSampleCount);
                     }
                     if (videoSampleCount <= muxAllTypeVideoFramesUntilIndex
                             || videoSampleCount == bFrameAfterPFrameIndex) {
@@ -959,10 +1063,15 @@
                 bufferInfo.presentationTimeUs = extractor.getSampleTime();
                 bufferInfo.flags = extractor.getSampleFlags();
                 int trackIndex = extractor.getSampleTrackIndex();
+                if (VERBOSE) {
+                    Log.v(TAG, "TrackIndex:" + trackIndex + " PresentationTimeUs:" +
+                                bufferInfo.presentationTimeUs + " Flags:" + bufferInfo.flags +
+                                " Size(bytes)" + bufferInfo.size);
+                }
                 if (trackIndex == videoTrackIndex) {
                     ++videoSampleCount;
                     if (VERBOSE) {
-                        Log.i(TAG, "videoSampleCount : " + videoSampleCount);
+                        Log.v(TAG, "videoSampleCount : " + videoSampleCount);
                     }
                     if (samplesDropSet == null || (!samplesDropSet.contains(videoSampleCount))) {
                         // Write video frame with start offset adjustment.
@@ -971,7 +1080,7 @@
                     }
                     else {
                         if (VERBOSE) {
-                            Log.i(TAG, "skipped this frame");
+                            Log.v(TAG, "skipped this frame");
                         }
                     }
                 } else {
@@ -1003,8 +1112,9 @@
      * Uses MediaExtractors and checks whether timestamps of all samples except in samplesDropSet
      *  and with start offsets adjustments for each track match.
      */
-    private void verifyTimestampsWithSamplesDropSet(int srcMediaId, String testMediaPath,
-            HashSet<Integer> samplesDropSet, Vector<Integer> startOffsetUsVect) throws IOException {
+    private void verifyTSWithSamplesDropAndStartOffset(int srcMediaId, boolean hasBframes,
+            String testMediaPath, HashSet<Integer> samplesDropSet,
+            Vector<Integer> startOffsetUsVect) throws IOException {
         AssetFileDescriptor srcFd = mResources.openRawResourceFd(srcMediaId);
         MediaExtractor extractorSrc = new MediaExtractor();
         extractorSrc.setDataSource(srcFd.getFileDescriptor(),
@@ -1014,8 +1124,19 @@
 
         int videoTrackIndex = -1;
         int videoStartOffsetUs = 0;
+        int minStartOffsetUs = Integer.MAX_VALUE;
         int trackCount = extractorSrc.getTrackCount();
 
+        // MPEG4Writer makes the start timestamp of an earliest track as zero and adjusts all
+        // other tracks' timestamp accordingly.
+        if (startOffsetUsVect != null) {
+            for (int startOffsetUs : startOffsetUsVect) {
+                minStartOffsetUs = Math.min(startOffsetUs, minStartOffsetUs);
+            }
+        } else {
+            minStartOffsetUs = 0;
+        }
+
         // Select video track.
         for (int i = 0; i < trackCount; i++) {
             MediaFormat format = extractorSrc.getTrackFormat(i);
@@ -1026,8 +1147,8 @@
                 }
                 extractorSrc.selectTrack(videoTrackIndex);
                 extractorTest.selectTrack(videoTrackIndex);
-                checkVideoSamplesTimeStamps(extractorSrc, extractorTest, samplesDropSet,
-                    videoStartOffsetUs);
+                checkVideoSamplesTimeStamps(extractorSrc, hasBframes, extractorTest, samplesDropSet,
+                        videoStartOffsetUs - minStartOffsetUs);
                 extractorSrc.unselectTrack(videoTrackIndex);
                 extractorTest.unselectTrack(videoTrackIndex);
             }
@@ -1046,7 +1167,8 @@
                 }
                 extractorSrc.selectTrack(audioTrackIndex);
                 extractorTest.selectTrack(audioTrackIndex);
-                checkAudioSamplesTimestamps(extractorSrc, extractorTest, audioStartOffsetUs);
+                checkAudioSamplesTimestamps(
+                        extractorSrc, extractorTest, audioStartOffsetUs - minStartOffsetUs);
             }
         }
 
@@ -1056,9 +1178,8 @@
     }
 
     // Check timestamps of all video samples.
-    private void checkVideoSamplesTimeStamps(MediaExtractor extractorSrc,
-                MediaExtractor extractorTest, HashSet<Integer> samplesDropSet,
-                int videoStartOffsetUs) {
+    private void checkVideoSamplesTimeStamps(MediaExtractor extractorSrc, boolean hasBFrames,
+            MediaExtractor extractorTest, HashSet<Integer> samplesDropSet, int videoStartOffsetUs) {
         long srcSampleTimeUs = -1;
         long testSampleTimeUs = -1;
         boolean srcAdvance = false;
@@ -1068,43 +1189,64 @@
         extractorSrc.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
         extractorTest.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
 
+        if (VERBOSE) {
+            Log.v(TAG, "videoSampleCount:" + videoSampleCount);
+            Log.v(TAG, "srcTrackIndex:" + extractorSrc.getSampleTrackIndex() +
+                        "  testTrackIndex:" + extractorTest.getSampleTrackIndex());
+        }
+
         do {
             ++videoSampleCount;
             srcSampleTimeUs = extractorSrc.getSampleTime();
             testSampleTimeUs = extractorTest.getSampleTime();
             if (VERBOSE) {
-                Log.i(TAG, "videoSampleCount:" + videoSampleCount);
-                Log.i(TAG, "srcTrackIndex:" + extractorSrc.getSampleTrackIndex() +
-                            "  testTrackIndex:" + extractorTest.getSampleTrackIndex());
                 Log.i(TAG, "srcTSus:" + srcSampleTimeUs + " testTSus:" + testSampleTimeUs);
             }
             if (samplesDropSet == null || !samplesDropSet.contains(videoSampleCount)) {
                 if (srcSampleTimeUs == -1 || testSampleTimeUs == -1) {
                   if (VERBOSE) {
-                    Log.d(TAG, "videoSampleCount:" + videoSampleCount);
-                    Log.d(TAG, "srcUs:" + srcSampleTimeUs + "testUs:" + testSampleTimeUs);
+                      Log.v(TAG, "srcUs:" + srcSampleTimeUs + "testUs:" + testSampleTimeUs);
                   }
                   fail("either source or test track reached end of stream");
                 }
                 // Stts values within 0.1ms(100us) difference are fudged to save too many
                 // stts entries in MPEG4Writer.
-                else if (Math.abs(srcSampleTimeUs + videoStartOffsetUs - testSampleTimeUs) > 100) {
+                // If Bframes are not present, then we manage start offset of video track in the
+                // duration of the first video sample. Second sample onwards has original timestamp.
+                else if (!hasBFrames
+                        && ((videoSampleCount > 1
+                        && Math.abs(srcSampleTimeUs + videoStartOffsetUs - testSampleTimeUs) > 100)
+                        || (videoSampleCount == 1
+                        && Math.abs(srcSampleTimeUs - testSampleTimeUs) > 100))) {
                     if (VERBOSE) {
-                        Log.d(TAG, "Fail:video timestamps didn't match");
-                        Log.d(TAG, "srcTrackIndex:" + extractorSrc.getSampleTrackIndex() +
-                            "  testTrackIndex:" + extractorTest.getSampleTrackIndex());
-                        Log.d(TAG, "srcTSus:" + srcSampleTimeUs + " testTSus:" + testSampleTimeUs);
-                        Log.d(TAG, "videoSampleCount:" + videoSampleCount);
+                        Log.v(TAG, "Fail:video timestamps didn't match");
+                        Log.v(TAG, "srcTrackIndex:" + extractorSrc.getSampleTrackIndex() +
+                                    "  testTrackIndex:" + extractorTest.getSampleTrackIndex());
+                        Log.v(TAG, "srcTSus:" + srcSampleTimeUs + " testTSus:" + testSampleTimeUs);
                     }
                     fail("video timestamps didn't match");
                 }
+                // If Bframes are present, then we manage start offsert of a video track in the
+                // corresponding edit list entry.
+                else if (hasBFrames
+                        && Math.abs(srcSampleTimeUs + videoStartOffsetUs - testSampleTimeUs)
+                        > 100) {
+                    if (VERBOSE) {
+                        Log.v(TAG, "Fail:video timestamps with B frames didn't match");
+                        Log.v(TAG,  "srcTrackIndex:" + extractorSrc.getSampleTrackIndex() +
+                                    "  testTrackIndex:" + extractorTest.getSampleTrackIndex());
+                        Log.v(TAG, "srcTSus:" + srcSampleTimeUs + " testTSus:" + testSampleTimeUs);
+                    }
+                    fail("video timestamps with B frames didn't match");
+                }
+
                 testAdvance = extractorTest.advance();
             }
             srcAdvance = extractorSrc.advance();
         } while(srcAdvance && testAdvance);
         if (srcAdvance != testAdvance) {
             if (VERBOSE) {
-                Log.d(TAG, "videoSampleCount:" + videoSampleCount);
+                Log.v(TAG, "videoSampleCount:" + videoSampleCount);
             }
             fail("either video track has not reached its last sample");
         }
@@ -1120,28 +1262,29 @@
 
         extractorSrc.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
         extractorTest.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
-
+        if (VERBOSE) {
+            Log.v(TAG, "audioStartOffsetUs: " + audioStartOffsetUs);
+            Log.v(TAG, "srcTrackIndex:" + extractorSrc.getSampleTrackIndex() +
+                        "  testTrackIndex:" + extractorTest.getSampleTrackIndex());
+        }
         // Check timestamps of all audio samples.
         do {
             ++audioSampleCount;
             srcSampleTimeUs = extractorSrc.getSampleTime();
             testSampleTimeUs = extractorTest.getSampleTime();
             if(VERBOSE) {
-                Log.d(TAG, "audioSampleCount:" + audioSampleCount);
-                Log.v(TAG, "srcTrackIndex:" + extractorSrc.getSampleTrackIndex() +
-                            "  testTrackIndex:" + extractorTest.getSampleTrackIndex());
+                Log.v(TAG, "audioSampleCount:" + audioSampleCount);
                 Log.v(TAG, "srcTSus:" + srcSampleTimeUs + " testTSus:" + testSampleTimeUs);
             }
 
             if (srcSampleTimeUs == -1 || testSampleTimeUs == -1) {
               if (VERBOSE) {
-                Log.d(TAG, "audioSampleCount:" + audioSampleCount);
-                Log.d(TAG, "srcTSus:" + srcSampleTimeUs + " testTSus:" + testSampleTimeUs);
+                  Log.v(TAG, "srcTSus:" + srcSampleTimeUs + " testTSus:" + testSampleTimeUs);
               }
               fail("either source or test track reached end of stream");
             }
-            // First audio sample would have zero timestamp and its start offset is implemented
-            // by assigning the first audio sample's duration as the offset. Second sample onwards
+            // First audio sample would be at zero.  Audio track's start offset is implemented
+            // by assigning the first audio sample's duration as that of the offset. Second sample
             // would play after the offset.  But video offset is achieved by edit list entry for
             // video tracks with BFrames. Need to revert the conditional check for first
             // audio sample once we implement empty edit list entry for audio.
diff --git a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
index be33988..e2d4421 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;
@@ -231,6 +232,7 @@
         int height;
         Camera camera = null;
         if (!hasCamera()) {
+            MediaUtils.skipTest("no camera");
             return;
         }
         // Try to get camera profile for QUALITY_LOW; if unavailable,
@@ -543,6 +545,7 @@
 
     public void testRecorderVideo() throws Exception {
         if (!hasCamera()) {
+            MediaUtils.skipTest("no camera");
             return;
         }
         mCamera = Camera.open(0);
@@ -567,6 +570,7 @@
 
     public void testSetOutputFile() throws Exception {
         if (!hasCamera()) {
+            MediaUtils.skipTest("no camera");
             return;
         }
         mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
@@ -850,22 +854,22 @@
         return startTimeOffset + frameIndex * 1000000 / frameRate;
     }
 
-    private void testLevel(String mediaType, int width, int height, int framerate,
-            int bitrate, int profile, int requestedLevel, int... expectedLevels) throws Exception {
+    private int testLevel(String mediaType, int width, int height, int framerate, int bitrate,
+            int profile, int requestedLevel, int... expectedLevels) throws Exception {
         CodecCapabilities cap = getCapsForPreferredCodecForMediaType(mediaType);
         if (cap == null) { // not supported
-            return;
+            return 0;
         }
         MediaCodecInfo.VideoCapabilities vCap = cap.getVideoCapabilities();
         if (!vCap.areSizeAndRateSupported(width, height, framerate)
             || !vCap.getBitrateRange().contains(bitrate * 1000)) {
             Log.i(TAG, "Skip the test");
-            return;
+            return 0;
         }
 
         Surface surface = MediaCodec.createPersistentInputSurface();
         if (surface == null) {
-            return;
+            return 0;
         }
         InputSurface encSurface = new InputSurface(surface);
         mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
@@ -950,9 +954,11 @@
             encSurface.release();
             encSurface = null;
         }
+        return 1;
     }
 
     public void testProfileAvcBaselineLevel1() throws Exception {
+        int testsRun = 0;
         int profile = AVCProfileBaseline;
 
         if (!hasH264()) {
@@ -961,16 +967,18 @@
         }
 
         /*              W    H   fps kbps  profile  request level   expected levels */
-        testLevel(AVC, 176, 144, 15, 64,   profile,  AVCLevel1, AVCLevel1);
+        testsRun += testLevel(AVC, 176, 144, 15, 64, profile, AVCLevel1, AVCLevel1);
         // Enable them when vendor fixes the failure
         //testLevel(AVC, 178, 144, 15, 64,   profile,  AVCLevel1, AVCLevel11);
         //testLevel(AVC, 178, 146, 15, 64,   profile,  AVCLevel1, AVCLevel11);
         //testLevel(AVC, 176, 144, 16, 64,   profile,  AVCLevel1, AVCLevel11);
         //testLevel(AVC, 176, 144, 15, 65,   profile,  AVCLevel1, AVCLevel1b);
-        testLevel(AVC, 176, 144, 15, 64,   profile,  AVCLevel1b, AVCLevel1,
-                AVCLevel1b);
+        testsRun += testLevel(AVC, 176, 144, 15, 64, profile, AVCLevel1b, AVCLevel1, AVCLevel1b);
         // testLevel(AVC, 176, 144, 15, 65,   profile,  AVCLevel2, AVCLevel1b,
         //        AVCLevel11, AVCLevel12, AVCLevel13, AVCLevel2);
+        if (testsRun == 0) {
+            MediaUtils.skipTest("VideoCapabilities or surface not found");
+        }
     }
 
 
@@ -1705,5 +1713,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/media/src/android/media/cts/SoundPoolTest.java b/tests/tests/media/src/android/media/cts/SoundPoolTest.java
index 2c56acd..c426a31 100644
--- a/tests/tests/media/src/android/media/cts/SoundPoolTest.java
+++ b/tests/tests/media/src/android/media/cts/SoundPoolTest.java
@@ -39,9 +39,9 @@
 
     private static final int SOUNDPOOL_STREAMS = 4;
     private static final int PRIORITY = 1;
-    private static final int LOUD = 20;
-    private static final int QUIET = LOUD / 2;
-    private static final int SILENT = 0;
+    private static final float LOUD = 1.f;
+    private static final float QUIET = LOUD / 4.f;
+    private static final float SILENT = 0.f;
     private File mFile;
     private SoundPool mSoundPool;
 
diff --git a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
index c7018a2..81e6a4c 100644
--- a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
+++ b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
@@ -75,6 +75,10 @@
         private LinkedList<Pair<ByteBuffer, BufferInfo>> mStream;
         private MediaFormat mFormat;
         private int mInputBufferSize;
+        // Media buffers(no CSD, no EOS) enqueued.
+        private int mMediaBuffersEnqueuedCount;
+        // Media buffers decoded.
+        private int mMediaBuffersDecodedCount;
 
         public VideoStorage() {
             mStream = new LinkedList<Pair<ByteBuffer, BufferInfo>>();
@@ -93,6 +97,9 @@
             BufferInfo savedInfo = new BufferInfo();
             savedInfo.set(0, savedBuffer.position(), info.presentationTimeUs, info.flags);
             mStream.addLast(Pair.create(savedBuffer, savedInfo));
+            if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+                ++mMediaBuffersEnqueuedCount;
+            }
         }
 
         private void play(MediaCodec decoder, Surface surface) {
@@ -101,6 +108,9 @@
             final Iterator<Pair<ByteBuffer, BufferInfo>> it = mStream.iterator();
             decoder.setCallback(new MediaCodec.Callback() {
                 public void onOutputBufferAvailable(MediaCodec codec, int ix, BufferInfo info) {
+                    if (info.size > 0) {
+                        ++mMediaBuffersDecodedCount;
+                    }
                     codec.releaseOutputBuffer(ix, info.size > 0);
                     if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                         synchronized (condition) {
@@ -146,17 +156,25 @@
                 }
             }
             decoder.stop();
+            // All enqueued media data buffers should have got decoded.
+            if (mMediaBuffersEnqueuedCount != mMediaBuffersDecodedCount) {
+                Log.i(TAG, "mMediaBuffersEnqueuedCount:" + mMediaBuffersEnqueuedCount);
+                Log.i(TAG, "mMediaBuffersDecodedCount:" + mMediaBuffersDecodedCount);
+                fail("not all enqueued encoded media buffers were decoded");
+            }
+            mMediaBuffersDecodedCount = 0;
         }
 
-        public void playAll(Surface surface) {
+        public boolean playAll(Surface surface) {
+            boolean skipped = true;
             if (mFormat == null) {
                 Log.i(TAG, "no stream to play");
-                return;
+                return !skipped;
             }
             String mime = mFormat.getString(MediaFormat.KEY_MIME);
             MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
             for (MediaCodecInfo info : mcl.getCodecInfos()) {
-                if (info.isEncoder()) {
+                if (info.isEncoder() || info.isAlias()) {
                     continue;
                 }
                 MediaCodec codec = null;
@@ -171,7 +189,9 @@
                 }
                 play(codec, surface);
                 codec.release();
+                skipped = false;
             }
+            return !skipped;
         }
     }
 
@@ -405,9 +425,7 @@
             }
         }
 
-        public void playBack(Surface surface) {
-            mEncodedStream.playAll(surface);
-        }
+        public boolean playBack(Surface surface) { return mEncodedStream.playAll(surface); }
 
         public void setFrameAndBitRates(int frameRate, int bitRate) {
             mFrameRate = frameRate;
@@ -1147,7 +1165,7 @@
             boolean success = processor.processLoop(
                     SOURCE_URL, mMime, mName, width, height, optional);
             if (success) {
-                processor.playBack(getActivity().getSurfaceHolder().getSurface());
+                success = processor.playBack(getActivity().getSurfaceHolder().getSurface());
             }
             return success;
         }
@@ -1195,7 +1213,7 @@
         ArrayList<Encoder> result = new ArrayList<Encoder>();
 
         for (MediaCodecInfo info : mcl.getCodecInfos()) {
-            if (!info.isEncoder() || !info.isVendor() != goog) {
+            if (!info.isEncoder() || !info.isVendor() != goog || info.isAlias()) {
                 continue;
             }
             CodecCapabilities caps = null;
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/mediametrics/Android.bp b/tests/tests/nativemedia/mediametrics/Android.bp
new file mode 100644
index 0000000..6e1ecc1
--- /dev/null
+++ b/tests/tests/nativemedia/mediametrics/Android.bp
@@ -0,0 +1,54 @@
+// 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.
+
+// Build the unit tests.
+
+cc_test {
+    name: "CtsNativeMediaMetricsTestCases",
+
+    compile_multilib: "both",
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+
+    srcs: ["src/MediaMetricsTest.cpp"],
+
+    shared_libs: [
+        "liblog",
+        "libmediametrics",
+        "libutils",
+    ],
+
+    static_libs: [
+        "libgtest",
+    ],
+
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+
+}
diff --git a/tests/tests/nativemedia/mediametrics/AndroidTest.xml b/tests/tests/nativemedia/mediametrics/AndroidTest.xml
new file mode 100644
index 0000000..b84510f
--- /dev/null
+++ b/tests/tests/nativemedia/mediametrics/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for CTS Native Media Metrics test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="media" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="CtsNativeMediaMetricsTestCases->/data/local/tmp/CtsNativeMediaMetricsTestCases" />
+        <option name="append-bitness" value="true" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="CtsNativeMediaMetricsTestCases" />
+        <option name="runtime-hint" value="1m" />
+    </test>
+</configuration>
diff --git a/tests/tests/nativemedia/mediametrics/src/MediaMetricsTest.cpp b/tests/tests/nativemedia/mediametrics/src/MediaMetricsTest.cpp
new file mode 100644
index 0000000..c5b023d
--- /dev/null
+++ b/tests/tests/nativemedia/mediametrics/src/MediaMetricsTest.cpp
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "MediaMetricsTest"
+
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+#include <MediaMetrics.h>
+
+//-----------------------------------------------------------------
+class MediaMetricsTest : public ::testing::Test {
+
+protected:
+    MediaMetricsTest() { }
+
+    virtual ~MediaMetricsTest() { }
+
+    /* Test setup*/
+    virtual void SetUp() {
+        handle_ = mediametrics_create("foo");
+    }
+
+    virtual void TearDown() {
+        mediametrics_delete(handle_);
+    }
+
+    mediametrics_handle_t handle_;
+};
+
+//-------------------------------------------------------------------------------------------------
+TEST_F(MediaMetricsTest, testCreateDelete) {
+    // Will be done with SetUp and TearDown.
+}
+
+TEST_F(MediaMetricsTest, testInt32) {
+    mediametrics_setInt32(handle_, "attr1", 100);
+    int32_t value;
+    EXPECT_TRUE(mediametrics_getInt32(handle_, "attr1",  &value));
+    EXPECT_EQ(100, value);
+
+    mediametrics_addInt32(handle_, "attr1", 50);
+    EXPECT_TRUE(mediametrics_getInt32(handle_, "attr1",  &value));
+    EXPECT_EQ(150, value);
+}
+
+TEST_F(MediaMetricsTest, testInt64) {
+    mediametrics_setInt64(handle_, "attr2", 1e10);
+    int64_t value;
+    EXPECT_TRUE(mediametrics_getInt64(handle_, "attr2",  &value));
+    EXPECT_EQ(1e10, value);
+
+    mediametrics_addInt64(handle_, "attr2", 50);
+    EXPECT_TRUE(mediametrics_getInt64(handle_, "attr2",  &value));
+    EXPECT_EQ(1e10 + 50, value);
+}
+
+TEST_F(MediaMetricsTest, testDouble) {
+    mediametrics_setDouble(handle_, "attr3", 100.0);
+    double value;
+    EXPECT_TRUE(mediametrics_getDouble(handle_, "attr3",  &value));
+    EXPECT_DOUBLE_EQ(100.0, value);
+
+    mediametrics_addDouble(handle_, "attr3", 50.0);
+    EXPECT_TRUE(mediametrics_getDouble(handle_, "attr3",  &value));
+    EXPECT_DOUBLE_EQ(150.0, value);
+}
+
+TEST_F(MediaMetricsTest, testRate) {
+    mediametrics_setRate(handle_, "attr4", 30, 1000);
+    int64_t count;
+    int64_t duration;
+    double rate;
+    EXPECT_TRUE(mediametrics_getRate(handle_, "attr4",  &count, &duration, &rate));
+    EXPECT_EQ(30, count);
+    EXPECT_EQ(1000, duration);
+    EXPECT_DOUBLE_EQ(30/1000.0, rate);
+
+    mediametrics_addRate(handle_, "attr4", 29, 1000);
+    EXPECT_TRUE(mediametrics_getRate(handle_, "attr4",  &count, &duration, &rate));
+    EXPECT_EQ(59, count);
+    EXPECT_EQ(2000, duration);
+    EXPECT_DOUBLE_EQ(59/2000.0, rate);
+}
+
+TEST_F(MediaMetricsTest, testCString) {
+    mediametrics_setCString(handle_, "attr5", "test_string");
+    char *value = nullptr;
+    EXPECT_TRUE(mediametrics_getCString(handle_, "attr5",  &value));
+    EXPECT_STREQ("test_string", value);
+    mediametrics_freeCString(value);
+}
+
+TEST_F(MediaMetricsTest, testCount) {
+    mediametrics_setInt32(handle_, "attr1", 100);
+    EXPECT_EQ(1, mediametrics_count(handle_));
+    mediametrics_setInt32(handle_, "attr2", 200);
+    mediametrics_setInt32(handle_, "attr3", 300);
+    EXPECT_EQ(3, mediametrics_count(handle_));
+}
+
+TEST_F(MediaMetricsTest, testReadable) {
+    mediametrics_setInt32(handle_, "attr1", 1);
+    mediametrics_setInt64(handle_, "attr2", 2);
+    mediametrics_setDouble(handle_, "attr3", 3.0);
+    mediametrics_setRate(handle_, "attr4", 4, 5);
+    mediametrics_setCString(handle_, "attr5", "test_string");
+
+    EXPECT_TRUE(strlen(mediametrics_readable(handle_)) > 0);
+}
+
+TEST_F(MediaMetricsTest, testSelfRecord) {
+    mediametrics_setInt32(handle_, "attr1", 100);
+    mediametrics_setInt64(handle_, "attr2", 1e10);
+    mediametrics_setDouble(handle_, "attr3", 100.0);
+    mediametrics_setRate(handle_, "attr4", 30, 1000);
+    mediametrics_setCString(handle_, "attr5", "test_string");
+    mediametrics_setUid(handle_, 10000);
+
+    // An invalid key 'foo' is used here. Expecting FALSE.
+    EXPECT_FALSE(mediametrics_selfRecord(handle_));
+}
+
+TEST_F(MediaMetricsTest, testIsEnabled) {
+    EXPECT_TRUE(mediametrics_isEnabled());
+}
+
+int main(int argc, char **argv) {
+    testing::InitGoogleTest(&argc, argv);
+
+    return RUN_ALL_TESTS();
+}
+
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..32e46ac 100644
--- a/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
+++ b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
@@ -76,6 +76,16 @@
         return pm.hasSystemFeature(PackageManager.FEATURE_MIDI);
     }
 
+    public static boolean hasLibAMidi() {
+        try {
+            System.loadLibrary("amidi");
+        } catch (UnsatisfiedLinkError ex) {
+            Log.e(TAG, "libamidi.so not found.");
+            return false;
+        }
+        return true;
+    }
+
     private byte[] generateRandomMessage(int len) {
         byte[] buffer = new byte[len];
         for(int index = 0; index < len; index++) {
@@ -131,7 +141,6 @@
     }
 
      protected void setUpEchoServer() throws Exception {
-        Log.i(TAG, "++ setUpEchoServer()");
         MidiDeviceInfo echoInfo = MidiEchoTestService.findEchoDevice(mContext);
 
         // Open device.
@@ -161,7 +170,6 @@
     }
 
     protected void tearDownEchoServer() throws IOException {
-        Log.i(TAG, "++ tearDownEchoServer()");
         // Query echo service directly to see if it is getting status updates.
         MidiEchoTestService echoService = MidiEchoTestService.getInstance();
 
@@ -209,11 +217,11 @@
 //
     @Before
     public void setUp() throws Exception {
-        Log.i(TAG, "++ setUp() mContext:" + mContext);
         if (!hasMidiSupport()) {
             Assert.assertTrue("FEATURE_MIDI Not Supported.", false);
             return; // Not supported so don't test it.
         }
+
         mMidiManager = (MidiManager)mContext.getSystemService(Context.MIDI_SERVICE);
         Assert.assertNotNull("Could not get the MidiManger.", mMidiManager);
 
@@ -222,16 +230,17 @@
 
     @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()");
         mMidiManager = null;
     }
 
     @Test
     public void test_A_MidiManager() throws Exception {
-        Log.i(TAG, "++++ test_A_MidiManager() this:" + System.identityHashCode(this));
-
         if (!hasMidiSupport()) {
             return; // Nothing to test
         }
@@ -242,13 +251,19 @@
         MidiDeviceInfo[] infos = mMidiManager.getDevices();
         Assert.assertNotNull("device list was null", infos);
         Assert.assertTrue("device list was empty", infos.length >= 1);
+    }
 
-        Log.i(TAG, "++++ test_A_MidiManager() - DONE");
+
+    @Test
+    public void test_AA_LibAMidiExists() throws Exception {
+        Assert.assertTrue("libamidi.so not found.", hasLibAMidi());
     }
 
     @Test
     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));
@@ -262,13 +277,10 @@
         Assert.assertTrue("Didn't get 1 send", getNumBytesSent(mTestContext) == buffer.length);
         Assert.assertEquals("Didn't get right number of bytes sent",
                 buffer.length, getNumBytesSent(mTestContext));
-
-        Log.i(TAG, "++++ test_B_SendData() - DONE");
     }
 
     @Test
     public void test_C_EchoSmallMessage() throws Exception {
-        Log.i(TAG, "++++ test_C_EchoSmallMessage() this:" + System.identityHashCode(this));
         if (!hasMidiSupport()) {
             return; // nothing to test
         }
@@ -291,13 +303,10 @@
 
         NativeMidiMessage message = getReceivedMessageAt(mTestContext, 0);
         compareMessages(buffer, timestamp, message);
-
-        Log.i(TAG, "++++ test_C_EchoSmallMessage() - DONE");
     }
 
     @Test
     public void test_D_EchoNMessages() throws Exception {
-        Log.i(TAG, "++++ test_D_EchoNMessages() this:" + System.identityHashCode(this));
         if (!hasMidiSupport()) {
             return; // nothing to test
         }
@@ -325,13 +334,10 @@
             NativeMidiMessage message = getReceivedMessageAt(mTestContext, msgIndex);
             compareMessages(buffers[msgIndex], timestamps[msgIndex], message);
         }
-
-        Log.i(TAG, "++++ test_D_EchoNMessages() - DONE");
     }
 
     @Test
     public void test_E_FlushMessages() throws Exception {
-        Log.i(TAG, "++++ test_E_FlushMessages() this:" + System.identityHashCode(this));
         if (!hasMidiSupport()) {
             return; // nothing to test
         }
@@ -362,13 +368,10 @@
             NativeMidiMessage message = getReceivedMessageAt(mTestContext, msgIndex);
             compareMessages(buffers[msgIndex], timestamps[msgIndex], message);
         }
-
-        Log.i(TAG, "++++ test_E_FlushMessages() - DONE");
     }
 
     @Test
     public void test_F_HugeMessage() throws Exception {
-        Log.i(TAG, "++++ test_F_HugeMessage() this:" + System.identityHashCode(this));
         if (!hasMidiSupport()) {
             return; // nothing to test
         }
@@ -383,8 +386,6 @@
         buffer = generateRandomMessage(kindaHugeMessageLen);
         result = writeMidi(mTestContext, buffer, 0, buffer.length);
         Assert.assertEquals("Kinda big write failed.", kindaHugeMessageLen, result);
-
-        Log.i(TAG, "++++ test_F_HugeMessage() - DONE");
     }
 
     /**
@@ -393,7 +394,6 @@
      */
     @Test
     public void test_G_NativeEchoTime() throws Exception {
-        Log.i(TAG, "++++ test_G_NativeEchoTime() this:" + System.identityHashCode(this));
         if (!hasMidiSupport()) {
             return; // nothing to test
         }
@@ -426,13 +426,10 @@
                     "timestamp:" + message.timestamp + " received:" + message.timeReceived,
                     (elapsedNanos < maxLatencyNanos));
         }
-
-        Log.i(TAG, "++++ test_G_NativeEchoTime() - DONE");
     }
 
     @Test
     public void test_H_EchoNMessages_PureNative() throws Exception {
-        Log.i(TAG, "++++ test_H_EchoNMessages_PureNative() this:" + System.identityHashCode(this));
         if (!hasMidiSupport()) {
             return; // nothing to test
         }
@@ -453,8 +450,6 @@
 
         int result = matchNativeMessages(mTestContext);
         Assert.assertEquals("Native Compare Test Failed", result, 0);
-
-        Log.i(TAG, "++++ test_H_EchoNMessages_PureNative() - DONE");
     }
 
     /**
@@ -463,8 +458,6 @@
      */
     @Test
     public void test_I_NativeEchoTime_PureNative() throws Exception {
-        Log.i(TAG, "++++ test_I_NativeEchoTime_PureNative() this:"
-                + System.identityHashCode(this));
         if (!hasMidiSupport()) {
             return; // nothing to test
         }
@@ -487,8 +480,6 @@
 
         int result = checkNativeLatency(mTestContext, maxLatencyNanos);
         Assert.assertEquals("failed pure native latency test.", 0, result);
-
-        Log.i(TAG, "++++ test_I_NativeEchoTime_PureNative() - DONE");
     }
 
     // Native Routines
diff --git a/tests/tests/net/src/android/net/rtp/cts/AudioGroupTest.java b/tests/tests/net/src/android/net/rtp/cts/AudioGroupTest.java
index 1bd7fad..fee8621 100644
--- a/tests/tests/net/src/android/net/rtp/cts/AudioGroupTest.java
+++ b/tests/tests/net/src/android/net/rtp/cts/AudioGroupTest.java
@@ -62,7 +62,7 @@
         mSocketB.connect(mStreamB.getLocalAddress(), mStreamB.getLocalPort());
         mStreamB.associate(mSocketB.getLocalAddress(), mSocketB.getLocalPort());
 
-        mGroup = new AudioGroup();
+        mGroup = new AudioGroup(mContext);
     }
 
     @Override
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 95f8126..8b24ee5 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: [
@@ -41,28 +39,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/AndroidTest.xml b/tests/tests/packageinstaller/atomicinstall/AndroidTest.xml
index e248a62..88a142d 100644
--- a/tests/tests/packageinstaller/atomicinstall/AndroidTest.xml
+++ b/tests/tests/packageinstaller/atomicinstall/AndroidTest.xml
@@ -24,8 +24,10 @@
         <option name="test-file-name" value="CtsAtomicInstallTestCases.apk" />
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <option name="teardown-command" value="pm uninstall com.android.tests.atomicinstall.testapp.A" />
-        <option name="teardown-command" value="pm uninstall com.android.tests.atomicinstall.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.AndroidJUnitTest" >
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..a4ee42b 100644
--- a/tests/tests/permission/AndroidTest.xml
+++ b/tests/tests/permission/AndroidTest.xml
@@ -42,6 +42,8 @@
         <option name="push" value="CtsAppThatRequestsLocationPermission29v4.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsLocationPermission29v4.apk" />
         <option name="push" value="CtsAppThatRequestsLocationPermission28.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsLocationPermission28.apk" />
         <option name="push" value="CtsAppThatRequestsLocationPermission22.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsLocationPermission22.apk" />
+        <option name="push" value="CtsAppThatRequestsStoragePermission29.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsStoragePermission29.apk" />
+        <option name="push" value="CtsAppThatRequestsStoragePermission28.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsStoragePermission28.apk" />
         <option name="push" value="CtsAppThatRequestsLocationAndBackgroundPermission29.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsLocationAndBackgroundPermission29.apk" />
         <option name="push" value="CtsAppThatAccessesLocationOnCommand.apk->/data/local/tmp/cts/permissions/CtsAppThatAccessesLocationOnCommand.apk" />
         <option name="push" value="AppThatDoesNotHaveBgLocationAccess.apk->/data/local/tmp/cts/permissions/AppThatDoesNotHaveBgLocationAccess.apk" />
@@ -50,12 +52,23 @@
         <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" />
+        <option name="push" value="CtsAdversarialPermissionUserApp.apk->/data/local/tmp/cts/permissions/CtsAdversarialPermissionUserApp.apk" />
+        <option name="push" value="CtsAdversarialPermissionDefinerApp.apk->/data/local/tmp/cts/permissions/CtsAdversarialPermissionDefinerApp.apk" />
+        <option name="push" value="CtsVictimPermissionDefinerApp.apk->/data/local/tmp/cts/permissions/CtsVictimPermissionDefinerApp.apk" />
+        <option name="push" value="CtsRuntimePermissionDefinerApp.apk->/data/local/tmp/cts/permissions/CtsRuntimePermissionDefinerApp.apk" />
+        <option name="push" value="CtsRuntimePermissionUserApp.apk->/data/local/tmp/cts/permissions/CtsRuntimePermissionUserApp.apk" />
     </target_preparer>
 
     <!-- Remove additional apps if installed -->
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="teardown-command" value="pm uninstall android.permission.cts.appthatrequestpermission" />
         <option name="teardown-command" value="pm uninstall android.permission.cts.appthatrequestnopermission" />
+        <option name="teardown-command" value="pm uninstall android.permission.cts.revokepermissionwhenremoved.AdversarialPermissionDefinerApp" />
+        <option name="teardown-command" value="pm uninstall android.permission.cts.revokepermissionwhenremoved.VictimPermissionDefinerApp" />
+        <option name="teardown-command" value="pm uninstall android.permission.cts.revokepermissionwhenremoved.userapp" />
+        <option name="teardown-command" value="pm uninstall android.permission.cts.revokepermissionwhenremoved.runtimepermissiondefinerapp" />
+        <option name="teardown-command" value="pm uninstall android.permission.cts.revokepermissionwhenremoved.runtimepermissionuserapp" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/tests/backup/app/permission22/Android.mk b/tests/tests/permission/AppThatRequestStoragePermission28/Android.mk
similarity index 82%
rename from tests/backup/app/permission22/Android.mk
rename to tests/tests/permission/AppThatRequestStoragePermission28/Android.mk
index 8790e19..ace3264 100644
--- a/tests/backup/app/permission22/Android.mk
+++ b/tests/tests/permission/AppThatRequestStoragePermission28/Android.mk
@@ -1,3 +1,4 @@
+#
 # Copyright (C) 2019 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -11,21 +12,19 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-
+#
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
 
-LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-
-LOCAL_PACKAGE_NAME := CtsPermissionBackupApp22
-LOCAL_SDK_VERSION := current
-
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+LOCAL_PACKAGE_NAME := CtsAppThatRequestsStoragePermission28
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/permission/AppThatRequestStoragePermission28/AndroidManifest.xml b/tests/tests/permission/AppThatRequestStoragePermission28/AndroidManifest.xml
new file mode 100644
index 0000000..a847f39
--- /dev/null
+++ b/tests/tests/permission/AppThatRequestStoragePermission28/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.permission.cts.appthatrequestpermission"
+    android:versionCode="2">
+
+    <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
+
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+
+    <application />
+</manifest>
+
diff --git a/tests/backup/app/permission22/Android.mk b/tests/tests/permission/AppThatRequestStoragePermission29/Android.mk
similarity index 82%
copy from tests/backup/app/permission22/Android.mk
copy to tests/tests/permission/AppThatRequestStoragePermission29/Android.mk
index 8790e19..aa9fabf 100644
--- a/tests/backup/app/permission22/Android.mk
+++ b/tests/tests/permission/AppThatRequestStoragePermission29/Android.mk
@@ -1,3 +1,4 @@
+#
 # Copyright (C) 2019 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -11,21 +12,19 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-
+#
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
 
-LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-
-LOCAL_PACKAGE_NAME := CtsPermissionBackupApp22
-LOCAL_SDK_VERSION := current
-
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+LOCAL_PACKAGE_NAME := CtsAppThatRequestsStoragePermission29
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/permission/AppThatRequestStoragePermission29/AndroidManifest.xml b/tests/tests/permission/AppThatRequestStoragePermission29/AndroidManifest.xml
new file mode 100644
index 0000000..c783085
--- /dev/null
+++ b/tests/tests/permission/AppThatRequestStoragePermission29/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.permission.cts.appthatrequestpermission"
+    android:versionCode="1">
+
+    <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
+
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+
+    <application />
+</manifest>
+
diff --git a/common/device-side/util/tests/Android.bp b/tests/tests/permission/AppThatRunsRationaleTests/Android.bp
similarity index 62%
rename from common/device-side/util/tests/Android.bp
rename to tests/tests/permission/AppThatRunsRationaleTests/Android.bp
index 2abba37..3447de4 100644
--- a/common/device-side/util/tests/Android.bp
+++ b/tests/tests/permission/AppThatRunsRationaleTests/Android.bp
@@ -1,28 +1,31 @@
-// 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",
+    ],
+
+    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 fccfeef..c44024b 100644
--- a/tests/tests/permission/OWNERS
+++ b/tests/tests/permission/OWNERS
@@ -4,3 +4,4 @@
 per-file RequestLocation.java = hallliu@google.com
 per-file NoAudioPermissionTest.java = elaurent@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 4ecf5b6..af9024f 100644
--- a/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
+++ b/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
@@ -56,7 +56,6 @@
 import android.os.Debug;
 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;
@@ -69,6 +68,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;
 
@@ -79,8 +79,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;
 
@@ -215,26 +213,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..afcd9e9
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/PermissionUpdateListenerTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.revokeRuntimePermission(PACKAGE_NAME, PERMISSION_NAME, sContext.getUser());
+            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.grantRuntimePermission(PACKAGE_NAME, PERMISSION_NAME, sContext.getUser());
+            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/permission/src/android/permission/cts/RemovePermissionTest.java b/tests/tests/permission/src/android/permission/cts/RemovePermissionTest.java
new file mode 100644
index 0000000..ff0204b
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/RemovePermissionTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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 android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.GET_PERMISSIONS;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.SecurityTest;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.Before;
+import org.junit.Test;
+
+@AppModeFull(reason = "Instant apps cannot read state of other packages.")
+public class RemovePermissionTest {
+    private static final String APP_PKG_NAME = "android.permission.cts.revokepermissionwhenremoved";
+    private static final String USER_PKG_NAME =
+            "android.permission.cts.revokepermissionwhenremoved.userapp";
+    private static final String TEST_PERMISSION =
+            "android.permission.cts.revokepermissionwhenremoved.TestPermission";
+    private static final String RUNTIME_PERMISSION_USER_PKG_NAME =
+            "android.permission.cts.revokepermissionwhenremoved.runtimepermissionuserapp";
+    private static final String RUNTIME_PERMISSION_DEFINER_PKG_NAME =
+            "android.permission.cts.revokepermissionwhenremoved.runtimepermissiondefinerapp";
+    private static final String TEST_RUNTIME_PERMISSION =
+            "android.permission.cts.revokepermissionwhenremoved.TestRuntimePermission";
+
+    private Context mContext;
+    private Instrumentation mInstrumentation;
+    private Object mMySync = new Object();
+
+    @Before
+    public void setContextAndInstrumentation() {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    }
+
+    @Before
+    public void wakeUpScreen() {
+        SystemUtil.runShellCommand("input keyevent KEYCODE_WAKEUP");
+    }
+
+    private boolean permissionGranted(String pkgName, String permName)
+            throws PackageManager.NameNotFoundException {
+        PackageInfo appInfo = mContext.getPackageManager().getPackageInfo(pkgName,
+                GET_PERMISSIONS);
+
+        for (int i = 0; i < appInfo.requestedPermissions.length; i++) {
+            if (appInfo.requestedPermissions[i].equals(permName)
+                    && ((appInfo.requestedPermissionsFlags[i] & REQUESTED_PERMISSION_GRANTED)
+                    != 0)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void installApp(String apk) throws InterruptedException {
+        String installResult = SystemUtil.runShellCommand(
+                "pm install -r -d data/local/tmp/cts/permissions/" + apk + ".apk");
+        synchronized (mMySync) {
+            mMySync.wait(10000);
+        }
+        assertEquals("Success", installResult.trim());
+    }
+
+    private void uninstallApp(String pkg) throws InterruptedException {
+        String uninstallResult = SystemUtil.runShellCommand(
+                "pm uninstall " + pkg);
+        synchronized (mMySync) {
+            mMySync.wait(10000);
+        }
+        assertEquals("Success", uninstallResult.trim());
+    }
+
+    private void grantPermission(String pkg, String permission) {
+        mInstrumentation.getUiAutomation().grantRuntimePermission(
+                pkg, permission);
+    }
+
+    @SecurityTest
+    @Test
+    public void permissionShouldBeRevokedIfRemoved() throws Throwable {
+        installApp("CtsAdversarialPermissionDefinerApp");
+        installApp("CtsAdversarialPermissionUserApp");
+
+        grantPermission(USER_PKG_NAME, TEST_PERMISSION);
+        assertTrue(permissionGranted(USER_PKG_NAME, TEST_PERMISSION));
+
+        // Uninstall app which defines a permission with the same name as in victim app.
+        // Install the victim app.
+        uninstallApp(APP_PKG_NAME + ".AdversarialPermissionDefinerApp");
+        installApp("CtsVictimPermissionDefinerApp");
+        assertFalse(permissionGranted(USER_PKG_NAME, TEST_PERMISSION));
+        uninstallApp(APP_PKG_NAME + ".userapp");
+        uninstallApp(APP_PKG_NAME + ".VictimPermissionDefinerApp");
+    }
+
+    @Test
+    public void permissionShouldRemainGrantedAfterAppUpdate() throws Throwable {
+        installApp("CtsRuntimePermissionDefinerApp");
+        installApp("CtsRuntimePermissionUserApp");
+
+        grantPermission(RUNTIME_PERMISSION_USER_PKG_NAME, TEST_RUNTIME_PERMISSION);
+        assertTrue(permissionGranted(RUNTIME_PERMISSION_USER_PKG_NAME, TEST_RUNTIME_PERMISSION));
+
+        // Install app which defines a permission. This is similar to update the app
+        // operation
+        installApp("CtsRuntimePermissionDefinerApp");
+        assertTrue(permissionGranted(RUNTIME_PERMISSION_USER_PKG_NAME, TEST_RUNTIME_PERMISSION));
+        uninstallApp(RUNTIME_PERMISSION_USER_PKG_NAME);
+        uninstallApp(RUNTIME_PERMISSION_DEFINER_PKG_NAME);
+    }
+}
diff --git a/tests/tests/permission/src/android/permission/cts/SplitPermissionTest.java b/tests/tests/permission/src/android/permission/cts/SplitPermissionTest.java
index 7731a11..da075c0 100644
--- a/tests/tests/permission/src/android/permission/cts/SplitPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/SplitPermissionTest.java
@@ -18,8 +18,10 @@
 
 import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
 import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_MEDIA_LOCATION;
 import static android.Manifest.permission.READ_CALL_LOG;
 import static android.Manifest.permission.READ_CONTACTS;
+import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
 import static android.app.AppOpsManager.MODE_FOREGROUND;
 import static android.app.AppOpsManager.MODE_IGNORED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
@@ -71,6 +73,10 @@
             TMP_DIR + "CtsAppThatRequestsContactsPermission15.apk";
     private static final String APK_CONTACTS_CALLLOG_16 =
             TMP_DIR + "CtsAppThatRequestsContactsAndCallLogPermission16.apk";
+    private static final String APK_STORAGE_29 =
+            TMP_DIR + "CtsAppThatRequestsStoragePermission29.apk";
+    private static final String APK_STORAGE_28 =
+            TMP_DIR + "CtsAppThatRequestsStoragePermission28.apk";
     private static final String APK_LOCATION_29 =
             TMP_DIR + "CtsAppThatRequestsLocationPermission29.apk";
     private static final String APK_LOCATION_28 =
@@ -260,6 +266,22 @@
      * If a permission was granted before the split happens, the new permission should inherit the
      * granted state.
      *
+     * This is a duplicate of {@link #inheritGrantedPermissionState} but for the storage permission
+     */
+    @Test
+    public void inheritGrantedPermissionStateStorage() throws Exception {
+        install(APK_STORAGE_29);
+        grantPermission(APP_PKG, READ_EXTERNAL_STORAGE);
+
+        install(APK_STORAGE_28);
+
+        assertPermissionGranted(ACCESS_MEDIA_LOCATION);
+    }
+
+    /**
+     * If a permission was granted before the split happens, the new permission should inherit the
+     * granted state.
+     *
      * <p>App using a shared uid
      */
     @Test
diff --git a/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java b/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java
index c15b7a4..de9a30b 100755
--- a/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java
+++ b/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java
@@ -107,15 +107,11 @@
                     break;
                 case READ_EXTERNAL_STORAGE:
                     assertSplit(split, ACCESS_MEDIA_LOCATION, Build.VERSION_CODES.Q);
-                    // Remove this split permission from seenSplits, ACCESS_MEDIA_LOCATION is not
-                    // always available hence removing this permission from seenSplits will
-                    // avoid seenSplits size check fail.
-                    seenSplits.remove(split);
                     break;
             }
         }
 
-        assertEquals(6, seenSplits.size());
+        assertEquals(7, seenSplits.size());
     }
 
     private void assertSplit(SplitPermissionInfo split, String permission, int targetSdk) {
diff --git a/tests/tests/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java b/tests/tests/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java
index 3671255..f0b8117 100644
--- a/tests/tests/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java
+++ b/tests/tests/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java
@@ -33,6 +33,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+
 /**
  * Test the non-location-related functionality of TelephonyManager.
  */
@@ -302,6 +304,27 @@
         }
     }
 
+    /**
+     * Verify that setForbiddenPlmns requires Permission.
+     * <p>
+     * Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     */
+    @Test
+    public void testSetForbiddenPlmns() {
+        if (!mHasTelephony) {
+            return;
+        }
+
+        try {
+            mTelephonyManager.setForbiddenPlmns(new ArrayList<String>());
+            fail("SetForbiddenPlmns did not throw a SecurityException");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
     private static Context getContext() {
         return InstrumentationRegistry.getContext();
     }
diff --git a/tests/backup/app/Android.mk b/tests/tests/permission/testapps/Android.mk
similarity index 91%
rename from tests/backup/app/Android.mk
rename to tests/tests/permission/testapps/Android.mk
index cddf11e..1d314d2 100644
--- a/tests/backup/app/Android.mk
+++ b/tests/tests/permission/testapps/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 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.
diff --git a/tests/backup/app/permission22/Android.mk b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/Android.mk
similarity index 81%
copy from tests/backup/app/permission22/Android.mk
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/Android.mk
index 8790e19..e81acdc 100644
--- a/tests/backup/app/permission22/Android.mk
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/Android.mk
@@ -11,21 +11,20 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-
+#
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
 
-LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-
-LOCAL_PACKAGE_NAME := CtsPermissionBackupApp22
-LOCAL_SDK_VERSION := current
-
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+LOCAL_PACKAGE_NAME := CtsAdversarialPermissionDefinerApp
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/AndroidManifest.xml b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/AndroidManifest.xml
new file mode 100644
index 0000000..d28fba8
--- /dev/null
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.permission.cts.revokepermissionwhenremoved.AdversarialPermissionDefinerApp">
+
+    <permission android:name="android.permission.cts.revokepermissionwhenremoved.TestPermission"
+        android:protectionLevel="dangerous"
+        android:label="TestPermission"
+        android:description="@string/test_permission" />
+
+    <application>
+    </application>
+</manifest>
diff --git a/libs/rollback/testapp/res_v1/values/values.xml b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/res/values/strings.xml
similarity index 84%
copy from libs/rollback/testapp/res_v1/values/values.xml
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/res/values/strings.xml
index 8e71917..bfb3e1e 100644
--- a/libs/rollback/testapp/res_v1/values/values.xml
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/res/values/strings.xml
@@ -14,6 +14,6 @@
      limitations under the License.
 -->
 
-<resources>
-    <integer name="app_version">1</integer>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="test_permission">Test Permission</string>
 </resources>
diff --git a/tests/backup/app/permission22/Android.mk b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/Android.mk
similarity index 81%
copy from tests/backup/app/permission22/Android.mk
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/Android.mk
index 8790e19..8856eb8 100644
--- a/tests/backup/app/permission22/Android.mk
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/Android.mk
@@ -11,21 +11,19 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-
+#
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
 
-LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-
-LOCAL_PACKAGE_NAME := CtsPermissionBackupApp22
-LOCAL_SDK_VERSION := current
-
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey2
+LOCAL_PACKAGE_NAME := CtsAdversarialPermissionUserApp
 
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/AndroidManifest.xml
similarity index 63%
copy from tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/AndroidManifest.xml
index 37276e2..f514d54 100644
--- a/tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/AndroidManifest.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,13 @@
  * WITHOUT WARRANTIES 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;
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.permission.cts.revokepermissionwhenremoved.userapp">
 
-import android.app.Activity;
-import android.os.Bundle;
+    <uses-permission android:name="android.permission.cts.revokepermissionwhenremoved.TestPermission" />
 
-public class MainActivity extends Activity {
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-    }
-}
+    <application>
+    </application>
+</manifest>
diff --git a/tests/backup/app/Android.mk b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/Android.mk
similarity index 91%
copy from tests/backup/app/Android.mk
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/Android.mk
index cddf11e..1d314d2 100644
--- a/tests/backup/app/Android.mk
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 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.
diff --git a/tests/backup/app/permission22/Android.mk b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/Android.mk
similarity index 81%
copy from tests/backup/app/permission22/Android.mk
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/Android.mk
index 8790e19..8e22243 100644
--- a/tests/backup/app/permission22/Android.mk
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/Android.mk
@@ -11,21 +11,20 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-
+#
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
 
-LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-
-LOCAL_PACKAGE_NAME := CtsPermissionBackupApp22
-LOCAL_SDK_VERSION := current
-
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+LOCAL_PACKAGE_NAME := CtsRuntimePermissionDefinerApp
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/AndroidManifest.xml b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/AndroidManifest.xml
new file mode 100644
index 0000000..d3cf6d0
--- /dev/null
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.permission.cts.revokepermissionwhenremoved.runtimepermissiondefinerapp">
+
+    <permission android:name="android.permission.cts.revokepermissionwhenremoved.TestRuntimePermission"
+        android:protectionLevel="dangerous"
+        android:label="TestPermission"
+        android:description="@string/test_permission" />
+
+    <application>
+    </application>
+</manifest>
diff --git a/libs/rollback/testapp/res_v1/values/values.xml b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/res/values/strings.xml
similarity index 84%
copy from libs/rollback/testapp/res_v1/values/values.xml
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/res/values/strings.xml
index 8e71917..bfb3e1e 100644
--- a/libs/rollback/testapp/res_v1/values/values.xml
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/res/values/strings.xml
@@ -14,6 +14,6 @@
      limitations under the License.
 -->
 
-<resources>
-    <integer name="app_version">1</integer>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="test_permission">Test Permission</string>
 </resources>
diff --git a/tests/backup/app/permission22/Android.mk b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/Android.mk
similarity index 81%
copy from tests/backup/app/permission22/Android.mk
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/Android.mk
index 8790e19..112a132 100644
--- a/tests/backup/app/permission22/Android.mk
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/Android.mk
@@ -11,21 +11,19 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-
+#
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
 
-LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-
-LOCAL_PACKAGE_NAME := CtsPermissionBackupApp22
-LOCAL_SDK_VERSION := current
-
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey2
+LOCAL_PACKAGE_NAME := CtsRuntimePermissionUserApp
 
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/AndroidManifest.xml
similarity index 61%
copy from tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/AndroidManifest.xml
index 37276e2..d977e46 100644
--- a/tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/AndroidManifest.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,13 @@
  * WITHOUT WARRANTIES 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;
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.permission.cts.revokepermissionwhenremoved.runtimepermissionuserapp">
 
-import android.app.Activity;
-import android.os.Bundle;
+    <uses-permission android:name="android.permission.cts.revokepermissionwhenremoved.TestRuntimePermission" />
 
-public class MainActivity extends Activity {
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-    }
-}
+    <application>
+    </application>
+</manifest>
diff --git a/tests/backup/app/permission22/Android.mk b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/Android.mk
similarity index 81%
copy from tests/backup/app/permission22/Android.mk
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/Android.mk
index 8790e19..e7f4072 100644
--- a/tests/backup/app/permission22/Android.mk
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/Android.mk
@@ -11,21 +11,21 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-
+#
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
 
-LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-
-LOCAL_PACKAGE_NAME := CtsPermissionBackupApp22
-LOCAL_SDK_VERSION := current
-
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
 
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+LOCAL_PACKAGE_NAME := CtsVictimPermissionDefinerApp
+
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/AndroidManifest.xml b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/AndroidManifest.xml
new file mode 100644
index 0000000..3fb0abd
--- /dev/null
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/AndroidManifest.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.permission.cts.revokepermissionwhenremoved.VictimPermissionDefinerApp">
+    <permission android:name="android.permission.cts.revokepermissionwhenremoved.TestPermission"
+        android:protectionLevel="signature"
+        android:label="Test Permission"
+        android:description="@string/test_permission" />
+    <application>
+    </application>
+</manifest>
diff --git a/libs/rollback/testapp/res_v1/values/values.xml b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/res/values/strings.xml
similarity index 84%
copy from libs/rollback/testapp/res_v1/values/values.xml
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/res/values/strings.xml
index 8e71917..bfb3e1e 100644
--- a/libs/rollback/testapp/res_v1/values/values.xml
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/res/values/strings.xml
@@ -14,6 +14,6 @@
      limitations under the License.
 -->
 
-<resources>
-    <integer name="app_version">1</integer>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="test_permission">Test Permission</string>
 </resources>
diff --git a/tests/tests/permission2/Android.bp b/tests/tests/permission2/Android.bp
index adea669..840359f 100644
--- a/tests/tests/permission2/Android.bp
+++ b/tests/tests/permission2/Android.bp
@@ -25,6 +25,7 @@
     ],
     libs: ["android.test.base.stubs"],
     static_libs: [
+        "androidx.test.core",
         "compatibility-device-util-axt",
         "ctstestrunner-axt",
         "guava",
@@ -32,6 +33,27 @@
         "truth-prebuilt",
         "permission-test-util-lib"
     ],
-    srcs: ["src/**/*.java"],
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt"
+    ],
     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 f2a841d..16ad73f 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. -->
@@ -2835,6 +2823,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.-->
@@ -3463,6 +3456,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
@@ -4218,6 +4216,18 @@
         android:description="@string/permdesc_bindCarrierServices"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi @TestApi Allows an application to forward cell broadcast messages to the cell
+         broadcast module. This is required in order to bind to the cell broadcast service, and
+         ensures that only the system can forward messages to it.
+
+         <p>Protection level: signature|privileged
+
+         @hide -->
+    <permission android:name="android.permission.BIND_CELL_BROADCAST_SERVICE"
+        android:label="@string/permlab_bindCellBroadcastService"
+        android:description="@string/permdesc_bindCellBroadcastService"
+        android:protectionLevel="signature" />
+
     <!--
         Allows the holder to start the permission usage screen for an app.
         <p>Protection level: signature|installer
@@ -4404,12 +4414,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. -->
@@ -4469,6 +4479,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"
@@ -4543,6 +4560,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/permission2/src/android/permission2/cts/RestrictedPermissionsTest.java b/tests/tests/permission2/src/android/permission2/cts/RestrictedPermissionsTest.java
index e6b4256..e20b2af 100644
--- a/tests/tests/permission2/src/android/permission2/cts/RestrictedPermissionsTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/RestrictedPermissionsTest.java
@@ -16,6 +16,8 @@
 
 package android.permission2.cts;
 
+import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
 import static android.Manifest.permission.READ_SMS;
 import static android.permission.cts.PermissionUtils.eventually;
 import static android.permission.cts.PermissionUtils.isGranted;
@@ -59,6 +61,7 @@
 import java.io.FileInputStream;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
@@ -221,28 +224,32 @@
     @Test
     @AppModeFull
     public void testLocationBackgroundPermissionWhitelistedAtInstall29() throws Exception {
-        installApp(APK_USES_LOCATION_29, null, null);
+        installApp(APK_USES_LOCATION_29, null, new ArraySet<>(Arrays.asList(ACCESS_FINE_LOCATION,
+                ACCESS_BACKGROUND_LOCATION)));
         assertAllRestrictedPermissionWhitelisted();
     }
 
     @Test
     @AppModeFull
     public void testLocationBackgroundPermissionNotWhitelistedAtInstall29() throws Exception {
-        installApp(APK_USES_LOCATION_29, Collections.EMPTY_SET, null);
+        installApp(APK_USES_LOCATION_29, Collections.emptySet(),
+                Collections.singleton(ACCESS_FINE_LOCATION));
         assertNoRestrictedPermissionWhitelisted();
     }
 
     @Test
     @AppModeFull
     public void testLocationBackgroundPermissionWhitelistedAtInstall22() throws Exception {
-        installApp(APK_USES_LOCATION_22, null, null);
+        installApp(APK_USES_LOCATION_22, null, new ArraySet<>(Arrays.asList(ACCESS_FINE_LOCATION,
+                ACCESS_BACKGROUND_LOCATION)));
         assertAllRestrictedPermissionWhitelisted();
     }
 
     @Test
     @AppModeFull
     public void testLocationBackgroundPermissionNotWhitelistedAtInstall22() throws Exception {
-        installApp(APK_USES_LOCATION_22, Collections.EMPTY_SET, null);
+        installApp(APK_USES_LOCATION_22, Collections.emptySet(),
+                Collections.singleton(ACCESS_FINE_LOCATION));
         assertNoRestrictedPermissionWhitelisted();
     }
 
@@ -914,20 +921,15 @@
 
     private @NonNull Set<String> getPermissionsOfAppWithAnyOfFlags(int flags) throws Exception {
         final PackageManager packageManager = getContext().getPackageManager();
-
-        final PackageInfo packageInfo = packageManager.getPackageInfo(PKG,
-                PackageManager.GET_PERMISSIONS);
-
-        final Set<String> hardRestrictedPermissions = new ArraySet<>();
-        for (String permission : packageInfo.requestedPermissions) {
+        final Set<String> restrictedPermissions = new ArraySet<>();
+        for (String permission : getRequestedPermissionsOfApp()) {
             PermissionInfo permInfo = packageManager.getPermissionInfo(permission, 0);
 
             if ((permInfo.flags & flags) != 0) {
-                hardRestrictedPermissions.add(permission);
+                restrictedPermissions.add(permission);
             }
         }
-
-        return hardRestrictedPermissions;
+        return restrictedPermissions;
     }
 
     private @NonNull Set<String> getRestrictedPermissionsOfApp() throws Exception {
@@ -935,6 +937,13 @@
                 PermissionInfo.FLAG_HARD_RESTRICTED | PermissionInfo.FLAG_SOFT_RESTRICTED);
     }
 
+    private @NonNull String[] getRequestedPermissionsOfApp() throws Exception {
+        final PackageManager packageManager = getContext().getPackageManager();
+        final PackageInfo packageInfo = packageManager.getPackageInfo(PKG,
+                PackageManager.GET_PERMISSIONS);
+        return packageInfo.requestedPermissions;
+    }
+
     private void assertAllRestrictedPermissionWhitelisted() throws Exception {
         assertRestrictedPermissionWhitelisted(getRestrictedPermissionsOfApp());
     }
@@ -999,7 +1008,7 @@
                             possibleModes.add(AppOpsManager.MODE_IGNORED);
                         }
                     } else {
-                        possibleModes.add(AppOpsManager.MODE_DEFAULT);
+                        possibleModes.add(AppOpsManager.MODE_IGNORED);
                     }
                 }
 
@@ -1120,6 +1129,8 @@
             for (String permission : adjustedGrantedPermissions) {
                 packageManager.grantRuntimePermission(PKG, permission,
                         getContext().getUser());
+                packageManager.updatePermissionFlags(permission, PKG,
+                        PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, 0, getContext().getUser());
             }
         });
 
@@ -1127,7 +1138,7 @@
         // applied until reviewed
         runWithShellPermissionIdentity(() -> {
             final PackageManager packageManager = getContext().getPackageManager();
-            for (String permission : getRestrictedPermissionsOfApp()) {
+            for (String permission : getRequestedPermissionsOfApp()) {
                 packageManager.updatePermissionFlags(permission, PKG,
                         PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED, 0,
                         getContext().getUser());
diff --git a/tests/tests/permission2/src/android/permission2/cts/RuntimePermissionProperties.kt b/tests/tests/permission2/src/android/permission2/cts/RuntimePermissionProperties.kt
new file mode 100644
index 0000000..e84e3af
--- /dev/null
+++ b/tests/tests/permission2/src/android/permission2/cts/RuntimePermissionProperties.kt
@@ -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.permission2.cts
+
+import android.Manifest.permission.ACCEPT_HANDOVER
+import android.Manifest.permission.ACCESS_COARSE_LOCATION
+import android.Manifest.permission.ACCESS_FINE_LOCATION
+import android.Manifest.permission.ACTIVITY_RECOGNITION
+import android.Manifest.permission.ADD_VOICEMAIL
+import android.Manifest.permission.ANSWER_PHONE_CALLS
+import android.Manifest.permission.BODY_SENSORS
+import android.Manifest.permission.CALL_PHONE
+import android.Manifest.permission.CAMERA
+import android.Manifest.permission.GET_ACCOUNTS
+import android.Manifest.permission.PROCESS_OUTGOING_CALLS
+import android.Manifest.permission.READ_CALENDAR
+import android.Manifest.permission.READ_CALL_LOG
+import android.Manifest.permission.READ_CELL_BROADCASTS
+import android.Manifest.permission.READ_CONTACTS
+import android.Manifest.permission.READ_EXTERNAL_STORAGE
+import android.Manifest.permission.READ_PHONE_NUMBERS
+import android.Manifest.permission.READ_PHONE_STATE
+import android.Manifest.permission.READ_SMS
+import android.Manifest.permission.RECEIVE_MMS
+import android.Manifest.permission.RECEIVE_SMS
+import android.Manifest.permission.RECEIVE_WAP_PUSH
+import android.Manifest.permission.RECORD_AUDIO
+import android.Manifest.permission.SEND_SMS
+import android.Manifest.permission.USE_SIP
+import android.Manifest.permission.WRITE_CALENDAR
+import android.Manifest.permission.WRITE_CALL_LOG
+import android.Manifest.permission.WRITE_CONTACTS
+import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
+import android.Manifest.permission_group.UNDEFINED
+import android.app.AppOpsManager.permissionToOp
+import android.content.pm.PackageManager.GET_PERMISSIONS
+import android.content.pm.PermissionInfo.PROTECTION_DANGEROUS
+import android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP
+import android.os.Build
+import android.permission.PermissionManager
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.runner.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class RuntimePermissionProperties {
+    private val context = InstrumentationRegistry.getInstrumentation().getTargetContext()
+    private val pm = context.packageManager
+
+    private val platformPkg = pm.getPackageInfo("android", GET_PERMISSIONS)
+    private val platformRuntimePerms = platformPkg.permissions
+            .filter { it.protection == PROTECTION_DANGEROUS }
+    private val platformBgPermNames = platformRuntimePerms.mapNotNull { it.backgroundPermission }
+
+    @Test
+    fun allRuntimeForegroundPermissionNeedAnAppOp() {
+        val platformFgPerms =
+            platformRuntimePerms.filter { !platformBgPermNames.contains(it.name) }
+
+        for (perm in platformFgPerms) {
+            assertThat(permissionToOp(perm.name)).named("AppOp for ${perm.name}").isNotNull()
+        }
+    }
+
+    @Test
+    fun groupOfRuntimePermissionsShouldBeUnknown() {
+        for (perm in platformRuntimePerms) {
+            assertThat(perm.group).named("Group of ${perm.name}").isEqualTo(UNDEFINED)
+        }
+    }
+
+    @Test
+    fun allAppOpPermissionNeedAnAppOp() {
+        val platformAppOpPerms = platformPkg.permissions
+                .filter { (it.protectionFlags and PROTECTION_FLAG_APPOP) != 0 }
+
+        for (perm in platformAppOpPerms) {
+            assertThat(permissionToOp(perm.name)).named("AppOp for ${perm.name}").isNotNull()
+        }
+    }
+
+    /**
+     * The permission of a background permission is the one of its foreground permission
+     */
+    @Test
+    fun allRuntimeBackgroundPermissionCantHaveAnAppOp() {
+        val platformBgPerms =
+            platformRuntimePerms.filter { platformBgPermNames.contains(it.name) }
+
+        for (perm in platformBgPerms) {
+            assertThat(permissionToOp(perm.name)).named("AppOp for ${perm.name}").isNull()
+        }
+    }
+
+    /**
+     * Commonly a new runtime permission is created by splitting an old one into twice
+     */
+    @Test
+    fun runtimePermissionsShouldHaveBeenSplitFromPreviousPermission() {
+        // Runtime permissions in Android P
+        val expectedPerms = mutableSetOf(READ_CONTACTS, WRITE_CONTACTS, GET_ACCOUNTS, READ_CALENDAR,
+            WRITE_CALENDAR, SEND_SMS, RECEIVE_SMS, READ_SMS, RECEIVE_MMS, RECEIVE_WAP_PUSH,
+            READ_CELL_BROADCASTS, READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE,
+            ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION, READ_CALL_LOG, WRITE_CALL_LOG,
+            PROCESS_OUTGOING_CALLS, READ_PHONE_STATE, READ_PHONE_NUMBERS, CALL_PHONE,
+            ADD_VOICEMAIL, USE_SIP, ANSWER_PHONE_CALLS, ACCEPT_HANDOVER, RECORD_AUDIO, CAMERA,
+            BODY_SENSORS)
+
+        // Add permission split since P
+        for (sdkVersion in Build.VERSION_CODES.P + 1..Build.VERSION_CODES.CUR_DEVELOPMENT + 1) {
+            for (splitPerm in
+                context.getSystemService(PermissionManager::class.java)!!.splitPermissions) {
+                if (splitPerm.targetSdk == sdkVersion &&
+                    expectedPerms.contains(splitPerm.splitPermission)) {
+                    expectedPerms.addAll(splitPerm.newPermissions)
+                }
+            }
+        }
+
+        // Add runtime permission added in Q which were _not_ split from a previously existing
+        // runtime permission
+        expectedPerms.add(ACTIVITY_RECOGNITION)
+
+        assertThat(expectedPerms).containsExactlyElementsIn(platformRuntimePerms.map { it.name })
+    }
+}
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
index 908807a..f45aed1 100644
--- a/tests/tests/print/Android.mk
+++ b/tests/tests/print/Android.mk
@@ -1,15 +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.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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/Android.bp b/tests/tests/provider/Android.bp
index 489025e..0828f57 100644
--- a/tests/tests/provider/Android.bp
+++ b/tests/tests/provider/Android.bp
@@ -35,6 +35,7 @@
 
     srcs: ["src/**/*.java"],
 
-    sdk_version: "test_current",
+    // uncomment when b/140885436 is fixed
+    // sdk_version: "test_current",
     min_sdk_version: "21",
 }
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 88%
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..4d834b4 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,19 @@
 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.Bundle;
 import android.os.Environment;
-import android.platform.test.annotations.Presubmit;
 import android.provider.MediaStore;
+import android.provider.MediaStore.Audio;
 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 +50,6 @@
 
 import java.io.File;
 
-@Presubmit
 @RunWith(Parameterized.class)
 public class MediaStore_Audio_MediaTest {
     private Context mContext;
@@ -78,6 +81,9 @@
                 Media.getContentUri(mVolumeName), null, null,
                     null, null));
         c.close();
+
+        assertEquals(ContentUris.withAppendedId(Media.getContentUri(mVolumeName), 42),
+                Media.getContentUri(mVolumeName, 42));
     }
 
     @Test
@@ -200,4 +206,22 @@
                 ProviderTestUtils.stageFile(R.raw.testmp3, new File(dir, "d.mp3")));
         assertEquals(d, mContentResolver.uncanonicalize(canonicalized));
     }
+
+    @Test
+    public void testSortLocale() {
+        final Bundle queryArgs = new Bundle();
+        queryArgs.putStringArray(ContentResolver.QUERY_ARG_SORT_COLUMNS,
+                new String[] { Audio.Media.TITLE });
+
+        for (String locale : new String[] {
+                "zh",
+                "zh@collation=pinyin",
+                "zh@collation=stroke",
+                "zh@collation=zhuyin",
+        }) {
+            queryArgs.putString(ContentResolver.QUERY_ARG_SORT_LOCALE, locale);
+            try (Cursor c = mContentResolver.query(mExternalAudio, null, queryArgs, null)) {
+            }
+        }
+    }
 }
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 799e3ac..223a89f 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;
@@ -27,6 +27,7 @@
 
 import android.app.AppOpsManager;
 import android.content.ContentResolver;
+import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -38,19 +39,18 @@
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 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;
@@ -65,7 +65,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";
@@ -203,7 +202,6 @@
         assertEquals(src.getHeight(), result.getHeight());
     }
 
-    @Presubmit
     @Test
     public void testGetContentUri() {
         Cursor c = null;
@@ -213,6 +211,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 92ce7e0..915d5fa 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;
@@ -29,28 +29,28 @@
 
 import android.app.AppOpsManager;
 import android.content.ContentResolver;
+import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.pm.PackageManager;
 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.Process;
 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;
@@ -66,7 +66,6 @@
 import java.io.OutputStream;
 import java.util.Arrays;
 
-@Presubmit
 @RunWith(Parameterized.class)
 public class MediaStore_Video_MediaTest {
     private Context mContext;
@@ -100,6 +99,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) {
@@ -109,18 +111,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");
@@ -169,7 +167,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)));
@@ -224,7 +222,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();
         }
@@ -240,8 +238,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"));
@@ -284,8 +284,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"));
@@ -310,7 +312,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 d835fcd..feda0fd 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 a92fbfa..7c85540 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 857e578..1520360 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..b87b36b
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/PackageInstallerTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.AppModeFull;
+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
+@AppModeFull
+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/PackageSignatureTest.java b/tests/tests/security/src/android/security/cts/PackageSignatureTest.java
index 1f4eba1..6280ce8 100644
--- a/tests/tests/security/src/android/security/cts/PackageSignatureTest.java
+++ b/tests/tests/security/src/android/security/cts/PackageSignatureTest.java
@@ -133,6 +133,8 @@
             // Test package to verify upgrades to privileged applications
             "com.android.cts.priv.ctsshim",
             "com.android.cts.ctsshim",
+            // Test APEX used in CTS tests.
+            "com.android.apex.cts.shim,",
 
             // Oom Catcher package to prevent tests from ooming device.
             "com.android.cts.oomcatcher"
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/slice/Android.bp b/tests/tests/slice/Android.bp
index bc25d6c..d3b8083 100644
--- a/tests/tests/slice/Android.bp
+++ b/tests/tests/slice/Android.bp
@@ -18,6 +18,7 @@
     // Tag this module as a cts test artifact
     test_suites: [
         "cts",
+        "sts",
         "vts",
         "general-tests",
     ],
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/telecom/src/android/telecom/cts/CallDetailsTest.java b/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java
index 7ca476c..3a70017 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java
@@ -22,10 +22,12 @@
 import static org.junit.Assert.assertThat;
 
 import android.content.Context;
+import android.database.Cursor;
 import android.graphics.drawable.Icon;
 import android.media.AudioManager;
 import android.os.Bundle;
 import android.net.Uri;
+import android.provider.CallLog;
 import android.telecom.Call;
 import android.telecom.Connection;
 import android.telecom.ConnectionRequest;
@@ -38,6 +40,8 @@
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Suites of tests that verifies the various Call details.
@@ -72,6 +76,7 @@
     public static final int TEST_EXTRA_VALUE = 10;
     public static final String TEST_EVENT = "com.test.event.TEST";
     public static final Uri TEST_DEFLECT_URI = Uri.fromParts("tel", "+16505551212", null);
+    private static final int ASYNC_TIMEOUT = 10000;
     private StatusHints mStatusHints;
     private Bundle mExtras = new Bundle();
 
@@ -752,4 +757,29 @@
                 "Call should have extras key " + expectedKey
         );
     }
+
+    /**
+     * Tests whether the CallLogManager logs the features of a call(HD call and Wifi call)
+     * correctly.
+     */
+    public void testLogHdAndWifi() throws Exception {
+        if (!mShouldTestTelecom) {
+            return;
+        }
+
+        // Register content observer on call log and get latch
+        CountDownLatch callLogEntryLatch = getCallLogEntryLatch();
+        mCall.disconnect();
+
+        // Wait on the call log latch.
+        callLogEntryLatch.await(ASYNC_TIMEOUT, TimeUnit.MILLISECONDS);
+
+        // Verify the contents of the call log
+        Cursor callsCursor = mContext.getContentResolver().query(CallLog.Calls.CONTENT_URI, null,
+                "features", null, null);
+        int features = callsCursor.getColumnIndex(CallLog.Calls.FEATURES);
+        assertEquals(CallLog.Calls.FEATURES_HD_CALL,
+                features & CallLog.Calls.FEATURES_HD_CALL);
+        assertEquals(CallLog.Calls.FEATURES_WIFI, features & CallLog.Calls.FEATURES_WIFI);
+    }
 }
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/MmsTest.java b/tests/tests/telephony/current/src/android/telephony/cts/MmsTest.java
index 7ca2334..60b0de2 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/MmsTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/MmsTest.java
@@ -49,14 +49,14 @@
 import com.google.android.mms.pdu.SendConf;
 import com.google.android.mms.pdu.SendReq;
 
+import org.junit.Before;
+import org.junit.Test;
+
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.Random;
 
-import org.junit.Before;
-import org.junit.Test;
-
 /**
  * Test sending MMS using {@link android.telephony.SmsManager}.
  */
@@ -185,7 +185,7 @@
         Log.i(TAG, "testSendMmsMessage");
         // Prime the MmsService so that MMS config is loaded
         final SmsManager smsManager = SmsManager.getDefault();
-        smsManager.getAutoPersisting();
+        smsManager.getCarrierConfigValues();
         // MMS config is loaded asynchronously. Wait a bit so it will be loaded.
         try {
             Thread.sleep(1000);
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java
index ca4b8e2..cddcba1 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java
@@ -30,6 +30,8 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
+import android.telephony.Annotation.RadioPowerState;
+import android.telephony.Annotation.SimActivationState;
 import android.telephony.CellInfo;
 import android.telephony.CellLocation;
 import android.telephony.PhoneStateListener;
@@ -84,8 +86,8 @@
     private boolean mOnRadioPowerStateChangedCalled;
     private boolean mVoiceActivationStateChangedCalled;
     private boolean mSrvccStateChangedCalled;
-    @TelephonyManager.RadioPowerState private int mRadioPowerState;
-    @TelephonyManager.SimActivationState private int mVoiceActivationState;
+    @RadioPowerState private int mRadioPowerState;
+    @SimActivationState private int mVoiceActivationState;
     private PreciseDataConnectionState mPreciseDataConnectionState;
     private PreciseCallState mPreciseCallState;
     private SignalStrength mSignalStrength;
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 51c1563..febb89d 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/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
index 850c8a0..d736119 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -29,10 +29,13 @@
 import android.Manifest.permission;
 import android.app.UiAutomation;
 import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
 import android.net.wifi.WifiInfo;
@@ -41,14 +44,17 @@
 import android.os.Build;
 import android.os.IBinder;
 import android.os.Looper;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telephony.AccessNetworkConstants;
+import android.telephony.Annotation.RadioPowerState;
 import android.telephony.AvailableNetworkInfo;
 import android.telephony.CallAttributes;
 import android.telephony.CallQuality;
+import android.telephony.CarrierConfigManager;
 import android.telephony.CellLocation;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PhoneStateListener;
@@ -61,6 +67,7 @@
 import android.telephony.UiccSlotInfo;
 import android.telephony.cts.locationaccessingapp.CtsLocationAccessService;
 import android.telephony.cts.locationaccessingapp.ICtsLocationAccessControl;
+import android.telephony.data.ApnSetting;
 import android.telephony.emergency.EmergencyNumber;
 import android.text.TextUtils;
 import android.util.Log;
@@ -70,11 +77,14 @@
 
 import com.android.compatibility.common.util.ShellIdentityUtils;
 import com.android.compatibility.common.util.TestThread;
+import com.android.internal.telephony.uicc.IccUtils;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
@@ -83,12 +93,14 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
+
 /**
  * Build, install and run the tests by running the commands below:
  *  make cts -j64
@@ -104,6 +116,11 @@
     private boolean mHasRadioPowerOff = false;
     private ServiceState mServiceState;
     private final Object mLock = new Object();
+
+    private CarrierConfigManager mCarrierConfigManager;
+    private String mSelfPackageName;
+    private String mSelfCertHash;
+
     private static final int TOLERANCE = 1000;
     private PhoneStateListener mListener;
     private static ConnectivityManager mCm;
@@ -138,6 +155,13 @@
 
     private static final int EMERGENCY_NUMBER_SOURCE_RIL_ECCLIST = 0;
     private static final Set<Integer> EMERGENCY_NUMBER_SOURCE_SET;
+
+    private static final String PLMN_A = "123456";
+    private static final String PLMN_B = "78901";
+    private static final List<String> FPLMN_TEST = Arrays.asList(PLMN_A, PLMN_B);
+    private static final int MAX_FPLMN_NUM = 100;
+    private static final int MIN_FPLMN_NUM = 3;
+
     static {
         EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>();
         EMERGENCY_NUMBER_SOURCE_SET.add(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING);
@@ -160,14 +184,52 @@
         EMERGENCY_SERVICE_CATEGORY_SET.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC);
     }
 
+    private int mTestSub;
+    private TelephonyManagerTest.CarrierConfigReceiver mReceiver;
+
+    private static class CarrierConfigReceiver extends BroadcastReceiver {
+        private CountDownLatch mLatch = new CountDownLatch(1);
+        private final int mSubId;
+
+        CarrierConfigReceiver(int subId) {
+            mSubId = subId;
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
+                int subId = intent.getIntExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
+                        SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+                if (mSubId == subId) {
+                    mLatch.countDown();
+                }
+            }
+        }
+
+        void clearQueue() {
+            mLatch = new CountDownLatch(1);
+        }
+
+        void waitForCarrierConfigChanged() throws Exception {
+            mLatch.await(5000, TimeUnit.MILLISECONDS);
+        }
+    }
+
     @Before
     public void setUp() throws Exception {
-        mTelephonyManager =
-                (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);
-        mCm = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
-        mSubscriptionManager = (SubscriptionManager) getContext()
-                .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+        mCm = getContext().getSystemService(ConnectivityManager.class);
+        mSubscriptionManager = getContext().getSystemService(SubscriptionManager.class);
         mPackageManager = getContext().getPackageManager();
+        mCarrierConfigManager = getContext().getSystemService(CarrierConfigManager.class);
+        mSelfPackageName = getContext().getPackageName();
+        mSelfCertHash = getCertHash(mSelfPackageName);
+        mTestSub = SubscriptionManager.getDefaultSubscriptionId();
+        mTelephonyManager = getContext().getSystemService(TelephonyManager.class)
+                .createForSubscriptionId(mTestSub);
+        mReceiver = new CarrierConfigReceiver(mTestSub);
+        IntentFilter filter = new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+        // ACTION_CARRIER_CONFIG_CHANGED is sticky, so we will get a callback right away.
+        getContext().registerReceiver(mReceiver, filter);
     }
 
     @After
@@ -176,6 +238,76 @@
             // unregister the listener
             mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_NONE);
         }
+        if (mReceiver != null) {
+            getContext().unregisterReceiver(mReceiver);
+            mReceiver = null;
+        }
+    }
+
+    private String getCertHash(String pkgName) throws Exception {
+        try {
+            PackageInfo pInfo = mPackageManager.getPackageInfo(pkgName,
+                    PackageManager.GET_SIGNATURES
+                            | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS);
+            MessageDigest md = MessageDigest.getInstance("SHA-1");
+            return IccUtils.bytesToHexString(md.digest(pInfo.signatures[0].toByteArray()));
+        } catch (PackageManager.NameNotFoundException ex) {
+            Log.e(TAG, pkgName + " not found", ex);
+            throw ex;
+        } catch (NoSuchAlgorithmException ex) {
+            Log.e(TAG, "Algorithm SHA1 is not found.");
+            throw ex;
+        }
+    }
+
+    /** Checks whether the cellular stack should be running on this device. */
+    private boolean hasCellular() {
+        return mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
+                && mTelephonyManager.getPhoneCount() > 0;
+    }
+
+    @Test
+    public void testHasCarrierPrivilegesViaCarrierConfigs() throws Exception {
+        if (!hasCellular()) return;
+        PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mTestSub);
+
+        try {
+            assertNotNull("CarrierConfigManager#getConfigForSubId() returned null",
+                    carrierConfig);
+            assertFalse("CarrierConfigManager#getConfigForSubId() returned empty bundle",
+                    carrierConfig.isEmpty());
+
+            // purge the certs in carrierConfigs first
+            carrierConfig.putStringArray(
+                    CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY, new String[]{});
+            overrideCarrierConfig(carrierConfig);
+            // verify we don't have privilege through carrierConfigs or Uicc
+            assertFalse(mTelephonyManager.hasCarrierPrivileges());
+
+            carrierConfig.putStringArray(
+                    CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY,
+                    new String[]{mSelfCertHash});
+
+            // verify we now have privilege after adding certificate to carrierConfigs
+            overrideCarrierConfig(carrierConfig);
+            assertTrue(mTelephonyManager.hasCarrierPrivileges());
+        } finally {
+            // purge the newly added certificate
+            carrierConfig.putStringArray(
+                    CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY, new String[]{});
+            // carrierConfig.remove(CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY);
+            overrideCarrierConfig(carrierConfig);
+
+            // verify we no longer have privilege after removing certificate
+            assertFalse(mTelephonyManager.hasCarrierPrivileges());
+        }
+    }
+
+    private void overrideCarrierConfig(PersistableBundle bundle) throws Exception {
+        mReceiver.clearQueue();
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCarrierConfigManager,
+                (cm) -> cm.overrideConfig(mTestSub, bundle));
+        mReceiver.waitForCarrierConfigChanged();
     }
 
     public static void grantLocationPermissions() {
@@ -235,7 +367,7 @@
             mLock.wait(TOLERANCE);
 
             assertTrue("Test register, mOnCellLocationChangedCalled should be true.",
-                mOnCellLocationChangedCalled);
+                    mOnCellLocationChangedCalled);
         }
 
         synchronized (mLock) {
@@ -244,7 +376,7 @@
             mLock.wait(TOLERANCE);
 
             assertTrue("Test register, mOnCellLocationChangedCalled should be true.",
-                mOnCellLocationChangedCalled);
+                    mOnCellLocationChangedCalled);
         }
 
         // unregister the listener
@@ -260,7 +392,7 @@
             mLock.wait(TOLERANCE);
 
             assertFalse("Test unregister, mOnCellLocationChangedCalled should be false.",
-                mOnCellLocationChangedCalled);
+                    mOnCellLocationChangedCalled);
         }
     }
 
@@ -325,9 +457,8 @@
         mTelephonyManager.getPhoneCount();
         mTelephonyManager.getDataEnabled();
         mTelephonyManager.getNetworkSpecifier();
-        mTelephonyManager.getNai();
-        TelecomManager telecomManager = (TelecomManager) getContext()
-                .getSystemService(Context.TELECOM_SERVICE);
+        ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, (tm) -> tm.getNai());
+        TelecomManager telecomManager = getContext().getSystemService(TelecomManager.class);
         PhoneAccountHandle defaultAccount = telecomManager
                 .getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL);
         mTelephonyManager.getVoicemailRingtoneUri(defaultAccount);
@@ -375,9 +506,9 @@
 
     @Test
     public void testServiceStateListeningWithoutPermissions() {
-            if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) return;
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) return;
 
-            withRevokedPermission(() -> {
+        withRevokedPermission(() -> {
                     ServiceState ss = (ServiceState) performLocationAccessCommand(
                             CtsLocationAccessService.COMMAND_GET_SERVICE_STATE_FROM_LISTENER);
                     assertServiceStateSanitization(ss, true);
@@ -661,9 +792,9 @@
         String esnPattern = "[0-9a-fA-F]{8}";
         String invalidPattern = "[0]{8}";
         assertTrue("ESN hex device id " + deviceId + " does not match pattern " + esnPattern,
-                   Pattern.matches(esnPattern, deviceId));
+                Pattern.matches(esnPattern, deviceId));
         assertFalse("ESN hex device id " + deviceId + " must not be a pseudo-ESN",
-                    "80".equals(deviceId.substring(0, 2)));
+                "80".equals(deviceId.substring(0, 2)));
         assertFalse("ESN hex device id " + deviceId + "must not be a zero sequence",
                 Pattern.matches(invalidPattern, deviceId));
     }
@@ -680,7 +811,7 @@
 
     private void assertSerialNumber() {
         String serial = ShellIdentityUtils.invokeStaticMethodWithShellPermissions(
-               Build::getSerial);
+                Build::getSerial);
         assertNotNull("Non-telephony devices must have a Build.getSerial() number.",
                 serial);
         assertTrue("Hardware id must be no longer than 20 characters.",
@@ -697,8 +828,7 @@
 
     /** @return mac address which requires the WiFi system to be enabled */
     private String getWifiMacAddress() {
-        WifiManager wifiManager = (WifiManager) getContext()
-                .getSystemService(Context.WIFI_SERVICE);
+        WifiManager wifiManager = getContext().getSystemService(WifiManager.class);
 
         boolean enabled = wifiManager.isWifiEnabled();
 
@@ -730,11 +860,19 @@
 
     @Test
     public void testGetNetworkCountryIso() {
-        String countryCode = mTelephonyManager.getNetworkCountryIso();
         if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            String countryCode = mTelephonyManager.getNetworkCountryIso();
             assertTrue("Country code '" + countryCode + "' did not match "
-                    + ISO_COUNTRY_CODE_PATTERN,
+                            + ISO_COUNTRY_CODE_PATTERN,
                     Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode));
+
+            for (int i = 0; i < mTelephonyManager.getPhoneCount(); i++) {
+                countryCode = mTelephonyManager.getNetworkCountryIso(i);
+
+                assertTrue("Country code '" + countryCode + "' did not match "
+                                + ISO_COUNTRY_CODE_PATTERN + " for slot " + i,
+                        Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode));
+            }
         } else {
             // Non-telephony may still have the property defined if it has a SIM.
         }
@@ -745,7 +883,7 @@
         String countryCode = mTelephonyManager.getSimCountryIso();
         if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
             assertTrue("Country code '" + countryCode + "' did not match "
-                    + ISO_COUNTRY_CODE_PATTERN,
+                            + ISO_COUNTRY_CODE_PATTERN,
                     Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode));
         } else {
             // Non-telephony may still have the property defined if it has a SIM.
@@ -935,7 +1073,7 @@
                 mListener = new PhoneStateListener() {
                     @Override
                     public void onRadioPowerStateChanged(
-                            @TelephonyManager.RadioPowerState int state) {
+                            @RadioPowerState int state) {
                         synchronized (mLock) {
                             if (state == TelephonyManager.RADIO_POWER_ON && mHasRadioPowerOff) {
                                 mRadioRebootTriggered = true;
@@ -1089,7 +1227,7 @@
     /**
      * Basic test to ensure {@link NetworkRegistrationInfo#getAccessNetworkTechnology()} not
      * throw any exception and returns valid result
-     * @see TelephonyManager.NetworkType
+     * @see android.telephony.Annotation.NetworkType
      */
     @Test
     public void testNetworkRegistationStateGetAccessNetworkTechnology() {
@@ -1136,8 +1274,7 @@
             return;
         }
 
-        SubscriptionManager sm = (SubscriptionManager) getContext()
-                .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+        SubscriptionManager sm = getContext().getSystemService(SubscriptionManager.class);
         List<SubscriptionInfo> subInfos = sm.getActiveSubscriptionInfoList();
 
         if (subInfos != null) {
@@ -1215,6 +1352,110 @@
     }
 
     /**
+     * Tests that the device properly sets and pads the contents of EF_FPLMN
+     */
+    @Test
+    public void testSetForbiddenPlmns() {
+        if (!supportSetFplmn()) {
+            return;
+        }
+        String[] originalFplmns = mTelephonyManager.getForbiddenPlmns();
+        try {
+            int numFplmnsSet = mTelephonyManager.setForbiddenPlmns(FPLMN_TEST);
+            String[] writtenFplmns = mTelephonyManager.getForbiddenPlmns();
+            assertEquals("Wrong return value for setFplmns with less than required fplmns: "
+                    + numFplmnsSet, FPLMN_TEST.size(), numFplmnsSet);
+            assertEquals("Wrong Fplmns content written", FPLMN_TEST, Arrays.asList(writtenFplmns));
+        } finally {
+            // Restore
+            mTelephonyManager.setForbiddenPlmns(Arrays.asList(originalFplmns));
+        }
+    }
+
+    /**
+     * Tests that the device properly truncates the contents of EF_FPLMN when provided size
+     * is too big.
+     */
+    @Test
+    public void testSetForbiddenPlmnsTruncate() {
+        if (!supportSetFplmn()) {
+            return;
+        }
+        String[] originalFplmns = mTelephonyManager.getForbiddenPlmns();
+        try {
+            List<String> targetFplmns = new ArrayList<>();
+            for (int i = 0; i < MIN_FPLMN_NUM; i++) {
+                targetFplmns.add(PLMN_A);
+            }
+            for (int i = MIN_FPLMN_NUM; i < MAX_FPLMN_NUM; i++) {
+                targetFplmns.add(PLMN_B);
+            }
+            int numFplmnsSet = mTelephonyManager.setForbiddenPlmns(targetFplmns);
+            String[] writtenFplmns = mTelephonyManager.getForbiddenPlmns();
+            assertTrue("Wrong return value for setFplmns with overflowing fplmns: " + numFplmnsSet,
+                    numFplmnsSet < MAX_FPLMN_NUM);
+            assertEquals("Number of Fplmns set does not equal number of Fplmns available",
+                    numFplmnsSet, writtenFplmns.length);
+            assertEquals("Wrong Fplmns content written", targetFplmns.subList(0, numFplmnsSet),
+                    Arrays.asList(writtenFplmns));
+        } finally {
+            // Restore
+            mTelephonyManager.setForbiddenPlmns(Arrays.asList(originalFplmns));
+        }
+    }
+
+    /**
+     * Tests that the device properly deletes the contents of EF_FPLMN
+     */
+    @Test
+    public void testSetForbiddenPlmnsDelete() {
+        if (!supportSetFplmn()) {
+            return;
+        }
+        String[] originalFplmns = mTelephonyManager.getForbiddenPlmns();
+        try {
+            // Support test for empty SIM
+            List<String> targetDummyFplmns = new ArrayList<>();
+            for (int i = 0; i < MIN_FPLMN_NUM; i++) {
+                targetDummyFplmns.add(PLMN_A);
+            }
+            mTelephonyManager.setForbiddenPlmns(targetDummyFplmns);
+            String[] writtenDummyFplmns = mTelephonyManager.getForbiddenPlmns();
+            assertEquals(targetDummyFplmns, Arrays.asList(writtenDummyFplmns));
+
+            List<String> targetFplmns = new ArrayList<>();
+            int numFplmnsSet = mTelephonyManager.setForbiddenPlmns(targetFplmns);
+            String[] writtenFplmns = mTelephonyManager.getForbiddenPlmns();
+            assertEquals("Wrong return value for setFplmns with empty list", 0, numFplmnsSet);
+            assertEquals("Wrong number of Fplmns written", 0, writtenFplmns.length);
+            // TODO wait for 10 minutes or so for the FPLMNS list to grow back
+        } finally {
+            // Restore
+            mTelephonyManager.setForbiddenPlmns(Arrays.asList(originalFplmns));
+        }
+    }
+
+
+    /**
+     * Tests that setForbiddenPlmns properly handles null input
+     */
+    @Test
+    public void testSetForbiddenPlmnsVoid() {
+        if (!supportSetFplmn()) {
+            return;
+        }
+        String[] originalFplmns = mTelephonyManager.getForbiddenPlmns();
+        try {
+            mTelephonyManager.setForbiddenPlmns(null);
+            fail("Expected IllegalArgumentException. Null input is not allowed");
+        } catch (IllegalArgumentException expected) {
+        } finally {
+            // Restore
+            mTelephonyManager.setForbiddenPlmns(Arrays.asList(originalFplmns));
+        }
+    }
+
+    /**
      * Verify that TelephonyManager.getCardIdForDefaultEuicc returns a positive value or either
      * UNINITIALIZED_CARD_ID or UNSUPPORTED_CARD_ID.
      */
@@ -1223,8 +1464,8 @@
         int cardId = mTelephonyManager.getCardIdForDefaultEuicc();
         assertTrue("Card ID for default EUICC is not a valid value",
                 cardId == TelephonyManager.UNSUPPORTED_CARD_ID
-                || cardId == TelephonyManager.UNINITIALIZED_CARD_ID
-                || cardId >= 0);
+                        || cardId == TelephonyManager.UNINITIALIZED_CARD_ID
+                        || cardId >= 0);
     }
 
     /**
@@ -1345,8 +1586,8 @@
         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
             return;
         }
-        Map<Integer, List<EmergencyNumber>> emergencyNumberList
-          = mTelephonyManager.getEmergencyNumberList();
+        Map<Integer, List<EmergencyNumber>> emergencyNumberList =
+                mTelephonyManager.getEmergencyNumberList();
 
         assertFalse(emergencyNumberList == null);
 
@@ -1357,27 +1598,27 @@
         // 112 and 911 should always be available
         // Reference: 3gpp 22.101, Section 10 - Emergency Calls
         assertTrue(checkIfEmergencyNumberListHasSpecificAddress(
-            emergencyNumberList.get(defaultSubId), "911"));
+                emergencyNumberList.get(defaultSubId), "911"));
         assertTrue(checkIfEmergencyNumberListHasSpecificAddress(
-            emergencyNumberList.get(defaultSubId), "112"));
+                emergencyNumberList.get(defaultSubId), "112"));
 
         // 000, 08, 110, 118, 119, and 999 should be always available when sim is absent
         // Reference: 3gpp 22.101, Section 10 - Emergency Calls
         if (mTelephonyManager.getPhoneCount() > 0
                 && mSubscriptionManager.getSimStateForSlotIndex(0)
-                    == TelephonyManager.SIM_STATE_ABSENT) {
+                == TelephonyManager.SIM_STATE_ABSENT) {
             assertTrue(checkIfEmergencyNumberListHasSpecificAddress(
-                emergencyNumberList.get(defaultSubId), "000"));
+                    emergencyNumberList.get(defaultSubId), "000"));
             assertTrue(checkIfEmergencyNumberListHasSpecificAddress(
-                emergencyNumberList.get(defaultSubId), "08"));
+                    emergencyNumberList.get(defaultSubId), "08"));
             assertTrue(checkIfEmergencyNumberListHasSpecificAddress(
-                emergencyNumberList.get(defaultSubId), "110"));
+                    emergencyNumberList.get(defaultSubId), "110"));
             assertTrue(checkIfEmergencyNumberListHasSpecificAddress(
-                emergencyNumberList.get(defaultSubId), "118"));
+                    emergencyNumberList.get(defaultSubId), "118"));
             assertTrue(checkIfEmergencyNumberListHasSpecificAddress(
-                emergencyNumberList.get(defaultSubId), "119"));
+                    emergencyNumberList.get(defaultSubId), "119"));
             assertTrue(checkIfEmergencyNumberListHasSpecificAddress(
-                emergencyNumberList.get(defaultSubId), "999"));
+                    emergencyNumberList.get(defaultSubId), "999"));
         }
     }
 
@@ -1398,7 +1639,7 @@
         // Reference: 3gpp 22.101, Section 10 - Emergency Calls
         if (mTelephonyManager.getPhoneCount() > 0
                 && mSubscriptionManager.getSimStateForSlotIndex(0)
-                    == TelephonyManager.SIM_STATE_ABSENT) {
+                == TelephonyManager.SIM_STATE_ABSENT) {
             assertTrue(mTelephonyManager.isEmergencyNumber("000"));
             assertTrue(mTelephonyManager.isEmergencyNumber("08"));
             assertTrue(mTelephonyManager.isEmergencyNumber("110"));
@@ -1471,18 +1712,18 @@
             fail("This test requires two SIM cards.");
         }
         ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
-            (tm) -> tm.setPreferredOpportunisticDataSubscription(
-                SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false,
-                null, null));
+                (tm) -> tm.setPreferredOpportunisticDataSubscription(
+                        SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false,
+                        null, null));
         // wait for the data change to take effect
         waitForMs(500);
         int subId =
-            ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
-                (tm) -> tm.getPreferredOpportunisticDataSubscription());
+                ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+                        (tm) -> tm.getPreferredOpportunisticDataSubscription());
         assertThat(subId).isEqualTo(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
         List<SubscriptionInfo> subscriptionInfoList =
-                    ShellIdentityUtils.invokeMethodWithShellPermissions(mSubscriptionManager,
-                            (tm) -> tm.getOpportunisticSubscriptions());
+                ShellIdentityUtils.invokeMethodWithShellPermissions(mSubscriptionManager,
+                        (tm) -> tm.getOpportunisticSubscriptions());
         Consumer<Integer> callbackSuccess = TelephonyManagerTest::assertSetOpportunisticSubSuccess;
         Consumer<Integer> callbackFailure =
                 TelephonyManagerTest::assertSetOpportunisticInvalidParameter;
@@ -1504,7 +1745,7 @@
             // wait for the data change to take effect
             waitForMs(500);
             subId = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
-                (tm) -> tm.getPreferredOpportunisticDataSubscription());
+                    (tm) -> tm.getPreferredOpportunisticDataSubscription());
             assertThat(subId).isEqualTo(subscriptionInfoList.get(0).getSubscriptionId());
         }
 
@@ -1515,7 +1756,7 @@
         // wait for the data change to take effect
         waitForMs(500);
         subId = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
-            (tm) -> tm.getPreferredOpportunisticDataSubscription());
+                (tm) -> tm.getPreferredOpportunisticDataSubscription());
         assertThat(subId).isEqualTo(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
     }
 
@@ -1598,8 +1839,8 @@
         }
 
         List<SubscriptionInfo> subscriptionInfoList =
-            ShellIdentityUtils.invokeMethodWithShellPermissions(mSubscriptionManager,
-                (tm) -> tm.getOpportunisticSubscriptions());
+                ShellIdentityUtils.invokeMethodWithShellPermissions(mSubscriptionManager,
+                        (tm) -> tm.getOpportunisticSubscriptions());
         List<String> mccMncs = new ArrayList<String>();
         List<Integer> bands = new ArrayList<Integer>();
         List<AvailableNetworkInfo> availableNetworkInfos = new ArrayList<AvailableNetworkInfo>();
@@ -1609,7 +1850,7 @@
                 TelephonyManagerTest::assertUpdateAvailableNetworkInvalidArguments;
         if (subscriptionInfoList == null || subscriptionInfoList.size() == 0
                 || !mSubscriptionManager.isActiveSubscriptionId(
-                        subscriptionInfoList.get(0).getSubscriptionId())) {
+                subscriptionInfoList.get(0).getSubscriptionId())) {
             AvailableNetworkInfo availableNetworkInfo = new AvailableNetworkInfo(randomSubId,
                     AvailableNetworkInfo.PRIORITY_HIGH, mccMncs, bands);
             availableNetworkInfos.add(availableNetworkInfo);
@@ -1629,15 +1870,15 @@
                     AvailableNetworkInfo.PRIORITY_HIGH, mccMncs, bands);
             availableNetworkInfos.add(availableNetworkInfo);
             ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
-                (tm) -> tm.updateAvailableNetworks(availableNetworkInfos,
-                        AsyncTask.SERIAL_EXECUTOR, callbackSuccess));
+                    (tm) -> tm.updateAvailableNetworks(availableNetworkInfos,
+                            AsyncTask.SERIAL_EXECUTOR, callbackSuccess));
             // wait for the data change to take effect
             waitForMs(500);
             // clear all the operations at the end of test.
             availableNetworkInfos.clear();
             ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
-                (tm) -> tm.updateAvailableNetworks(availableNetworkInfos,
-                        AsyncTask.SERIAL_EXECUTOR, callbackSuccess));
+                    (tm) -> tm.updateAvailableNetworks(availableNetworkInfos,
+                            AsyncTask.SERIAL_EXECUTOR, callbackSuccess));
         }
     }
 
@@ -1732,6 +1973,28 @@
         }
     }
 
+    @Test
+    public void testIsDataEnabledForApn() {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            return;
+        }
+        // verify SecurityException
+        try {
+            mTelephonyManager.isDataEnabledForApn(ApnSetting.TYPE_MMS);
+            fail("testIsDataEnabledForApn: Expected SecurityException on isDataEnabledForApn");
+        } catch (SecurityException se) {
+            // expected
+        }
+
+        // test with permission
+        try {
+            ShellIdentityUtils.invokeMethodWithShellPermissions(
+                    mTelephonyManager, (tm) -> tm.isDataEnabledForApn(ApnSetting.TYPE_MMS));
+        } catch (SecurityException se) {
+            fail("testIsDataEnabledForApn: SecurityException not expected");
+        }
+    }
+
     /**
      * Validate Emergency Number address that only contains the dialable character.
      *
@@ -1862,8 +2125,8 @@
         boolean isInAnyCategory = false;
         for (int possibleCategoryValue = EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
                 possibleCategoryValue <= (EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE
-                         | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE
-                         | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE
+                        | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE
+                        | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE
                         | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD
                         | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE
                         | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC
@@ -1929,4 +2192,17 @@
             Log.d(TAG, "InterruptedException while waiting: " + e);
         }
     }
+
+    /**
+     * Verify that the phone is supporting the action of setForbiddenPlmn.
+     *
+     * @return whether to proceed the test
+     */
+    private boolean supportSetFplmn() {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            return false;
+        }
+        return mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM;
+    }
 }
+
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyRegistryManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyRegistryManagerTest.java
new file mode 100644
index 0000000..a6a2c4f
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyRegistryManagerTest.java
@@ -0,0 +1,35 @@
+package android.telephony.cts;
+
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.os.telephony.TelephonyRegistryManager;
+import androidx.test.InstrumentationRegistry;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test TelephonyRegistryManagerTest APIs.
+ */
+public class TelephonyRegistryManagerTest {
+    private TelephonyRegistryManager mTelephonyRegistryMgr;
+
+    @Before
+    public void setUp() throws Exception {
+        mTelephonyRegistryMgr = (TelephonyRegistryManager) InstrumentationRegistry.getContext()
+            .getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
+    }
+
+    /**
+     * expect security exception as there is no carrier privilege permission.
+     */
+    @Test
+    public void testNotifyCarrierNetworkChange() {
+        try {
+            mTelephonyRegistryMgr.notifyCarrierNetworkChange(true);
+            fail("Expected SecurityException for notifyCarrierNetworkChange");
+        } catch (SecurityException ex) {
+            /* Expected */
+        }
+    }
+}
\ No newline at end of file
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/telephony3/src/android/telephony3/cts/TelephonyManagerTest.java b/tests/tests/telephony3/src/android/telephony3/cts/TelephonyManagerTest.java
index cd960b8..57f2e9b 100644
--- a/tests/tests/telephony3/src/android/telephony3/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony3/src/android/telephony3/cts/TelephonyManagerTest.java
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.os.Build;
 import android.telephony.TelephonyManager;
-import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
@@ -76,6 +75,10 @@
                     "An app targeting pre-Q with the READ_PHONE_STATE permission granted must "
                             + "receive null when invoking getSimSerialNumber",
                     mTelephonyManager.getSimSerialNumber());
+            assertNull(
+                    "An app targeting pre-Q with the READ_PHONE_STATE permission granted must "
+                            + "receive null when invoking getNai",
+                    mTelephonyManager.getNai());
             // Since Build.getSerial is not documented to return null in previous releases this test
             // verifies that the Build.UNKNOWN value is returned when the caller does not have
             // permission to access the device identifier.
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/cts/BoringLayoutTest.java b/tests/tests/text/src/android/text/cts/BoringLayoutTest.java
index 5f17b32..cb62639 100644
--- a/tests/tests/text/src/android/text/cts/BoringLayoutTest.java
+++ b/tests/tests/text/src/android/text/cts/BoringLayoutTest.java
@@ -20,6 +20,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
@@ -264,6 +265,17 @@
     }
 
     @Test
+    public void testIsBoringForEmptyString() {
+        Metrics metrics = new Metrics();
+        assertNotNull(BoringLayout.isBoring("", new TextPaint(), metrics));
+
+        // The default font Roboto has non-zero ascent/descent values. If metrics returns zeros, it
+        // means failed to retrieve the font metrics.
+        assertNotEquals(0, metrics.ascent);
+        assertNotEquals(0, metrics.descent);
+    }
+
+    @Test
     public void testIsBoring_resetsFontMetrics() {
         int someInt = 100;
         String text = "some text";
diff --git a/tests/tests/text/src/android/text/cts/StaticLayoutTest.java b/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
index 00856cd..12a4271 100644
--- a/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
+++ b/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
@@ -33,6 +33,7 @@
 import android.graphics.Paint.FontMetricsInt;
 import android.graphics.Typeface;
 import android.os.LocaleList;
+import android.platform.test.annotations.SecurityTest;
 import android.text.Editable;
 import android.text.Layout;
 import android.text.Layout.Alignment;
@@ -1683,6 +1684,7 @@
     }
 
     // This is for b/140755449
+    @SecurityTest
     @Test
     public void testBidiVisibleEnd() {
         TextPaint paint = new TextPaint();
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/text/src/android/text/style/cts/ReplacementSpanTest.java b/tests/tests/text/src/android/text/style/cts/ReplacementSpanTest.java
index 7dca324..e0829d9 100644
--- a/tests/tests/text/src/android/text/style/cts/ReplacementSpanTest.java
+++ b/tests/tests/text/src/android/text/style/cts/ReplacementSpanTest.java
@@ -16,6 +16,8 @@
 
 package android.text.style.cts;
 
+import static org.junit.Assert.assertEquals;
+
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Paint.FontMetricsInt;
@@ -42,6 +44,16 @@
         replacementSpan.updateDrawState(null);
     }
 
+    @Test
+    public void testContentDescription() {
+        final String testContentDescription = "testContentDescription";
+        ReplacementSpan replacementSpan = new MockReplacementSpan();
+
+        replacementSpan.setContentDescription(testContentDescription);
+
+        assertEquals(testContentDescription, replacementSpan.getContentDescription());
+    }
+
     private class MockReplacementSpan extends ReplacementSpan {
         @Override
         public void draw(Canvas canvas, CharSequence text, int start, int end,
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 c9d9108..6a0ba7c 100644
--- a/tests/tests/uiautomation/Android.bp
+++ b/tests/tests/uiautomation/Android.bp
@@ -22,7 +22,7 @@
         "general-tests",
     ],
     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..75eab20 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.ANSWER_PHONE_CALLS" />
 
-  <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..e05dc4b 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.ANSWER_PHONE_CALLS" />
     </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..9135e56 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.ANSWER_PHONE_CALLS, 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.ANSWER_PHONE_CALLS,
                     context.getPackageName()), PackageManager.PERMISSION_DENIED);
             packageManager.grantRuntimePermission(context.getPackageName(),
-                    Manifest.permission.CAMERA, Process.myUserHandle());
+                    Manifest.permission.ANSWER_PHONE_CALLS, 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.ANSWER_PHONE_CALLS,
                 context.getPackageName()), PackageManager.PERMISSION_GRANTED);
 
 
@@ -136,7 +136,7 @@
         }
         try {
             packageManager.revokeRuntimePermission(context.getPackageName(),
-                    Manifest.permission.CAMERA, Process.myUserHandle());
+                    Manifest.permission.ANSWER_PHONE_CALLS, 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/res/layout/viewpropertyanimator_test_layout.xml b/tests/tests/uirendering/res/layout/viewpropertyanimator_test_layout.xml
new file mode 100644
index 0000000..b6f67d8
--- /dev/null
+++ b/tests/tests/uirendering/res/layout/viewpropertyanimator_test_layout.xml
@@ -0,0 +1,33 @@
+<?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.
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:background="#FFFFFF"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent">
+    <FrameLayout
+        android:id="@+id/viewalpha_test_container"
+        android:background="#0000FF"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+            <android.uirendering.cts.testclasses.view.AlphaTestView
+                android:id="@+id/alpha_test_view"
+                android:layout_width="100px"
+                android:layout_height="100px"
+            />
+    </FrameLayout>
+</FrameLayout>
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/testclasses/LayerTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
index db37463..f42b004 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
@@ -562,8 +562,10 @@
                     // Adjust Y to match the same gradient percentage, regardless of vertical
                     // fading edge length.
                     int verticalFadingEdgeLength = webview.getVerticalFadingEdgeLength();
-                    testPoints[2].y = TEST_HEIGHT - verticalFadingEdgeLength * 10 / 42;
-                    testPoints[3].y = TEST_HEIGHT - verticalFadingEdgeLength * 5 / 42;
+                    testPoints[2].y = TEST_HEIGHT
+                        - (int) Math.round(verticalFadingEdgeLength * 10.0 / 42);
+                    testPoints[3].y = TEST_HEIGHT
+                        - (int) Math.round(verticalFadingEdgeLength * 5.0 / 42);
                 }, true, hwFence)
                 .runWithVerifier(new SamplePointVerifier(
                         testPoints,
@@ -603,8 +605,10 @@
                     // Adjust Y to match the same gradient percentage, regardless of vertical
                     // fading edge length.
                     int verticalFadingEdgeLength = webview.getVerticalFadingEdgeLength();
-                    testPoints[3].y = TEST_HEIGHT - verticalFadingEdgeLength * 10 / 42;
-                    testPoints[4].y = TEST_HEIGHT - verticalFadingEdgeLength * 5 / 42;
+                    testPoints[3].y = TEST_HEIGHT
+                        - (int) Math.round(verticalFadingEdgeLength * 10.0 / 42);
+                    testPoints[4].y = TEST_HEIGHT
+                        - (int) Math.round(verticalFadingEdgeLength * 5.0 / 42);
                 }, true, hwFence)
                 .runWithVerifier(new SamplePointVerifier(
                         testPoints,
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewPropertyAnimatorTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewPropertyAnimatorTests.java
new file mode 100644
index 0000000..0f749d9
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewPropertyAnimatorTests.java
@@ -0,0 +1,542 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uirendering.cts.testclasses;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.graphics.Color;
+import android.uirendering.cts.R;
+import android.uirendering.cts.bitmapverifiers.ColorVerifier;
+import android.uirendering.cts.testclasses.view.AlphaTestView;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+import android.uirendering.cts.testinfrastructure.ViewInitializer;
+import android.view.View;
+import android.view.ViewPropertyAnimator;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ViewPropertyAnimatorTests extends ActivityTestBase {
+
+    @Test
+    public void testViewCustomAlpha() {
+        createViewPropertyAnimatorTest(new ViewPropertyAnimatorTestDelegate<AlphaTestView>() {
+            @Override
+            public void configureView(AlphaTestView target) {
+                target.setStartColor(Color.RED);
+                target.setEndColor(Color.BLUE);
+            }
+
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.alpha(0.0f);
+            }
+
+            @Override
+            public void verifyViewState(AlphaTestView target) {
+                assertEquals(Color.BLUE, target.getBlendedColor());
+            }
+
+        }).runWithVerifier(new ColorVerifier(Color.BLUE));
+    }
+
+    @Test
+    public void testViewNonCustomAlpha() {
+        final CountDownLatch latch = new CountDownLatch(1);
+        createTest().addLayout(R.layout.viewpropertyanimator_test_layout,
+                (ViewInitializer) view -> {
+                    View testContent = view.findViewById(R.id.viewalpha_test_container);
+                    testContent.animate().alpha(0.0f).setListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            latch.countDown();
+                        }
+                    }).setDuration(16).start();
+                }, true, latch).runWithVerifier(new ColorVerifier(Color.WHITE));
+    }
+
+    @Test
+    public void testViewCustomAlphaBy() {
+        createViewPropertyAnimatorTest(new ViewPropertyAnimatorTestDelegate<AlphaTestView>() {
+            @Override
+            public void configureView(AlphaTestView target) {
+                target.setStartColor(Color.RED);
+                target.setEndColor(Color.BLUE);
+                target.setAlpha(0.5f);
+            }
+
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.alphaBy(-0.5f);
+            }
+
+            @Override
+            public void verifyViewState(AlphaTestView target) {
+                assertEquals(Color.BLUE, target.getBlendedColor());
+            }
+
+        }).runWithVerifier(new ColorVerifier(Color.BLUE));
+    }
+
+    @Test
+    public void testViewTranslateX() {
+        runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.translationX(100.0f);
+            }
+
+            @Override
+            public void verifyViewState(View target) {
+                assertEquals(100.0f, target.getTranslationX());
+            }
+        });
+    }
+
+    @Test
+    public void testViewTranslateXBy() {
+        runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+            @Override
+            public void configureView(View target) {
+                target.setTranslationX(20.0f);
+            }
+
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.translationXBy(100.0f);
+            }
+
+            @Override
+            public void verifyViewState(View target) {
+                assertEquals(120.0f, target.getTranslationX());
+            }
+        });
+    }
+
+    @Test
+    public void testViewTranslateY() {
+        runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.translationY(60.0f);
+
+            }
+
+            @Override
+            public void verifyViewState(View target) {
+                assertEquals(60.0f, target.getTranslationY());
+            }
+        });
+    }
+
+    @Test
+    public void testViewTranslateYBy() {
+        runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+            @Override
+            public void configureView(View target) {
+                target.setTranslationY(30.0f);
+            }
+
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.translationYBy(60.0f);
+            }
+
+            @Override
+            public void verifyViewState(View target) {
+                assertEquals(90.0f, target.getTranslationY());
+            }
+        });
+    }
+
+    @Test
+    public void testViewTranslateZ() {
+        runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.translationZ(30.0f);
+            }
+
+            @Override
+            public void verifyViewState(View target) {
+                assertEquals(30.0f, target.getTranslationZ());
+            }
+        });
+    }
+
+    @Test
+    public void testViewTranslateZBy() {
+        runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+            @Override
+            public void configureView(View target) {
+                target.setTranslationZ(40.0f);
+            }
+
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.translationZBy(30.0f);
+            }
+
+            @Override
+            public void verifyViewState(View target) {
+                assertEquals(70.0f, target.getTranslationZ());
+            }
+        });
+    }
+
+    @Test
+    public void testViewRotation() {
+        runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.rotation(20.0f);
+            }
+
+            @Override
+            public void verifyViewState(View target) {
+                assertEquals(20.0f, target.getRotation());
+            }
+        });
+    }
+
+    @Test
+    public void testViewRotationBy() {
+        runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+            @Override
+            public void configureView(View target) {
+                target.setRotation(30.0f);
+            }
+
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.rotationBy(20.0f);
+            }
+
+            @Override
+            public void verifyViewState(View target) {
+                assertEquals(50.0f, target.getRotation());
+            }
+        });
+    }
+
+    @Test
+    public void testViewRotationX() {
+        runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.rotationX(80.0f);
+            }
+
+            @Override
+            public void verifyViewState(View target) {
+                assertEquals(80.0f, target.getRotationX());
+            }
+        });
+    }
+
+    @Test
+    public void testViewRotationXBy() {
+        runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+            @Override
+            public void configureView(View target) {
+                target.setRotationX(30.0f);
+            }
+
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.rotationXBy(80.0f);
+            }
+
+            @Override
+            public void verifyViewState(View target) {
+                assertEquals(110.0f, target.getRotationX());
+            }
+        });
+    }
+
+    @Test
+    public void testViewRotationY() {
+        runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.rotationY(25.0f);
+            }
+
+            @Override
+            public void verifyViewState(View target) {
+                assertEquals(25.0f, target.getRotationY());
+            }
+        });
+    }
+
+    @Test
+    public void testViewRotationYBy() {
+        runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+            @Override
+            public void configureView(View target) {
+                target.setRotationY(10.0f);
+            }
+
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.rotationYBy(25.0f);
+            }
+
+            @Override
+            public void verifyViewState(View target) {
+                assertEquals(35.0f, target.getRotationY());
+            }
+        });
+    }
+
+    @Test
+    public void testViewScaleX() {
+        runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.scaleX(2.5f);
+            }
+
+            @Override
+            public void verifyViewState(View target) {
+                assertEquals(2.5f, target.getScaleX());
+            }
+        });
+    }
+
+    @Test
+    public void testViewScaleXBy() {
+        runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+            @Override
+            public void configureView(View target) {
+                target.setScaleX(1.2f);
+            }
+
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.scaleXBy(2.5f);
+            }
+
+            @Override
+            public void verifyViewState(View target) {
+                assertEquals(3.7f, target.getScaleX());
+            }
+        });
+    }
+
+    @Test
+    public void testViewScaleY() {
+        runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.scaleY(3.2f);
+            }
+
+            @Override
+            public void verifyViewState(View target) {
+                assertEquals(3.2f, target.getScaleY());
+            }
+        });
+    }
+
+    @Test
+    public void testViewScaleYBy() {
+        runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+            @Override
+            public void configureView(View target) {
+                target.setScaleY(1.2f);
+            }
+
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.scaleYBy(3.2f);
+            }
+
+            @Override
+            public void verifyViewState(View target) {
+                assertEquals(4.4f, target.getScaleY());
+            }
+        });
+    }
+
+    @Test
+    public void testViewX() {
+        runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.x(27.0f);
+            }
+
+            @Override
+            public void verifyViewState(View target) {
+                assertEquals(27.0f, target.getX());
+            }
+        });
+    }
+
+    @Test
+    public void testViewXBy() {
+        runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+            @Override
+            public void configureView(View target) {
+                target.setX(140.0f);
+            }
+
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.xBy(27.0f);
+            }
+
+            @Override
+            public void verifyViewState(View target) {
+                assertEquals(167.0f, target.getX());
+            }
+        });
+    }
+
+    @Test
+    public void testViewY() {
+        runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.y(77.0f);
+            }
+
+            @Override
+            public void verifyViewState(View target) {
+                assertEquals(77.0f, target.getY());
+            }
+        });
+    }
+
+    @Test
+    public void testViewYBy() {
+        runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+            @Override
+            public void configureView(View target) {
+                target.setY(80.0f);
+            }
+
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.yBy(77.0f);
+            }
+
+            @Override
+            public void verifyViewState(View target) {
+                assertEquals(157.0f, target.getY());
+            }
+        });
+    }
+
+    @Test
+    public void testViewZ() {
+        runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.z(17.0f);
+            }
+
+            @Override
+            public void verifyViewState(View target) {
+                assertEquals(17.0f, target.getZ());
+            }
+        });
+    }
+
+    @Test
+    public void testViewZBy() {
+        runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+            @Override
+            public void configureView(View target) {
+                target.setZ(38.0f);
+            }
+
+            @Override
+            public void configureAnimator(ViewPropertyAnimator animator) {
+                animator.zBy(17.0f);
+            }
+
+            @Override
+            public void verifyViewState(View target) {
+                assertEquals(55.0f, target.getZ());
+            }
+        });
+    }
+
+    private void runViewPropertyAnimatorTestWithoutVerification(
+            ViewPropertyAnimatorTestDelegate delegate) {
+        createViewPropertyAnimatorTest(delegate).runWithoutVerification();
+    }
+
+    private TestCaseBuilder createViewPropertyAnimatorTest(
+            final ViewPropertyAnimatorTestDelegate delegate) {
+        final CountDownLatch latch = new CountDownLatch(1);
+        return createTest().addLayout(R.layout.viewpropertyanimator_test_layout,
+                (ViewInitializer) view -> {
+                    AlphaTestView alphaView = view.findViewById(R.id.alpha_test_view);
+                    delegate.configureView(alphaView);
+                    alphaView.setStartColor(Color.RED);
+                    alphaView.setEndColor(Color.BLUE);
+                    ViewPropertyAnimator animator = alphaView.animate();
+                    delegate.configureAnimator(animator);
+                    animator.setListener(
+                            new AnimatorListenerAdapter() {
+
+                                @Override
+                                public void onAnimationEnd(Animator animator) {
+                                    delegate.verifyViewState(alphaView);
+                                    latch.countDown();
+                                }
+
+                            }).setDuration(16).start();
+                }, true, latch);
+    }
+
+    private interface ViewPropertyAnimatorTestDelegate<T extends View> {
+
+        default void configureView(T target) {
+            // NO-OP
+        }
+
+        void configureAnimator(ViewPropertyAnimator animator);
+
+        void verifyViewState(T target);
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/AlphaTestView.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/AlphaTestView.java
new file mode 100644
index 0000000..d475c8f
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/AlphaTestView.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.uirendering.cts.testclasses.view;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Test View used to verify a View's custom alpha implementation logic
+ * in conjunction with {@link android.view.ViewPropertyAnimator}
+ */
+public class AlphaTestView extends View {
+
+    private Paint mPaint = new Paint();
+    private int mStartColor = Color.RED;
+    private int mEndColor = Color.BLUE;
+
+    public AlphaTestView(Context context) {
+        super(context);
+    }
+
+    public AlphaTestView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public AlphaTestView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public AlphaTestView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    public void setStartColor(int startColor) {
+        mStartColor = startColor;
+        mPaint.setColor(mStartColor);
+    }
+
+    public void setEndColor(int endColor) {
+        mEndColor = endColor;
+    }
+
+    @Override
+    protected boolean onSetAlpha(int alpha) {
+        mPaint.setColor(blendColor(mStartColor, mEndColor, 1.0f - alpha / 255.0f));
+        return true;
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        canvas.drawRect(getLeft(), getTop(), getRight(), getBottom(), mPaint);
+    }
+
+    public int getBlendedColor() {
+        return mPaint.getColor();
+    }
+
+    private int blendColor(int color1, int color2, float ratio) {
+        float inverseRatio = 1 - ratio;
+        float a = (float) Color.alpha(color1) * inverseRatio + (float) Color.alpha(color2) * ratio;
+        float r = (float) Color.red(color1) * inverseRatio + (float) Color.red(color2) * ratio;
+        float g = (float) Color.green(color1) * inverseRatio + (float) Color.green(color2) * ratio;
+        float b = (float) Color.blue(color1) * inverseRatio + (float) Color.blue(color2) * ratio;
+        return Color.argb((int) a, (int) r, (int) g, (int) b);
+    }
+}
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..9cab157 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));
@@ -367,15 +384,19 @@
                     }
                 }
             } else {
-                // Check there are no black nor white pixels, because bilerp sampling changed
-                // pure black/white to a variety of gray intermediates.
+                // Check that a third of pixels are not black nor white, because bilerp sampling
+                // changed pure black/white to a variety of gray intermediates.
+                int nonBlackWhitePixels = 0;
                 for (int j = 0; j < pixels.length; j++) {
-                    if (pixels[j] == Color.BLACK || pixels[j] == Color.WHITE) {
-                        success = false;
+                    if (pixels[j] != Color.BLACK && pixels[j] != Color.WHITE) {
+                        nonBlackWhitePixels++;
+                    } else {
                         failPosition = j;
-                        break;
                     }
                 }
+                if (nonBlackWhitePixels < pixels.length / 3) {
+                    success = false;
+                }
             }
             assertTrue("Unexpected color at position " + failPosition + " = "
                     + Integer.toHexString(pixels[failPosition]) + " " + transform.toString(),
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/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/AndroidManifest.xml b/tests/tests/voicesettings/AndroidManifest.xml
index 5ea6a84..8be0b80 100644
--- a/tests/tests/voicesettings/AndroidManifest.xml
+++ b/tests/tests/voicesettings/AndroidManifest.xml
@@ -23,7 +23,7 @@
     <application>
         <uses-library android:name="android.test.runner" />
 
-        <activity android:name="com.android.compatibility.common.util.BroadcastTestStartActivity"
+        <activity android:name=".BroadcastTestStartActivity"
                   android:label="The Target Activity for VoiceSettings CTS Test">
             <intent-filter>
                 <action android:name="android.intent.action.TEST_START_ACTIVITY_ZEN_MODE" />
diff --git a/tests/tests/voicesettings/AndroidTest.xml b/tests/tests/voicesettings/AndroidTest.xml
index 6de903a..11e749e 100644
--- a/tests/tests/voicesettings/AndroidTest.xml
+++ b/tests/tests/voicesettings/AndroidTest.xml
@@ -25,7 +25,6 @@
         <option name="test-file-name" value="CtsVoiceSettingsTestCases.apk" />
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <option name="run-command" value="settings put secure voice_interaction_service android.voicesettings.service/.MainInteractionService" />
 
         <!-- Close the "turn on battery saver?" dialog, and wait for the broadcast queue to drain. -->
         <option name="teardown-command" value="am broadcast --receiver-foreground -a android.intent.action.CLOSE_SYSTEM_DIALOGS" />
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java
index 2ef1999..2992c5b 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java
@@ -18,13 +18,15 @@
 
 import static android.provider.Settings.ACTION_VOICE_CONTROL_AIRPLANE_MODE;
 
-import com.android.compatibility.common.util.BroadcastTestBase;
-import com.android.compatibility.common.util.BroadcastUtils;
+import static com.google.common.truth.Truth.assertThat;
 
 import android.provider.Settings;
-import android.provider.Settings.Global;
 import android.util.Log;
 
+import com.android.compatibility.common.util.BroadcastUtils;
+
+import org.junit.Test;
+
 public class AirplaneModeTest extends BroadcastTestBase {
     static final String TAG = "AirplaneModeTest";
     private static final String VOICE_SETTINGS_PACKAGE = "android.voicesettings.service";
@@ -36,17 +38,12 @@
     private static final int AIRPLANE_MODE_IS_ON = 1;
 
     @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mContext = getInstrumentation().getTargetContext();
+    protected void customSetup() throws Exception {
         mHasFeature = mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS);
         Log.v(TAG, "setUp(): mHasFeature=" + mHasFeature);
     }
 
-    public AirplaneModeTest() {
-        super();
-    }
-
+    @Test
     public void testAll() throws Exception {
         if (!mHasFeature) {
             Log.i(TAG, "The device doesn't support feature: " + FEATURE_VOICE_RECOGNIZERS);
@@ -96,7 +93,7 @@
         // verify the test results
         int mode = getMode();
         Log.i(TAG, "After testing, AIRPLANE_MODE is set to: " + mode);
-        assertEquals(expectedMode, mode);
+        assertThat(mode).isEqualTo(expectedMode);
         Log.i(TAG, "Successfully Tested: " + test);
         return true;
     }
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java
index 759d82e..181c4ba 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java
@@ -18,13 +18,14 @@
 
 import static android.provider.Settings.ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE;
 
-import com.android.compatibility.common.util.BroadcastTestBase;
-import com.android.compatibility.common.util.BroadcastUtils;
-
 import android.content.Context;
 import android.os.PowerManager;
 import android.util.Log;
 
+import com.android.compatibility.common.util.BroadcastUtils;
+
+import org.junit.Test;
+
 public class BatterySaverModeTest extends BroadcastTestBase {
     static final String TAG = "BatterySaverModeTest";
     private static final String VOICE_SETTINGS_PACKAGE = "android.voicesettings.service";
@@ -32,17 +33,12 @@
         "android.voicesettings.service.VoiceInteractionMain";
     protected static final String FEATURE_VOICE_RECOGNIZERS = "android.software.voice_recognizers";
 
-    public BatterySaverModeTest() {
-        super();
-    }
-
     @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mContext = getInstrumentation().getTargetContext();
+    protected void customSetup() throws Exception {
         mHasFeature = mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS);
     }
 
+    @Test
     public void testAll() throws Exception {
         if (!mHasFeature) {
             Log.i(TAG, "The device doesn't support feature: " + FEATURE_VOICE_RECOGNIZERS);
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/BroadcastTestBase.java b/tests/tests/voicesettings/src/android/voicesettings/cts/BroadcastTestBase.java
similarity index 66%
rename from common/device-side/util-axt/src/com/android/compatibility/common/util/BroadcastTestBase.java
rename to tests/tests/voicesettings/src/android/voicesettings/cts/BroadcastTestBase.java
index 7500050..95e9433 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/BroadcastTestBase.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/BroadcastTestBase.java
@@ -14,7 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.compatibility.common.util;
+package android.voicesettings.cts;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
 
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -23,18 +29,31 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
-import android.test.ActivityInstrumentationTestCase2;
+import android.provider.Settings;
 import android.util.Log;
 
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.BroadcastUtils;
+import com.android.compatibility.common.util.SettingsStateChangerRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-public class BroadcastTestBase extends ActivityInstrumentationTestCase2<
-                                       BroadcastTestStartActivity> {
+@RunWith(AndroidJUnit4.class)
+public abstract class BroadcastTestBase {
+
     static final String TAG = "BroadcastTestBase";
     protected static final int TIMEOUT_MS = 20 * 1000;
 
-    protected Context mContext;
+    protected final Context mContext = getInstrumentation().getTargetContext();
     protected Bundle mResultExtras;
     private CountDownLatch mLatch;
     protected ActivityDoneReceiver mActivityDoneReceiver = null;
@@ -42,20 +61,29 @@
     private BroadcastUtils.TestcaseType mTestCaseType;
     protected boolean mHasFeature;
 
-    public BroadcastTestBase() {
-        super(BroadcastTestStartActivity.class);
-    }
+    private final SettingsStateChangerRule mServiceSetterRule = new SettingsStateChangerRule(
+            mContext, Settings.Secure.VOICE_INTERACTION_SERVICE,
+            "android.voicesettings.service/.MainInteractionService");
+    private final ActivityTestRule<BroadcastTestStartActivity> mActivityTestRule =
+            new ActivityTestRule<>(BroadcastTestStartActivity.class, false, false);
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+    @Rule
+    public final RuleChain mgRules = RuleChain
+            .outerRule(mServiceSetterRule)
+            .around(mActivityTestRule);
+
+    @Before
+    public void setUp() throws Exception {
         mHasFeature = false;
+
+        customSetup();
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public final void tearDown() throws Exception {
         Log.v(TAG, getClass().getSimpleName() + ".tearDown(): hasFeature=" + mHasFeature
                 + " receiver=" + mActivityDoneReceiver);
+
         if (mHasFeature && mActivityDoneReceiver != null) {
             try {
                 mContext.unregisterReceiver(mActivityDoneReceiver);
@@ -66,13 +94,18 @@
             }
             mActivityDoneReceiver = null;
         }
-        super.tearDown();
+    }
+
+    /**
+     * Test-specific setup - doesn't need to call {@code super} neither use <code>@Before</code>.
+     */
+    protected void customSetup() throws Exception {
     }
 
     protected boolean isIntentSupported(String intentStr) {
         Intent intent = new Intent(intentStr);
         final PackageManager manager = mContext.getPackageManager();
-        assertNotNull(manager);
+        assertThat(manager).isNotNull();
         if (manager.resolveActivity(intent, 0) == null) {
             Log.i(TAG, "No Activity found for the intent: " + intentStr);
             return false;
@@ -83,13 +116,12 @@
     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();
+        intent.setComponent(new ComponentName(mContext, BroadcastTestStartActivity.class));
+        mActivity = mActivityTestRule.launchActivity(intent);
     }
 
-    protected void registerBroadcastReceiver(BroadcastUtils.TestcaseType testCaseType) throws Exception {
+    protected void registerBroadcastReceiver(BroadcastUtils.TestcaseType testCaseType)
+            throws Exception {
         mTestCaseType = testCaseType;
         mLatch = new CountDownLatch(1);
         mActivityDoneReceiver = new ActivityDoneReceiver();
@@ -98,7 +130,7 @@
     }
 
     protected boolean startTestAndWaitForBroadcast(BroadcastUtils.TestcaseType testCaseType,
-                                                   String pkg, String cls) throws Exception {
+            String pkg, String cls) throws Exception {
         Log.i(TAG, "Begin Testing: " + testCaseType);
         registerBroadcastReceiver(testCaseType);
         mActivity.startTest(testCaseType.toString(), pkg, cls);
@@ -114,7 +146,7 @@
         public void onReceive(Context context, Intent intent) {
             if (intent.getAction().equals(
                     BroadcastUtils.BROADCAST_INTENT +
-                        BroadcastTestBase.this.mTestCaseType.toString())) {
+                            BroadcastTestBase.this.mTestCaseType.toString())) {
                 Bundle extras = intent.getExtras();
                 Log.i(TAG, "received_broadcast for " + BroadcastUtils.toBundleString(extras));
                 BroadcastTestBase.this.mResultExtras = extras;
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/BroadcastTestStartActivity.java b/tests/tests/voicesettings/src/android/voicesettings/cts/BroadcastTestStartActivity.java
similarity index 95%
rename from common/device-side/util-axt/src/com/android/compatibility/common/util/BroadcastTestStartActivity.java
rename to tests/tests/voicesettings/src/android/voicesettings/cts/BroadcastTestStartActivity.java
index 4b3e85d..163c2e4 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/BroadcastTestStartActivity.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/BroadcastTestStartActivity.java
@@ -14,14 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.compatibility.common.util;
+package android.voicesettings.cts;
 
 import android.app.Activity;
-import android.content.Intent;
 import android.content.ComponentName;
+import android.content.Intent;
 import android.os.Bundle;
 import android.util.Log;
 
+import com.android.compatibility.common.util.BroadcastUtils;
+
 public class BroadcastTestStartActivity extends Activity {
     static final String TAG = "BroadcastTestStartActivity";
 
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
index e01f3b9..5bb2f8c 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
@@ -20,13 +20,15 @@
 import static android.provider.Settings.EXTRA_DO_NOT_DISTURB_MODE_ENABLED;
 import static android.provider.Settings.EXTRA_DO_NOT_DISTURB_MODE_MINUTES;
 
-import com.android.compatibility.common.util.BroadcastTestBase;
-import com.android.compatibility.common.util.BroadcastUtils;
+import static com.google.common.truth.Truth.assertThat;
 
 import android.provider.Settings;
-import android.provider.Settings.Global;
 import android.util.Log;
 
+import com.android.compatibility.common.util.BroadcastUtils;
+
+import org.junit.Test;
+
 public class ZenModeTest extends BroadcastTestBase {
     static final String TAG = "ZenModeTest";
     private static final String VOICE_SETTINGS_PACKAGE = "android.voicesettings.service";
@@ -35,9 +37,7 @@
     protected static final String FEATURE_VOICE_RECOGNIZERS = "android.software.voice_recognizers";
 
     @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mContext = getInstrumentation().getTargetContext();
+    protected void customSetup() throws Exception {
         mHasFeature = mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS);
     }
 
@@ -47,10 +47,7 @@
     private static final int ZEN_MODE_IS_OFF = 0;
     private static final int ZEN_MODE_IS_ALARMS = 3;
 
-    public ZenModeTest() {
-        super();
-    }
-
+    @Test
     public void testAll() throws Exception {
         if (!mHasFeature) {
             Log.i(TAG, "The device doesn't support feature: " + FEATURE_VOICE_RECOGNIZERS);
@@ -99,15 +96,15 @@
         // verify the test results
         int mode = getMode();
         Log.i(TAG, "After testing, zen-mode is set to: " + mode);
-        assertEquals(expectedMode, mode);
+        assertThat(mode).isEqualTo(expectedMode);
         Log.i(TAG, "results_received: " + BroadcastUtils.toBundleString(mResultExtras));
-        assertNotNull(mResultExtras);
+        assertThat(mResultExtras).isNotNull();
         if (expectedMode == ZEN_MODE_IS_ALARMS) {
-            assertTrue(mResultExtras.getBoolean(EXTRA_DO_NOT_DISTURB_MODE_ENABLED));
-            assertEquals(BroadcastUtils.NUM_MINUTES_FOR_ZENMODE,
-                    mResultExtras.getInt(EXTRA_DO_NOT_DISTURB_MODE_MINUTES));
+            assertThat(mResultExtras.getBoolean(EXTRA_DO_NOT_DISTURB_MODE_ENABLED)).isTrue();
+            assertThat(mResultExtras.getInt(EXTRA_DO_NOT_DISTURB_MODE_MINUTES)).isEqualTo(
+                    BroadcastUtils.NUM_MINUTES_FOR_ZENMODE);
         } else {
-            assertFalse(mResultExtras.getBoolean(EXTRA_DO_NOT_DISTURB_MODE_ENABLED));
+            assertThat(mResultExtras.getBoolean(EXTRA_DO_NOT_DISTURB_MODE_ENABLED)).isFalse();
         }
         Log.i(TAG, "Successfully Tested: " + test);
         return true;
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
index ec39a5c..8c3b252 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
@@ -902,8 +902,11 @@
         @Override
         public void onPageFinished(WebView view, String url) {
             super.onPageFinished(view, url);
-            assertTrue(mOnPageStartedCalled);
-            assertTrue(mOnLoadResourceCalled);
+            // TODO(ntfschr): propagate these exceptions to the instrumentation thread.
+            assertTrue("Expected onPageStarted to be called before onPageFinished",
+                    mOnPageStartedCalled);
+            assertTrue("Expected onLoadResource to be called before onPageFinished",
+                    mOnLoadResourceCalled);
             mOnPageFinishedCalled = true;
         }
 
diff --git a/tests/tests/widget/AndroidTest.xml b/tests/tests/widget/AndroidTest.xml
index fd9afe8..8cd40dc 100644
--- a/tests/tests/widget/AndroidTest.xml
+++ b/tests/tests/widget/AndroidTest.xml
@@ -26,5 +26,6 @@
         <option name="package" value="android.widget.cts" />
         <option name="runtime-hint" value="11m55s" />
         <option name="hidden-api-checks" value="false" />
+        <option name="instrumentation-arg" key="thisisignored" value="thisisignored --no-window-animation" />
     </test>
 </configuration>
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/listview_layout.xml b/tests/tests/widget/res/layout/listview_layout.xml
index 3094a89..79669c1 100644
--- a/tests/tests/widget/res/layout/listview_layout.xml
+++ b/tests/tests/widget/res/layout/listview_layout.xml
@@ -16,6 +16,7 @@
 
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/content"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
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/AbsListViewTest.java b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
index 612b233..0a57c21 100644
--- a/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
@@ -45,6 +45,7 @@
 import android.content.res.Configuration;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Insets;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.text.Editable;
@@ -58,6 +59,7 @@
 import android.view.Menu;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowInsets;
 import android.widget.AbsListView;
 import android.widget.AbsListView.OnScrollListener;
 import android.widget.AdapterView;
@@ -128,7 +130,7 @@
     private static final float DELTA = 0.001f;
 
     @Before
-    public void setup() throws Exception {
+    public void setup() throws Throwable {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
         final Activity activity = mActivityRule.getActivity();
         // Always use the activity context
@@ -146,6 +148,17 @@
                 android.R.layout.simple_list_item_1, COUNTRY_LIST);
 
         mListView = (ListView) activity.findViewById(R.id.listview_default);
+
+        // Full-height drag gestures clash with system navigation gestures (such as
+        // swipe up from the bottom of the screen to go home). Get the system
+        // gesture insets and apply bottom padding on the entire content so
+        // that our own drag gestures are processed within the activity.
+        WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mListView, () -> {
+            ViewGroup content = activity.findViewById(R.id.content);
+            WindowInsets rootWindowInsets = content.getRootWindowInsets();
+            Insets systemGestureInsets = rootWindowInsets.getSystemGestureInsets();
+            content.setPadding(0, 0, 0, systemGestureInsets.bottom);
+        });
     }
 
     private boolean isWatch() {
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/SeekBarTest.java b/tests/tests/widget/src/android/widget/cts/SeekBarTest.java
index 7cd082b..50405dc 100644
--- a/tests/tests/widget/src/android/widget/cts/SeekBarTest.java
+++ b/tests/tests/widget/src/android/widget/cts/SeekBarTest.java
@@ -26,8 +26,11 @@
 
 import android.app.Activity;
 import android.app.Instrumentation;
+import android.graphics.Rect;
 import android.os.SystemClock;
 import android.view.MotionEvent;
+import android.view.View;
+import android.view.WindowInsets;
 import android.widget.SeekBar;
 
 import androidx.test.InstrumentationRegistry;
@@ -42,6 +45,9 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Test {@link SeekBar}.
  */
@@ -57,10 +63,37 @@
             new ActivityTestRule<>(SeekBarCtsActivity.class);
 
     @Before
-    public void setup() {
+    public void setup() throws Throwable {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
         mActivity = mActivityRule.getActivity();
-        mSeekBar = (SeekBar) mActivity.findViewById(R.id.seekBar);
+        mSeekBar = mActivity.findViewById(R.id.seekBar);
+        if (mSeekBar.isAttachedToWindow()) {
+            updateExclusionRects();
+        } else {
+            mSeekBar.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+                @Override
+                public void onViewAttachedToWindow(View view) {
+                    mSeekBar.removeOnAttachStateChangeListener(this);
+                    updateExclusionRects();
+                }
+
+                @Override
+                public void onViewDetachedFromWindow(View view) {
+                }
+            });
+        }
+    }
+
+    private void updateExclusionRects() {
+        // "Mark" the left edge of our seek bar to be excluded from system gestures.
+        // This does not need to be RTL-aware since the logic in the change listener
+        // always injects the events from left to right.
+        WindowInsets rootWindowInsets = mSeekBar.getRootWindowInsets();
+        List<Rect> exclusion = new ArrayList<>();
+        exclusion.add(new Rect(0, 0,
+                rootWindowInsets.getSystemGestureInsets().left,
+                mSeekBar.getHeight()));
+        mSeekBar.setSystemGestureExclusionRects(exclusion);
     }
 
     @Test
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..2296ae2 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
@@ -113,14 +114,12 @@
         assertEquals(R.style.ExplicitStyle1, stackTextView1textSize[1])
         assertEquals(R.style.ParentOfExplicitStyle1, stackTextView1textSize[2])
         assertEquals(R.style.TextViewWithoutColorAndAppearance, stackTextView1textSize[3])
-        val stackTextView1textColorHighlight =
-                textview1.getAttributeResolutionStack(android.R.attr.textColorHighlight)
-        assertEquals(5, stackTextView1textColorHighlight.size.toLong())
-        assertEquals(R.layout.widget_attribute_layout, stackTextView1textColorHighlight[0])
-        assertEquals(R.style.ExplicitStyle1, stackTextView1textColorHighlight[1])
-        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