Merge "[automerger skipped] Merge "Uses a self-destruct receiver to kill an external process." into oreo-mr1-cts-dev am: f8c434dac0  -s ours" into pie-cts-dev am: 63ed9bda10  -s ours am: f1fa083bc6  -s ours
am: c60b98a9e2  -s ours

Change-Id: Iddc84cefd4ea4a984091812384a20bbb63169698
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 5f3e99f..0424122 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -11,3 +11,4 @@
                       tests/tests/uirendering/
                       tests/tests/view/
                       tests/tests/widget/
+                      common/device-side/util/
diff --git a/apps/CameraITS/build/envsetup.sh b/apps/CameraITS/build/envsetup.sh
index a21108e..ae12e10 100644
--- a/apps/CameraITS/build/envsetup.sh
+++ b/apps/CameraITS/build/envsetup.sh
@@ -63,6 +63,4 @@
         echo ">> Unit test for $M failed" >&2
 done
 
-alias gpylint='gpylint --disable=F0401 --disable=C6304 --rcfile=$CAMERA_ITS_TOP"/build/scripts/gpylint_rcfile"'
-# F0401 ignores import errors since gpylint does not have the python paths
-# C6304 ignore Copyright line errors.
+alias gpylint='gpylint --rcfile=$CAMERA_ITS_TOP"/build/scripts/gpylint_rcfile"'
diff --git a/apps/CameraITS/build/scripts/gpylint_rcfile b/apps/CameraITS/build/scripts/gpylint_rcfile
index 37f43f7..f92c613 100644
--- a/apps/CameraITS/build/scripts/gpylint_rcfile
+++ b/apps/CameraITS/build/scripts/gpylint_rcfile
@@ -13,7 +13,10 @@
 # --enable=similarities". If you want to run only the classes checker, but have
 # no Warning level messages displayed, use"--disable=all --enable=classes
 # --disable=W"
-disable=design,similarities,no-self-use,attribute-defined-outside-init,locally-disabled,star-args,pointless-except,bad-option-value,global-statement,fixme,suppressed-message,useless-suppression
+disable=design,similarities,no-self-use,attribute-defined-outside-init,locally-disabled,star-args,pointless-except,bad-option-value,global-statement,fixme,suppressed-message,useless-suppression, F0401, C6304, C0111
+# F0401 ignores import errors since gpylint does not have the python paths
+# C6304 ignore Copyright line errors.
+# C0111 ignore Docstring at top of file.
 
 # Enable the message, report, category or checker with the given id(s). You can
 # either give multiple identifier separated by comma (,) or put this option
diff --git a/apps/CameraITS/tests/scene0/test_gyro_bias.py b/apps/CameraITS/tests/scene0/test_gyro_bias.py
index 86445fe..36070e5 100644
--- a/apps/CameraITS/tests/scene0/test_gyro_bias.py
+++ b/apps/CameraITS/tests/scene0/test_gyro_bias.py
@@ -12,47 +12,44 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import its.image
+import os
+import time
+
 import its.caps
 import its.device
+import its.image
 import its.objects
 import its.target
-import time
-from matplotlib import pylab
-import os.path
 import matplotlib
-import matplotlib.pyplot
+from matplotlib import pylab
 import numpy
 
+NAME = os.path.basename(__file__).split('.')[0]
+N = 20  # Number of samples averaged together, in the plot.
+MEAN_THRESH = 0.01  # PASS/FAIL threshold for gyro mean drift
+VAR_THRESH = 0.001  # PASS/FAIL threshold for gyro variance drift
+
+
 def main():
     """Test if the gyro has stable output when device is stationary.
     """
-    NAME = os.path.basename(__file__).split(".")[0]
-
-    # Number of samples averaged together, in the plot.
-    N = 20
-
-    # Pass/fail thresholds for gyro drift
-    MEAN_THRESH = 0.01
-    VAR_THRESH = 0.001
-
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
         # Only run test if the appropriate caps are claimed.
         its.caps.skip_unless(its.caps.sensor_fusion(props))
 
-        print "Collecting gyro events"
+        print 'Collecting gyro events'
         cam.start_sensor_events()
         time.sleep(5)
-        gyro_events = cam.get_sensor_events()["gyro"]
+        gyro_events = cam.get_sensor_events()['gyro']
 
     nevents = (len(gyro_events) / N) * N
     gyro_events = gyro_events[:nevents]
-    times = numpy.array([(e["time"] - gyro_events[0]["time"])/1000000000.0
+    times = numpy.array([(e['time'] - gyro_events[0]['time'])/1000000000.0
                          for e in gyro_events])
-    xs = numpy.array([e["x"] for e in gyro_events])
-    ys = numpy.array([e["y"] for e in gyro_events])
-    zs = numpy.array([e["z"] for e in gyro_events])
+    xs = numpy.array([e['x'] for e in gyro_events])
+    ys = numpy.array([e['y'] for e in gyro_events])
+    zs = numpy.array([e['z'] for e in gyro_events])
 
     # Group samples into size-N groups and average each together, to get rid
     # of individual random spikes in the data.
@@ -61,17 +58,19 @@
     ys = ys.reshape(nevents/N, N).mean(1)
     zs = zs.reshape(nevents/N, N).mean(1)
 
-    pylab.plot(times, xs, 'r', label="x")
-    pylab.plot(times, ys, 'g', label="y")
-    pylab.plot(times, zs, 'b', label="z")
-    pylab.xlabel("Time (seconds)")
-    pylab.ylabel("Gyro readings (mean of %d samples)"%(N))
+    pylab.plot(times, xs, 'r', label='x')
+    pylab.plot(times, ys, 'g', label='y')
+    pylab.plot(times, zs, 'b', label='z')
+    pylab.xlabel('Time (seconds)')
+    pylab.ylabel('Gyro readings (mean of %d samples)'%(N))
     pylab.legend()
-    matplotlib.pyplot.savefig("%s_plot.png" % (NAME))
+    matplotlib.pyplot.savefig('%s_plot.png' % (NAME))
 
-    for samples in [xs,ys,zs]:
-        assert(samples.mean() < MEAN_THRESH)
-        assert(numpy.var(samples) < VAR_THRESH)
+    for samples in [xs, ys, zs]:
+        mean = samples.mean()
+        var = numpy.var(samples)
+        assert mean < MEAN_THRESH, 'mean: %.3f, TOL=%.2f' % (mean, MEAN_THRESH)
+        assert var < VAR_THRESH, 'var: %.4f, TOL=%.3f' % (var, VAR_THRESH)
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CameraITS/tests/scene0/test_jitter.py b/apps/CameraITS/tests/scene0/test_jitter.py
index 6a156dd..861c14e 100644
--- a/apps/CameraITS/tests/scene0/test_jitter.py
+++ b/apps/CameraITS/tests/scene0/test_jitter.py
@@ -12,24 +12,26 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import its.image
+import os.path
+
 import its.caps
 import its.device
+import its.image
 import its.objects
-import os.path
-from matplotlib import pylab
+
 import matplotlib
-import matplotlib.pyplot
+from matplotlib import pylab
+
+# PASS/FAIL thresholds
+MIN_AVG_FRAME_DELTA = 30  # at least 30ms delta between frames
+MAX_VAR_FRAME_DELTA = 0.01  # variance of frame deltas
+MAX_FRAME_DELTA_JITTER = 0.3  # max ms gap from the average frame delta
+
+NAME = os.path.basename(__file__).split('.')[0]
+
 
 def main():
-    """Measure jitter in camera timestamps.
-    """
-    NAME = os.path.basename(__file__).split(".")[0]
-
-    # Pass/fail thresholds
-    MIN_AVG_FRAME_DELTA = 30 # at least 30ms delta between frames
-    MAX_VAR_FRAME_DELTA = 0.01 # variance of frame deltas
-    MAX_FRAME_DELTA_JITTER = 0.3 # max ms gap from the average frame delta
+    """Measure jitter in camera timestamps."""
 
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
@@ -41,25 +43,28 @@
 
         # Print out the millisecond delta between the start of each exposure
         tstamps = [c['metadata']['android.sensor.timestamp'] for c in caps]
-        deltas = [tstamps[i]-tstamps[i-1] for i in range(1,len(tstamps))]
+        deltas = [tstamps[i]-tstamps[i-1] for i in range(1, len(tstamps))]
         deltas_ms = [d/1000000.0 for d in deltas]
         avg = sum(deltas_ms) / len(deltas_ms)
         var = sum([d*d for d in deltas_ms]) / len(deltas_ms) - avg * avg
         range0 = min(deltas_ms) - avg
         range1 = max(deltas_ms) - avg
-        print "Average:", avg
-        print "Variance:", var
-        print "Jitter range:", range0, "to", range1
+        print 'Average:', avg
+        print 'Variance:', var
+        print 'Jitter range:', range0, 'to', range1
 
         # Draw a plot.
         pylab.plot(range(len(deltas_ms)), deltas_ms)
-        matplotlib.pyplot.savefig("%s_deltas.png" % (NAME))
+        pylab.title(NAME)
+        pylab.xlabel('frame number')
+        pylab.ylabel('jitter (ms)')
+        matplotlib.pyplot.savefig('%s_deltas.png' % (NAME))
 
         # Test for pass/fail.
-        assert(avg > MIN_AVG_FRAME_DELTA)
-        assert(var < MAX_VAR_FRAME_DELTA)
-        assert(abs(range0) < MAX_FRAME_DELTA_JITTER)
-        assert(abs(range1) < MAX_FRAME_DELTA_JITTER)
+        assert avg > MIN_AVG_FRAME_DELTA
+        assert var < MAX_VAR_FRAME_DELTA
+        assert abs(range0) < MAX_FRAME_DELTA_JITTER
+        assert abs(range1) < MAX_FRAME_DELTA_JITTER
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CameraITS/tests/scene0/test_unified_timestamps.py b/apps/CameraITS/tests/scene0/test_unified_timestamps.py
index ae4583f..f72f229 100644
--- a/apps/CameraITS/tests/scene0/test_unified_timestamps.py
+++ b/apps/CameraITS/tests/scene0/test_unified_timestamps.py
@@ -12,10 +12,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import time
+
+import its.caps
 import its.device
 import its.objects
-import its.caps
-import time
+
 
 def main():
     """Test if image and motion sensor events are in the same time domain.
@@ -36,7 +38,7 @@
         ts_image0 = cap['metadata']['android.sensor.timestamp']
 
         # Get the timestamps of motion events.
-        print "Reading sensor measurements"
+        print 'Reading sensor measurements'
         sensors = cam.get_sensors()
         cam.start_sensor_events()
         time.sleep(2.0)
@@ -45,20 +47,20 @@
         ts_sensor_last = {}
         for sensor, existing in sensors.iteritems():
             if existing:
-                assert(len(events[sensor]) > 0)
-                ts_sensor_first[sensor] = events[sensor][0]["time"]
-                ts_sensor_last[sensor] = events[sensor][-1]["time"]
+                assert events[sensor], '%s sensor has no events!' % sensor
+                ts_sensor_first[sensor] = events[sensor][0]['time']
+                ts_sensor_last[sensor] = events[sensor][-1]['time']
 
         # Get the timestamp of another image.
         cap = cam.do_capture(req, fmt)
         ts_image1 = cap['metadata']['android.sensor.timestamp']
 
-        print "Image timestamps:", ts_image0, ts_image1
+        print 'Image timestamps:', ts_image0, ts_image1
 
         # The motion timestamps must be between the two image timestamps.
         for sensor, existing in sensors.iteritems():
             if existing:
-                print "%s timestamps: %d %d" % (sensor, ts_sensor_first[sensor],
+                print '%s timestamps: %d %d' % (sensor, ts_sensor_first[sensor],
                                                 ts_sensor_last[sensor])
                 assert ts_image0 < ts_sensor_first[sensor] < ts_image1
                 assert ts_image0 < ts_sensor_last[sensor] < ts_image1
diff --git a/apps/CameraITS/tests/scene1/test_black_white.py b/apps/CameraITS/tests/scene1/test_black_white.py
index 18bc001..95f856a 100644
--- a/apps/CameraITS/tests/scene1/test_black_white.py
+++ b/apps/CameraITS/tests/scene1/test_black_white.py
@@ -12,19 +12,20 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import its.image
+import os.path
+
 import its.caps
 import its.device
+import its.image
 import its.objects
-from matplotlib import pylab
-import os.path
 import matplotlib
-import matplotlib.pyplot
+from matplotlib import pylab
+
+NAME = os.path.basename(__file__).split(".")[0]
+
 
 def main():
-    """Test that the device will produce full black+white images.
-    """
-    NAME = os.path.basename(__file__).split(".")[0]
+    """Test that the device will produce full black+white images."""
 
     r_means = []
     g_means = []
@@ -40,54 +41,65 @@
         if debug:
             fmt = largest_yuv
         else:
-            match_ar = (largest_yuv['width'], largest_yuv['height'])
+            match_ar = (largest_yuv["width"], largest_yuv["height"])
             fmt = its.objects.get_smallest_yuv_format(props, match_ar=match_ar)
 
-        expt_range = props['android.sensor.info.exposureTimeRange']
-        sens_range = props['android.sensor.info.sensitivityRange']
+        expt_range = props["android.sensor.info.exposureTimeRange"]
+        sens_range = props["android.sensor.info.sensitivityRange"]
 
         # Take a shot with very low ISO and exposure time. Expect it to
         # be black.
-        print "Black shot: sens = %d, exp time = %.4fms" % (
-                sens_range[0], expt_range[0]/1000000.0)
         req = its.objects.manual_capture_request(sens_range[0], expt_range[0])
         cap = cam.do_capture(req, fmt)
         img = its.image.convert_capture_to_rgb_image(cap)
-        its.image.write_image(img, "%s_black.jpg" % (NAME))
+        its.image.write_image(img, '%s_black.jpg' % (NAME))
         tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
         black_means = its.image.compute_image_means(tile)
         r_means.append(black_means[0])
         g_means.append(black_means[1])
         b_means.append(black_means[2])
         print "Dark pixel means:", black_means
+        r_exp = cap["metadata"]["android.sensor.exposureTime"]
+        r_iso = cap["metadata"]["android.sensor.sensitivity"]
+        print "Black shot write values: sens = %d, exp time = %.4fms" % (
+                sens_range[0], expt_range[0]/1000000.0)
+        print "Black shot read values: sens = %d, exp time = %.4fms\n" % (
+                r_iso, r_exp/1000000.0)
 
         # Take a shot with very high ISO and exposure time. Expect it to
         # be white.
-        print "White shot: sens = %d, exp time = %.2fms" % (
-                sens_range[1], expt_range[1]/1000000.0)
         req = its.objects.manual_capture_request(sens_range[1], expt_range[1])
         cap = cam.do_capture(req, fmt)
         img = its.image.convert_capture_to_rgb_image(cap)
-        its.image.write_image(img, "%s_white.jpg" % (NAME))
+        its.image.write_image(img, '%s_white.jpg' % (NAME))
         tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
         white_means = its.image.compute_image_means(tile)
         r_means.append(white_means[0])
         g_means.append(white_means[1])
         b_means.append(white_means[2])
         print "Bright pixel means:", white_means
+        r_exp = cap["metadata"]["android.sensor.exposureTime"]
+        r_iso = cap["metadata"]["android.sensor.sensitivity"]
+        print "White shot write values: sens = %d, exp time = %.2fms" % (
+                sens_range[1], expt_range[1]/1000000.0)
+        print "White shot read values: sens = %d, exp time = %.2fms\n" % (
+                r_iso, r_exp/1000000.0)
 
         # Draw a plot.
-        pylab.plot([0,1], r_means, 'r')
-        pylab.plot([0,1], g_means, 'g')
-        pylab.plot([0,1], b_means, 'b')
-        pylab.ylim([0,1])
+        pylab.title("test_black_white")
+        pylab.plot([0, 1], r_means, "-ro")
+        pylab.plot([0, 1], g_means, "-go")
+        pylab.plot([0, 1], b_means, "-bo")
+        pylab.xlabel("Capture Number")
+        pylab.ylabel("Output Values (Normalized)")
+        pylab.ylim([0, 1])
         matplotlib.pyplot.savefig("%s_plot_means.png" % (NAME))
 
-        for val in black_means:
-            assert(val < 0.025)
-        for val in white_means:
-            assert(val > 0.975)
+        for black_mean in black_means:
+            assert black_mean < 0.025
+        for white_mean in white_means:
+            assert white_mean > 0.975
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
 
diff --git a/apps/CameraITS/tests/scene1/test_burst_sameness_manual.py b/apps/CameraITS/tests/scene1/test_burst_sameness_manual.py
index edb8995..b3fb606 100644
--- a/apps/CameraITS/tests/scene1/test_burst_sameness_manual.py
+++ b/apps/CameraITS/tests/scene1/test_burst_sameness_manual.py
@@ -12,14 +12,25 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import its.image
+import os.path
 import its.caps
 import its.device
+import its.image
 import its.objects
 import its.target
-import os.path
+
+from matplotlib import pylab
+import matplotlib.pyplot
 import numpy
 
+BURST_LEN = 50
+BURSTS = 5
+COLORS = ["R", "G", "B"]
+FRAMES = BURST_LEN * BURSTS
+NAME = os.path.basename(__file__).split(".")[0]
+SPREAD_THRESH = 0.03
+
+
 def main():
     """Take long bursts of images and check that they're all identical.
 
@@ -27,13 +38,6 @@
     frames that are processed differently or have artifacts. Uses manual
     capture settings.
     """
-    NAME = os.path.basename(__file__).split(".")[0]
-
-    BURST_LEN = 50
-    BURSTS = 5
-    FRAMES = BURST_LEN * BURSTS
-
-    SPREAD_THRESH = 0.03
 
     with its.device.ItsSession() as cam:
 
@@ -41,11 +45,12 @@
         props = cam.get_camera_properties()
         its.caps.skip_unless(its.caps.manual_sensor(props) and
                              its.caps.per_frame_control(props))
+        debug = its.caps.debug_mode()
 
         _, fmt = its.objects.get_fastest_manual_capture_settings(props)
         e, s = its.target.get_target_exposure_combos(cam)["minSensitivity"]
         req = its.objects.manual_capture_request(s, e)
-        w,h = fmt["width"], fmt["height"]
+        w, h = fmt["width"], fmt["height"]
 
         # Capture bursts of YUV shots.
         # Get the mean values of a center patch for each.
@@ -53,10 +58,10 @@
         r_means = []
         g_means = []
         b_means = []
-        imgs = numpy.empty([FRAMES,h,w,3])
+        imgs = numpy.empty([FRAMES, h, w, 3])
         for j in range(BURSTS):
             caps = cam.do_capture([req]*BURST_LEN, [fmt])
-            for i,cap in enumerate(caps):
+            for i, cap in enumerate(caps):
                 n = j*BURST_LEN + i
                 imgs[n] = its.image.convert_capture_to_rgb_image(cap)
                 tile = its.image.get_image_patch(imgs[n], 0.45, 0.45, 0.1, 0.1)
@@ -65,21 +70,35 @@
                 g_means.append(means[1])
                 b_means.append(means[2])
 
-        # Dump all images.
-        print "Dumping images"
-        for i in range(FRAMES):
-            its.image.write_image(imgs[i], "%s_frame%03d.jpg"%(NAME,i))
+        # Dump all images if debug
+        if debug:
+            print "Dumping images"
+            for i in range(FRAMES):
+                its.image.write_image(imgs[i], "%s_frame%03d.jpg"%(NAME, i))
 
         # The mean image.
         img_mean = imgs.mean(0)
         its.image.write_image(img_mean, "%s_mean.jpg"%(NAME))
 
-        # Pass/fail based on center patch similarity.
-        for means in [r_means, g_means, b_means]:
-            spread = max(means) - min(means)
-            print spread
-            assert(spread < SPREAD_THRESH)
+        # Plot means vs frames
+        frames = range(FRAMES)
+        pylab.title(NAME)
+        pylab.plot(frames, r_means, "-ro")
+        pylab.plot(frames, g_means, "-go")
+        pylab.plot(frames, b_means, "-bo")
+        pylab.ylim([0, 1])
+        pylab.xlabel("frame number")
+        pylab.ylabel("RGB avg [0, 1]")
+        matplotlib.pyplot.savefig("%s_plot_means.png" % (NAME))
 
-if __name__ == '__main__':
+        # PASS/FAIL based on center patch similarity.
+        for plane, means in enumerate([r_means, g_means, b_means]):
+            spread = max(means) - min(means)
+            msg = "%s spread: %.5f, SPREAD_THRESH: %.3f" % (
+                    COLORS[plane], spread, SPREAD_THRESH)
+            print msg
+            assert spread < SPREAD_THRESH, msg
+
+if __name__ == "__main__":
     main()
 
diff --git a/apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py b/apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py
index d087ab1..ec4b2fa 100644
--- a/apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py
+++ b/apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py
@@ -94,8 +94,12 @@
         luma_normal = lumas[imid] / shift_mid
         expected_lumas = [min(1.0, luma_normal * ev_shift) for ev_shift in ev_shifts]
 
-        pylab.plot(ev_steps, lumas, 'r')
-        pylab.plot(ev_steps, expected_lumas, 'b')
+        pylab.plot(ev_steps, lumas, '-ro')
+        pylab.plot(ev_steps, expected_lumas, '-bo')
+        pylab.title(NAME)
+        pylab.xlabel('EV Compensation')
+        pylab.ylabel('Mean Luma (Normalized)')
+
         matplotlib.pyplot.savefig("%s_plot_means.png" % (NAME))
 
         luma_diffs = [expected_lumas[i] - lumas[i] for i in range(len(ev_steps))]
diff --git a/apps/CameraITS/tests/scene1/test_ev_compensation_basic.py b/apps/CameraITS/tests/scene1/test_ev_compensation_basic.py
index 32e5001..3526465 100644
--- a/apps/CameraITS/tests/scene1/test_ev_compensation_basic.py
+++ b/apps/CameraITS/tests/scene1/test_ev_compensation_basic.py
@@ -91,6 +91,7 @@
             assert caps[THRESH_CONVERGE_FOR_EV-1]['metadata']['android.control.aeState'] == LOCKED
 
         pylab.plot(evs, lumas, '-ro')
+        pylab.title(NAME)
         pylab.xlabel('EV Compensation')
         pylab.ylabel('Mean Luma (Normalized)')
         matplotlib.pyplot.savefig('%s_plot_means.png' % (NAME))
diff --git a/apps/CameraITS/tests/scene1/test_latching.py b/apps/CameraITS/tests/scene1/test_latching.py
index 79f0f1a..362b7b8 100644
--- a/apps/CameraITS/tests/scene1/test_latching.py
+++ b/apps/CameraITS/tests/scene1/test_latching.py
@@ -12,15 +12,19 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import its.image
+import os.path
+
 import its.caps
 import its.device
+import its.image
 import its.objects
 import its.target
-from matplotlib import pylab
-import os.path
+
 import matplotlib
-import matplotlib.pyplot
+from matplotlib import pylab
+
+NAME = os.path.basename(__file__).split('.')[0]
+
 
 def main():
     """Test that settings latch on the right frame.
@@ -29,14 +33,13 @@
     request parameters between shots. Checks that the images that come back
     have the expected properties.
     """
-    NAME = os.path.basename(__file__).split(".")[0]
 
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
         its.caps.skip_unless(its.caps.full_or_better(props))
 
-        _,fmt = its.objects.get_fastest_manual_capture_settings(props)
-        e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
+        _, fmt = its.objects.get_fastest_manual_capture_settings(props)
+        e, s = its.target.get_target_exposure_combos(cam)['midExposureTime']
         e /= 2.0
 
         r_means = []
@@ -44,26 +47,26 @@
         b_means = []
 
         reqs = [
-            its.objects.manual_capture_request(s,  e,   0.0, True, props),
-            its.objects.manual_capture_request(s,  e,   0.0, True, props),
-            its.objects.manual_capture_request(s*2,e,   0.0, True, props),
-            its.objects.manual_capture_request(s*2,e,   0.0, True, props),
-            its.objects.manual_capture_request(s,  e,   0.0, True, props),
-            its.objects.manual_capture_request(s,  e,   0.0, True, props),
-            its.objects.manual_capture_request(s,  e*2, 0.0, True, props),
-            its.objects.manual_capture_request(s,  e,   0.0, True, props),
-            its.objects.manual_capture_request(s*2,e,   0.0, True, props),
-            its.objects.manual_capture_request(s,  e,   0.0, True, props),
-            its.objects.manual_capture_request(s,  e*2, 0.0, True, props),
-            its.objects.manual_capture_request(s,  e,   0.0, True, props),
-            its.objects.manual_capture_request(s,  e*2, 0.0, True, props),
-            its.objects.manual_capture_request(s,  e*2, 0.0, True, props),
-            ]
+                its.objects.manual_capture_request(s, e, 0.0, True, props),
+                its.objects.manual_capture_request(s, e, 0.0, True, props),
+                its.objects.manual_capture_request(s*2, e, 0.0, True, props),
+                its.objects.manual_capture_request(s*2, e, 0.0, True, props),
+                its.objects.manual_capture_request(s, e, 0.0, True, props),
+                its.objects.manual_capture_request(s, e, 0.0, True, props),
+                its.objects.manual_capture_request(s, e*2, 0.0, True, props),
+                its.objects.manual_capture_request(s, e, 0.0, True, props),
+                its.objects.manual_capture_request(s*2, e, 0.0, True, props),
+                its.objects.manual_capture_request(s, e, 0.0, True, props),
+                its.objects.manual_capture_request(s, e*2, 0.0, True, props),
+                its.objects.manual_capture_request(s, e, 0.0, True, props),
+                its.objects.manual_capture_request(s, e*2, 0.0, True, props),
+                its.objects.manual_capture_request(s, e*2, 0.0, True, props),
+                ]
 
         caps = cam.do_capture(reqs, fmt)
-        for i,cap in enumerate(caps):
+        for i, cap in enumerate(caps):
             img = its.image.convert_capture_to_rgb_image(cap)
-            its.image.write_image(img, "%s_i=%02d.jpg" % (NAME, i))
+            its.image.write_image(img, '%s_i=%02d.jpg' % (NAME, i))
             tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
             rgb_means = its.image.compute_image_means(tile)
             r_means.append(rgb_means[0])
@@ -72,15 +75,18 @@
 
         # Draw a plot.
         idxs = range(len(r_means))
-        pylab.plot(idxs, r_means, 'r')
-        pylab.plot(idxs, g_means, 'g')
-        pylab.plot(idxs, b_means, 'b')
-        pylab.ylim([0,1])
-        matplotlib.pyplot.savefig("%s_plot_means.png" % (NAME))
+        pylab.plot(idxs, r_means, '-ro')
+        pylab.plot(idxs, g_means, '-go')
+        pylab.plot(idxs, b_means, '-bo')
+        pylab.ylim([0, 1])
+        pylab.title(NAME)
+        pylab.xlabel('capture')
+        pylab.ylabel('RGB means')
+        matplotlib.pyplot.savefig('%s_plot_means.png' % (NAME))
 
         g_avg = sum(g_means) / len(g_means)
         g_ratios = [g / g_avg for g in g_means]
-        g_hilo = [g>1.0 for g in g_ratios]
+        g_hilo = [g > 1.0 for g in g_ratios]
         assert(g_hilo == [False, False, True, True, False, False, True,
                           False, True, False, True, False, True, True])
 
diff --git a/apps/CameraITS/tests/scene1/test_locked_burst.py b/apps/CameraITS/tests/scene1/test_locked_burst.py
index befbbed..76a8203 100644
--- a/apps/CameraITS/tests/scene1/test_locked_burst.py
+++ b/apps/CameraITS/tests/scene1/test_locked_burst.py
@@ -12,15 +12,20 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import its.image
-import its.device
-import its.objects
-import its.caps
 import os.path
-import numpy
-from matplotlib import pylab
-import matplotlib
-import matplotlib.pyplot
+import its.caps
+import its.device
+import its.image
+import its.objects
+
+BURST_LEN = 8
+COLORS = ['R', 'G', 'B']
+FPS_MAX_DIFF = 2.0
+NAME = os.path.basename(__file__).split('.')[0]
+SPREAD_THRESH_MANUAL_SENSOR = 0.01
+SPREAD_THRESH = 0.03
+VALUE_THRESH = 0.1
+
 
 def main():
     """Test 3A lock + YUV burst (using auto settings).
@@ -29,12 +34,6 @@
     don't have MANUAL_SENSOR or PER_FRAME_CONTROLS. The test checks
     YUV image consistency while the frame rate check is in CTS.
     """
-    NAME = os.path.basename(__file__).split(".")[0]
-
-    BURST_LEN = 8
-    SPREAD_THRESH_MANUAL_SENSOR = 0.01
-    SPREAD_THRESH = 0.03
-    FPS_MAX_DIFF = 2.0
 
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
@@ -49,9 +48,10 @@
         fmt = its.objects.get_largest_yuv_format(props)
 
         # After 3A has converged, lock AE+AWB for the duration of the test.
+        print 'Locking AE & AWB'
         req = its.objects.fastest_auto_capture_request(props)
-        req["android.control.awbLock"] = True
-        req["android.control.aeLock"] = True
+        req['android.control.awbLock'] = True
+        req['android.control.aeLock'] = True
 
         # Capture bursts of YUV shots.
         # Get the mean values of a center patch for each.
@@ -59,23 +59,32 @@
         g_means = []
         b_means = []
         caps = cam.do_capture([req]*BURST_LEN, fmt)
-        for i,cap in enumerate(caps):
+        for i, cap in enumerate(caps):
             img = its.image.convert_capture_to_rgb_image(cap)
-            its.image.write_image(img, "%s_frame%d.jpg"%(NAME,i))
+            its.image.write_image(img, '%s_frame%d.jpg'%(NAME, i))
             tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
             means = its.image.compute_image_means(tile)
             r_means.append(means[0])
             g_means.append(means[1])
             b_means.append(means[2])
 
-        # Pass/fail based on center patch similarity.
-        for means in [r_means, g_means, b_means]:
-            spread = max(means) - min(means)
-            print "Patch mean spread", spread, \
-                    " (min/max: ",  min(means), "/", max(means), ")"
+        # Assert center patch brightness & similarity
+        for i, means in enumerate([r_means, g_means, b_means]):
+            plane = COLORS[i]
+            min_means = min(means)
+            spread = max(means) - min_means
+            print '%s patch mean spread %.5f. means = [' % (plane, spread),
+            for j in range(BURST_LEN):
+                print '%.5f' % means[j],
+            print ']'
+            e_msg = 'Image too dark!  %s: %.5f, THRESH: %.2f' % (
+                    plane, min_means, VALUE_THRESH)
+            assert min_means > VALUE_THRESH, e_msg
             threshold = SPREAD_THRESH_MANUAL_SENSOR \
                     if its.caps.manual_sensor(props) else SPREAD_THRESH
-            assert(spread < threshold)
+            e_msg = '%s center patch spread: %.5f, THRESH: %.2f' % (
+                    plane, spread, threshold)
+            assert spread < threshold, e_msg
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CameraITS/tests/scene1/test_param_color_correction.py b/apps/CameraITS/tests/scene1/test_param_color_correction.py
index 83f4f7f..96962f0 100644
--- a/apps/CameraITS/tests/scene1/test_param_color_correction.py
+++ b/apps/CameraITS/tests/scene1/test_param_color_correction.py
@@ -12,15 +12,20 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import its.image
+import os.path
+
 import its.caps
 import its.device
+import its.image
 import its.objects
 import its.target
-from matplotlib import pylab
-import os.path
+
 import matplotlib
-import matplotlib.pyplot
+from matplotlib import pylab
+
+NAME = os.path.basename(__file__).split('.')[0]
+THRESHOLD_MAX_DIFF = 0.1
+
 
 def main():
     """Test that the android.colorCorrection.* params are applied when set.
@@ -31,9 +36,6 @@
 
     Uses a linear tonemap.
     """
-    NAME = os.path.basename(__file__).split(".")[0]
-
-    THRESHOLD_MAX_DIFF = 0.1
 
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
@@ -50,23 +52,23 @@
             match_ar = (largest_yuv['width'], largest_yuv['height'])
             fmt = its.objects.get_smallest_yuv_format(props, match_ar=match_ar)
 
-        e, s = its.target.get_target_exposure_combos(cam)["midSensitivity"]
+        e, s = its.target.get_target_exposure_combos(cam)['midSensitivity']
         req = its.objects.manual_capture_request(s, e, 0.0, True, props)
-        req["android.colorCorrection.mode"] = 0
+        req['android.colorCorrection.mode'] = 0
 
         # Transforms:
         # 1. Identity
         # 2. Identity
         # 3. Boost blue
-        transforms = [its.objects.int_to_rational([1,0,0, 0,1,0, 0,0,1]),
-                      its.objects.int_to_rational([1,0,0, 0,1,0, 0,0,1]),
-                      its.objects.int_to_rational([1,0,0, 0,1,0, 0,0,2])]
+        transforms = [its.objects.int_to_rational([1, 0, 0, 0, 1, 0, 0, 0, 1]),
+                      its.objects.int_to_rational([1, 0, 0, 0, 1, 0, 0, 0, 1]),
+                      its.objects.int_to_rational([1, 0, 0, 0, 1, 0, 0, 0, 2])]
 
         # Gains:
         # 1. Unit
         # 2. Boost red
         # 3. Unit
-        gains = [[1,1,1,1], [2,1,1,1], [1,1,1,1]]
+        gains = [[1, 1, 1, 1], [2, 1, 1, 1], [1, 1, 1, 1]]
 
         r_means = []
         g_means = []
@@ -77,36 +79,39 @@
         # 2. With a higher red gain, and identity transform.
         # 3. With unit gains, and a transform that boosts blue.
         for i in range(len(transforms)):
-            req["android.colorCorrection.transform"] = transforms[i]
-            req["android.colorCorrection.gains"] = gains[i]
+            req['android.colorCorrection.transform'] = transforms[i]
+            req['android.colorCorrection.gains'] = gains[i]
             cap = cam.do_capture(req, fmt)
             img = its.image.convert_capture_to_rgb_image(cap)
-            its.image.write_image(img, "%s_req=%d.jpg" % (NAME, i))
+            its.image.write_image(img, '%s_req=%d.jpg' % (NAME, i))
             tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
             rgb_means = its.image.compute_image_means(tile)
             r_means.append(rgb_means[0])
             g_means.append(rgb_means[1])
             b_means.append(rgb_means[2])
             ratios = [rgb_means[0] / rgb_means[1], rgb_means[2] / rgb_means[1]]
-            print "Means = ", rgb_means, "   Ratios =", ratios
+            print 'Means = ', rgb_means, '   Ratios =', ratios
 
         # Draw a plot.
         domain = range(len(transforms))
-        pylab.plot(domain, r_means, 'r')
-        pylab.plot(domain, g_means, 'g')
-        pylab.plot(domain, b_means, 'b')
-        pylab.ylim([0,1])
-        matplotlib.pyplot.savefig("%s_plot_means.png" % (NAME))
+        pylab.plot(domain, r_means, '-ro')
+        pylab.plot(domain, g_means, '-go')
+        pylab.plot(domain, b_means, '-bo')
+        pylab.ylim([0, 1])
+        pylab.title(NAME)
+        pylab.xlabel('Unity, R boost, B boost')
+        pylab.ylabel('RGB means')
+        matplotlib.pyplot.savefig('%s_plot_means.png' % (NAME))
 
         # Expect G0 == G1 == G2, R0 == 0.5*R1 == R2, B0 == B1 == 0.5*B2
         # Also need to ensure that the image is not clamped to white/black.
-        assert(all(g_means[i] > 0.2 and g_means[i] < 0.8 for i in xrange(3)))
-        assert(abs(g_means[1] - g_means[0]) < THRESHOLD_MAX_DIFF)
-        assert(abs(g_means[2] - g_means[1]) < THRESHOLD_MAX_DIFF)
-        assert(abs(r_means[2] - r_means[0]) < THRESHOLD_MAX_DIFF)
-        assert(abs(r_means[1] - 2.0 * r_means[0]) < THRESHOLD_MAX_DIFF)
-        assert(abs(b_means[1] - b_means[0]) < THRESHOLD_MAX_DIFF)
-        assert(abs(b_means[2] - 2.0 * b_means[0]) < THRESHOLD_MAX_DIFF)
+        assert all(g_means[i] > 0.2 and g_means[i] < 0.8 for i in xrange(3))
+        assert abs(g_means[1] - g_means[0]) < THRESHOLD_MAX_DIFF
+        assert abs(g_means[2] - g_means[1]) < THRESHOLD_MAX_DIFF
+        assert abs(r_means[2] - r_means[0]) < THRESHOLD_MAX_DIFF
+        assert abs(r_means[1] - 2.0 * r_means[0]) < THRESHOLD_MAX_DIFF
+        assert abs(b_means[1] - b_means[0]) < THRESHOLD_MAX_DIFF
+        assert abs(b_means[2] - 2.0 * b_means[0]) < THRESHOLD_MAX_DIFF
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CameraITS/tests/scene1/test_param_exposure_time.py b/apps/CameraITS/tests/scene1/test_param_exposure_time.py
index 90ad0b6..8df412c 100644
--- a/apps/CameraITS/tests/scene1/test_param_exposure_time.py
+++ b/apps/CameraITS/tests/scene1/test_param_exposure_time.py
@@ -12,20 +12,22 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import its.image
+import os.path
+
 import its.caps
 import its.device
+import its.image
 import its.objects
 import its.target
-from matplotlib import pylab
-import os.path
+
 import matplotlib
-import matplotlib.pyplot
+from matplotlib import pylab
+
+NAME = os.path.basename(__file__).split('.')[0]
+
 
 def main():
-    """Test that the android.sensor.exposureTime parameter is applied.
-    """
-    NAME = os.path.basename(__file__).split(".")[0]
+    """Test that the android.sensor.exposureTime parameter is applied."""
 
     exp_times = []
     r_means = []
@@ -45,13 +47,14 @@
             match_ar = (largest_yuv['width'], largest_yuv['height'])
             fmt = its.objects.get_smallest_yuv_format(props, match_ar=match_ar)
 
-        e,s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
-        for i,e_mult in enumerate([0.8, 0.9, 1.0, 1.1, 1.2]):
-            req = its.objects.manual_capture_request(s, e * e_mult, 0.0, True, props)
+        e, s = its.target.get_target_exposure_combos(cam)['midExposureTime']
+        for i, e_mult in enumerate([0.8, 0.9, 1.0, 1.1, 1.2]):
+            req = its.objects.manual_capture_request(
+                    s, e * e_mult, 0.0, True, props)
             cap = cam.do_capture(req, fmt)
             img = its.image.convert_capture_to_rgb_image(cap)
             its.image.write_image(
-                    img, "%s_frame%d.jpg" % (NAME, i))
+                    img, '%s_frame%d.jpg' % (NAME, i))
             tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
             rgb_means = its.image.compute_image_means(tile)
             exp_times.append(e * e_mult)
@@ -60,16 +63,19 @@
             b_means.append(rgb_means[2])
 
     # Draw a plot.
-    pylab.plot(exp_times, r_means, 'r')
-    pylab.plot(exp_times, g_means, 'g')
-    pylab.plot(exp_times, b_means, 'b')
-    pylab.ylim([0,1])
-    matplotlib.pyplot.savefig("%s_plot_means.png" % (NAME))
+    pylab.plot(exp_times, r_means, '-ro')
+    pylab.plot(exp_times, g_means, '-go')
+    pylab.plot(exp_times, b_means, '-bo')
+    pylab.ylim([0, 1])
+    pylab.title(NAME)
+    pylab.xlabel('Exposure times (ns)')
+    pylab.ylabel('RGB means')
+    matplotlib.pyplot.savefig('%s_plot_means.png' % (NAME))
 
     # Test for pass/fail: check that each shot is brighter than the previous.
     for means in [r_means, g_means, b_means]:
         for i in range(len(means)-1):
-            assert(means[i+1] > means[i])
+            assert means[i+1] > means[i]
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CameraITS/tests/scene1/test_param_sensitivity.py b/apps/CameraITS/tests/scene1/test_param_sensitivity.py
index 0d40042..84d1226 100644
--- a/apps/CameraITS/tests/scene1/test_param_sensitivity.py
+++ b/apps/CameraITS/tests/scene1/test_param_sensitivity.py
@@ -12,22 +12,23 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import its.image
+import os.path
+
 import its.caps
 import its.device
+import its.image
 import its.objects
 import its.target
-from matplotlib import pylab
-import os.path
+
 import matplotlib
-import matplotlib.pyplot
+from matplotlib import pylab
+
+NAME = os.path.basename(__file__).split('.')[0]
+NUM_STEPS = 5
+
 
 def main():
-    """Test that the android.sensor.sensitivity parameter is applied.
-    """
-    NAME = os.path.basename(__file__).split(".")[0]
-
-    NUM_STEPS = 5
+    """Test that the android.sensor.sensitivity parameter is applied."""
 
     sensitivities = None
     r_means = []
@@ -47,17 +48,18 @@
             match_ar = (largest_yuv['width'], largest_yuv['height'])
             fmt = its.objects.get_smallest_yuv_format(props, match_ar=match_ar)
 
-        expt,_ = its.target.get_target_exposure_combos(cam)["midSensitivity"]
+        expt, _ = its.target.get_target_exposure_combos(cam)['midSensitivity']
         sens_range = props['android.sensor.info.sensitivityRange']
         sens_step = (sens_range[1] - sens_range[0]) / float(NUM_STEPS-1)
-        sensitivities = [sens_range[0] + i * sens_step for i in range(NUM_STEPS)]
+        sensitivities = [
+                sens_range[0] + i * sens_step for i in range(NUM_STEPS)]
 
         for s in sensitivities:
             req = its.objects.manual_capture_request(s, expt)
             cap = cam.do_capture(req, fmt)
             img = its.image.convert_capture_to_rgb_image(cap)
             its.image.write_image(
-                    img, "%s_iso=%04d.jpg" % (NAME, s))
+                    img, '%s_iso=%04d.jpg' % (NAME, s))
             tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
             rgb_means = its.image.compute_image_means(tile)
             r_means.append(rgb_means[0])
@@ -65,16 +67,19 @@
             b_means.append(rgb_means[2])
 
     # Draw a plot.
-    pylab.plot(sensitivities, r_means, 'r')
-    pylab.plot(sensitivities, g_means, 'g')
-    pylab.plot(sensitivities, b_means, 'b')
-    pylab.ylim([0,1])
-    matplotlib.pyplot.savefig("%s_plot_means.png" % (NAME))
+    pylab.plot(sensitivities, r_means, '-ro')
+    pylab.plot(sensitivities, g_means, '-go')
+    pylab.plot(sensitivities, b_means, '-bo')
+    pylab.ylim([0, 1])
+    pylab.title(NAME)
+    pylab.xlabel('Gain (ISO)')
+    pylab.ylabel('RGB means')
+    matplotlib.pyplot.savefig('%s_plot_means.png' % (NAME))
 
     # Test for pass/fail: check that each shot is brighter than the previous.
     for means in [r_means, g_means, b_means]:
         for i in range(len(means)-1):
-            assert(means[i+1] > means[i])
+            assert means[i+1] > means[i]
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CameraITS/tests/scene1/test_param_shading_mode.py b/apps/CameraITS/tests/scene1/test_param_shading_mode.py
index 45c9a12..85bacf0 100644
--- a/apps/CameraITS/tests/scene1/test_param_shading_mode.py
+++ b/apps/CameraITS/tests/scene1/test_param_shading_mode.py
@@ -12,15 +12,21 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import os
+
 import its.caps
 import its.device
 import its.image
 import its.objects
+
 import matplotlib
-import numpy
-import os
-import os.path
 from matplotlib import pylab
+import numpy
+
+NAME = os.path.basename(__file__).split('.')[0]
+NUM_SHADING_MODE_SWITCH_LOOPS = 3
+THRESHOLD_DIFF_RATIO = 0.15
+
 
 def main():
     """Test that the android.shading.mode param is applied.
@@ -28,10 +34,6 @@
     Switching shading modes and checks that the lens shading maps are
     modified as expected.
     """
-    NAME = os.path.basename(__file__).split(".")[0]
-
-    NUM_SHADING_MODE_SWITCH_LOOPS = 3
-    THRESHOLD_DIFF_RATIO = 0.15
 
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
@@ -43,8 +45,8 @@
         mono_camera = its.caps.mono_camera(props)
 
         # lsc_off devices should always support OFF(0), FAST(1), and HQ(2)
-        assert(props.has_key("android.shading.availableModes") and
-               set(props["android.shading.availableModes"]) == set([0, 1, 2]))
+        assert(props.has_key('android.shading.availableModes') and
+               set(props['android.shading.availableModes']) == set([0, 1, 2]))
 
         # Test 1: Switching shading modes several times and verify:
         #   1. Lens shading maps with mode OFF are all 1.0
@@ -52,7 +54,7 @@
         #      shading modes.
         #   3. Lens shading maps with mode HIGH_QUALITY are similar after
         #      switching shading modes.
-        cam.do_3a(mono_camera=mono_camera);
+        cam.do_3a(mono_camera=mono_camera)
 
         # Get the reference lens shading maps for OFF, FAST, and HIGH_QUALITY
         # in different sessions.
@@ -60,57 +62,60 @@
         reference_maps = [[] for mode in range(3)]
         num_map_gains = 0
         for mode in range(1, 3):
-            req = its.objects.auto_capture_request();
-            req["android.statistics.lensShadingMapMode"] = 1
-            req["android.shading.mode"] = mode
-            cap_res = cam.do_capture(req)["metadata"]
-            lsc_map = cap_res["android.statistics.lensShadingCorrectionMap"]
-            assert(lsc_map.has_key("width") and
-                   lsc_map.has_key("height") and
-                   lsc_map["width"] != None and lsc_map["height"] != None)
+            req = its.objects.auto_capture_request()
+            req['android.statistics.lensShadingMapMode'] = 1
+            req['android.shading.mode'] = mode
+            cap_res = cam.do_capture(req)['metadata']
+            lsc_map = cap_res['android.statistics.lensShadingCorrectionMap']
+            assert(lsc_map.has_key('width') and
+                   lsc_map.has_key('height') and
+                   lsc_map['width'] is not None and
+                   lsc_map['height'] is not None)
             if mode == 1:
-                num_map_gains = lsc_map["width"] * lsc_map["height"] * 4
+                num_map_gains = lsc_map['width'] * lsc_map['height'] * 4
                 reference_maps[0] = [1.0] * num_map_gains
-            reference_maps[mode] = lsc_map["map"]
+            reference_maps[mode] = lsc_map['map']
 
         # Get the lens shading maps while switching modes in one session.
         reqs = []
         for i in range(NUM_SHADING_MODE_SWITCH_LOOPS):
             for mode in range(3):
-                req = its.objects.auto_capture_request();
-                req["android.statistics.lensShadingMapMode"] = 1
-                req["android.shading.mode"] = mode
-                reqs.append(req);
+                req = its.objects.auto_capture_request()
+                req['android.statistics.lensShadingMapMode'] = 1
+                req['android.shading.mode'] = mode
+                reqs.append(req)
 
         caps = cam.do_capture(reqs)
 
         # shading_maps[mode][loop]
         shading_maps = [[[] for loop in range(NUM_SHADING_MODE_SWITCH_LOOPS)]
-                for mode in range(3)]
+                        for mode in range(3)]
 
         # Get the shading maps out of capture results
         for i in range(len(caps)):
             shading_maps[i % 3][i / 3] = \
-                    caps[i]["metadata"] \
-                    ["android.statistics.lensShadingCorrectionMap"]["map"]
+                    caps[i]['metadata']['android.statistics.lensShadingCorrectionMap']['map']
 
         # Draw the maps
         for mode in range(3):
             for i in range(NUM_SHADING_MODE_SWITCH_LOOPS):
                 pylab.clf()
-                pylab.plot(range(num_map_gains), shading_maps[mode][i], 'r')
-                pylab.plot(range(num_map_gains), reference_maps[mode], 'g')
+                pylab.plot(range(num_map_gains), shading_maps[mode][i], '-ro')
+                pylab.plot(range(num_map_gains), reference_maps[mode], '-go')
                 pylab.xlim([0, num_map_gains])
                 pylab.ylim([0.9, 4.0])
-                matplotlib.pyplot.savefig("%s_ls_maps_mode_%d_loop_%d.png" %
-                                          (NAME, mode, i))
+                name = '%s_ls_maps_mode_%d_loop_%d' % (NAME, mode, i)
+                pylab.title(name)
+                pylab.xlabel('Map gains')
+                pylab.ylabel('Lens shading maps')
+                matplotlib.pyplot.savefig('%s.png' % name)
 
-        print "Verifying lens shading maps with mode OFF are all 1.0"
+        print 'Verifying lens shading maps with mode OFF are all 1.0'
         for i in range(NUM_SHADING_MODE_SWITCH_LOOPS):
-            assert(numpy.allclose(shading_maps[0][i], reference_maps[0]))
+            assert numpy.allclose(shading_maps[0][i], reference_maps[0])
 
         for mode in range(1, 3):
-            print "Verifying lens shading maps with mode", mode, "are similar"
+            print 'Verifying lens shading maps with mode', mode, 'are similar'
             for i in range(NUM_SHADING_MODE_SWITCH_LOOPS):
                 assert(numpy.allclose(shading_maps[mode][i],
                                       reference_maps[mode],
diff --git a/apps/CameraITS/tests/scene1/test_post_raw_sensitivity_boost.py b/apps/CameraITS/tests/scene1/test_post_raw_sensitivity_boost.py
index 73c001d..8b3ef86 100644
--- a/apps/CameraITS/tests/scene1/test_post_raw_sensitivity_boost.py
+++ b/apps/CameraITS/tests/scene1/test_post_raw_sensitivity_boost.py
@@ -12,28 +12,31 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import its.device
+import os.path
+
 import its.caps
+import its.device
 import its.image
 import its.objects
 import its.target
-import os.path
-from matplotlib import pylab
+
 import matplotlib
-import matplotlib.pyplot
+from matplotlib import pylab
+
+NAME = os.path.basename(__file__).split('.')[0]
+RATIO_THRESHOLD = 0.1  # Each raw image
+# Waive the check if raw pixel value is below this level (signal too small
+# that small black level error converts to huge error in percentage)
+RAW_PIXEL_VAL_THRESHOLD = 0.03
+
 
 def main():
-    """Capture a set of raw/yuv images with different
-        sensitivity/post Raw sensitivity boost combination
+    """Check post RAW sensitivity boost.
+
+        Capture a set of raw/yuv images with different
+        sensitivity/post RAW sensitivity boost combination
         and check if the output pixel mean matches request settings
     """
-    NAME = os.path.basename(__file__).split(".")[0]
-
-    # Each raw image
-    RATIO_THRESHOLD = 0.1
-    # Waive the check if raw pixel value is below this level (signal too small
-    # that small black level error converts to huge error in percentage)
-    RAW_PIXEL_VAL_THRESHOLD = 0.03
 
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
@@ -43,8 +46,8 @@
                              its.caps.per_frame_control(props) and
                              not its.caps.mono_camera(props))
 
-        w,h = its.objects.get_available_output_sizes(
-                "yuv", props, (1920, 1080))[0]
+        w, h = its.objects.get_available_output_sizes(
+                'yuv', props, (1920, 1080))[0]
 
         if its.caps.raw16(props):
             raw_format = 'raw'
@@ -52,19 +55,18 @@
             raw_format = 'raw10'
         elif its.caps.raw12(props):
             raw_format = 'raw12'
-        else: # should not reach here
+        else:  # should not reach here
             raise its.error.Error('Cannot find available RAW output format')
 
-        out_surfaces = [{"format": raw_format},
-                        {"format": "yuv", "width": w, "height": h}]
+        out_surfaces = [{'format': raw_format},
+                        {'format': 'yuv', 'width': w, 'height': h}]
 
         sens_min, sens_max = props['android.sensor.info.sensitivityRange']
         sens_boost_min, sens_boost_max = \
                 props['android.control.postRawSensitivityBoostRange']
 
-
         e_target, s_target = \
-                its.target.get_target_exposure_combos(cam)["midSensitivity"]
+                its.target.get_target_exposure_combos(cam)['midSensitivity']
 
         reqs = []
         settings = []
@@ -97,51 +99,60 @@
             (s, s_boost) = settings[i]
             raw_cap = raw_caps[i]
             yuv_cap = yuv_caps[i]
-            raw_rgb = its.image.convert_capture_to_rgb_image(raw_cap, props=props)
+            raw_rgb = its.image.convert_capture_to_rgb_image(
+                    raw_cap, props=props)
             yuv_rgb = its.image.convert_capture_to_rgb_image(yuv_cap)
-            raw_tile = its.image.get_image_patch(raw_rgb, 0.45,0.45,0.1,0.1)
-            yuv_tile = its.image.get_image_patch(yuv_rgb, 0.45,0.45,0.1,0.1)
+            raw_tile = its.image.get_image_patch(raw_rgb, 0.45, 0.45, 0.1, 0.1)
+            yuv_tile = its.image.get_image_patch(yuv_rgb, 0.45, 0.45, 0.1, 0.1)
             raw_rgb_means.append(its.image.compute_image_means(raw_tile))
             yuv_rgb_means.append(its.image.compute_image_means(yuv_tile))
-            its.image.write_image(raw_tile,
-                    "%s_raw_s=%04d_boost=%04d.jpg" % (NAME,s,s_boost))
-            its.image.write_image(yuv_tile,
-                    "%s_yuv_s=%04d_boost=%04d.jpg" % (NAME,s,s_boost))
-            print "s=%d, s_boost=%d: raw_means %s, yuv_means %s"%(
-                    s,s_boost,raw_rgb_means[-1], yuv_rgb_means[-1])
+            its.image.write_image(raw_tile, '%s_raw_s=%04d_boost=%04d.jpg' % (
+                    NAME, s, s_boost))
+            its.image.write_image(yuv_tile, '%s_yuv_s=%04d_boost=%04d.jpg' % (
+                    NAME, s, s_boost))
+            print 's=%d, s_boost=%d: raw_means %s, yuv_means %s'%(
+                    s, s_boost, raw_rgb_means[-1], yuv_rgb_means[-1])
 
         xs = range(len(reqs))
-        pylab.plot(xs, [rgb[0] for rgb in raw_rgb_means], 'r')
-        pylab.plot(xs, [rgb[1] for rgb in raw_rgb_means], 'g')
-        pylab.plot(xs, [rgb[2] for rgb in raw_rgb_means], 'b')
-        pylab.ylim([0,1])
-        matplotlib.pyplot.savefig("%s_raw_plot_means.png" % (NAME))
+        pylab.plot(xs, [rgb[0] for rgb in raw_rgb_means], '-ro')
+        pylab.plot(xs, [rgb[1] for rgb in raw_rgb_means], '-go')
+        pylab.plot(xs, [rgb[2] for rgb in raw_rgb_means], '-bo')
+        pylab.ylim([0, 1])
+        name = '%s_raw_plot_means' % NAME
+        pylab.title(name)
+        pylab.xlabel('requests')
+        pylab.ylabel('RGB means')
+        matplotlib.pyplot.savefig('%s.png' % name)
         pylab.clf()
-        pylab.plot(xs, [rgb[0] for rgb in yuv_rgb_means], 'r')
-        pylab.plot(xs, [rgb[1] for rgb in yuv_rgb_means], 'g')
-        pylab.plot(xs, [rgb[2] for rgb in yuv_rgb_means], 'b')
-        pylab.ylim([0,1])
-        matplotlib.pyplot.savefig("%s_yuv_plot_means.png" % (NAME))
+        pylab.plot(xs, [rgb[0] for rgb in yuv_rgb_means], '-ro')
+        pylab.plot(xs, [rgb[1] for rgb in yuv_rgb_means], '-go')
+        pylab.plot(xs, [rgb[2] for rgb in yuv_rgb_means], '-bo')
+        pylab.ylim([0, 1])
+        name = '%s_yuv_plot_means' % NAME
+        pylab.title(name)
+        pylab.xlabel('requests')
+        pylab.ylabel('RGB means')
+        matplotlib.pyplot.savefig('%s.png' % name)
 
-        rgb_str = ["R", "G", "B"]
+        rgb_str = ['R', 'G', 'B']
         # Test that raw means is about 2x brighter than next step
         for step in range(1, len(reqs)):
-            (s_prev, s_boost_prev) = settings[step - 1]
+            (s_prev, _) = settings[step - 1]
             (s, s_boost) = settings[step]
             expect_raw_ratio = s_prev / float(s)
             raw_thres_min = expect_raw_ratio * (1 - RATIO_THRESHOLD)
             raw_thres_max = expect_raw_ratio * (1 + RATIO_THRESHOLD)
             for rgb in range(3):
                 ratio = raw_rgb_means[step - 1][rgb] / raw_rgb_means[step][rgb]
-                print ("Step (%d,%d) %s channel: %f, %f, ratio %f," +
-                       " threshold_min %f, threshold_max %f") % (
+                print 'Step (%d,%d) %s channel: %f, %f, ratio %f,' % (
                         step-1, step, rgb_str[rgb],
                         raw_rgb_means[step - 1][rgb],
-                        raw_rgb_means[step][rgb],
-                        ratio, raw_thres_min, raw_thres_max)
-                if (raw_rgb_means[step][rgb] <= RAW_PIXEL_VAL_THRESHOLD):
+                        raw_rgb_means[step][rgb], ratio),
+                print 'threshold_min %f, threshold_max %f' % (
+                        raw_thres_min, raw_thres_max)
+                if raw_rgb_means[step][rgb] <= RAW_PIXEL_VAL_THRESHOLD:
                     continue
-                assert(raw_thres_min < ratio < raw_thres_max)
+                assert raw_thres_min < ratio < raw_thres_max
 
         # Test that each yuv step is about the same bright as their mean
         yuv_thres_min = 1 - RATIO_THRESHOLD
@@ -149,13 +160,13 @@
         for rgb in range(3):
             vals = [val[rgb] for val in yuv_rgb_means]
             for step in range(len(reqs)):
-                if (raw_rgb_means[step][rgb] <= RAW_PIXEL_VAL_THRESHOLD):
+                if raw_rgb_means[step][rgb] <= RAW_PIXEL_VAL_THRESHOLD:
                     vals = vals[:step]
             mean = sum(vals) / len(vals)
-            print "%s channel vals %s mean %f"%(rgb_str[rgb], vals, mean)
+            print '%s channel vals %s mean %f'%(rgb_str[rgb], vals, mean)
             for step in range(len(vals)):
                 ratio = vals[step] / mean
-                assert(yuv_thres_min < ratio < yuv_thres_max)
+                assert yuv_thres_min < ratio < yuv_thres_max
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py b/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
index 049426a..c565fa0 100644
--- a/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
+++ b/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
@@ -12,23 +12,46 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import its.image
+import os.path
+
 import its.caps
 import its.device
+import its.image
 import its.objects
 import its.target
-import math
+
 import matplotlib
-import matplotlib.pyplot
-import numpy
-import os.path
 from matplotlib import pylab
+import numpy
+
+NAME = os.path.basename(__file__).split(".")[0]
+NUM_SAMPLES = 4
+THRESH_REL_SHARPNESS_DIFF = 0.15
+
+
+def check_edge_modes(sharpness):
+    """Check that the sharpness for the different edge modes is correct."""
+    print " Verify HQ(2) is sharper than OFF(0)"
+    assert sharpness[2] > sharpness[0]
+
+    print " Verify ZSL(3) is similar to OFF(0)"
+    e_msg = "ZSL: %.5f, OFF: %.5f, RTOL: %.2f" % (
+            sharpness[3], sharpness[0], THRESH_REL_SHARPNESS_DIFF)
+    assert numpy.isclose(sharpness[3], sharpness[0],
+                         THRESH_REL_SHARPNESS_DIFF), e_msg
+
+    print " Verify OFF(0) is not sharper than FAST(1)"
+    assert sharpness[1] > sharpness[0] * (1.0 - THRESH_REL_SHARPNESS_DIFF)
+
+    print " Verify FAST(1) is not sharper than HQ(2)"
+    assert sharpness[2] > sharpness[1] * (1.0 - THRESH_REL_SHARPNESS_DIFF)
 
 
 def test_edge_mode(cam, edge_mode, sensitivity, exp, fd, out_surface,
                    reprocess_format=None):
-    """Return sharpness of the output image and the capture result metadata
-       for a capture request with the given edge mode, sensitivity, exposure
+    """Return sharpness of the output images and the capture result metadata.
+
+       Processes a capture request with a given edge mode, sensitivity, exposure
        time, focus distance, output surface parameter, and reprocess format
        (None for a regular request.)
 
@@ -41,7 +64,7 @@
             android.sensor.exposureTime.
         fd: Focus distance for the request as defined in
             android.lens.focusDistance
-        output_surface: Specifications of the output image format and size.
+        out_surface: Specifications of the output image format and size.
         reprocess_format: (Optional) The reprocessing format. If not None,
                 reprocessing will be enabled.
 
@@ -52,23 +75,20 @@
             "sharpness"
     """
 
-    NAME = os.path.basename(__file__).split(".")[0]
-    NUM_SAMPLES = 4
-
     req = its.objects.manual_capture_request(sensitivity, exp)
     req["android.lens.focusDistance"] = fd
     req["android.edge.mode"] = edge_mode
-    if (reprocess_format != None):
+    if reprocess_format:
         req["android.reprocess.effectiveExposureFactor"] = 1.0
 
     sharpness_list = []
+    caps = cam.do_capture([req]*NUM_SAMPLES, [out_surface], reprocess_format)
     for n in range(NUM_SAMPLES):
-        cap = cam.do_capture(req, out_surface, reprocess_format)
-        img = its.image.convert_capture_to_rgb_image(cap)
+        img = its.image.convert_capture_to_rgb_image(caps[n])
         if n == 0:
             its.image.write_image(img, "%s_reprocess_fmt_%s_edge=%d.jpg" %
-                (NAME, reprocess_format, edge_mode))
-            res_edge_mode = cap["metadata"]["android.edge.mode"]
+                                  (NAME, reprocess_format, edge_mode))
+            res_edge_mode = caps[n]["metadata"]["android.edge.mode"]
         tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
         sharpness_list.append(its.image.compute_image_sharpness(tile))
 
@@ -78,9 +98,9 @@
 
     return ret
 
+
 def main():
-    """Test that the android.edge.mode param is applied when set for
-       reprocessing requests.
+    """Test android.edge.mode param applied when set for reprocessing requests.
 
     Capture non-reprocess images for each edge mode and calculate their
     sharpness as a baseline.
@@ -90,8 +110,6 @@
     the sharpess of non-reprocess images.
     """
 
-    THRESHOLD_RELATIVE_SHARPNESS_DIFF = 0.15
-
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
 
@@ -103,19 +121,23 @@
 
         mono_camera = its.caps.mono_camera(props)
         # If reprocessing is supported, ZSL EE mode must be avaiable.
-        assert(its.caps.edge_mode(props, 3))
+        assert its.caps.edge_mode(props, 3), "EE mode not available!"
 
         reprocess_formats = []
-        if (its.caps.yuv_reprocess(props)):
+        if its.caps.yuv_reprocess(props):
             reprocess_formats.append("yuv")
-        if (its.caps.private_reprocess(props)):
+        if its.caps.private_reprocess(props):
             reprocess_formats.append("private")
 
         size = its.objects.get_available_output_sizes("jpg", props)[0]
-        out_surface = {"width":size[0], "height":size[1], "format":"jpg"}
+        out_surface = {"width": size[0], "height": size[1], "format": "jpg"}
 
         # Get proper sensitivity, exposure time, and focus distance.
-        s,e,_,_,fd = cam.do_3a(get_results=True, mono_camera=mono_camera)
+        s, e, _, _, fd = cam.do_3a(get_results=True, mono_camera=mono_camera)
+
+        # Intialize plot
+        pylab.figure("reprocess_result")
+        gr_color = {"yuv": "r", "private": "g", "none": "b"}
 
         # Get the sharpness for each edge mode for regular requests
         sharpness_regular = []
@@ -130,8 +152,11 @@
             edge_mode_reported_regular.append(ret["edge_mode"])
             sharpness_regular.append(ret["sharpness"])
 
-        print "Reported edge modes:", edge_mode_reported_regular
+        pylab.plot(range(4), sharpness_regular, "-"+gr_color["none"]+"o")
+        print "Reported edge modes",
+        print "regular requests:", edge_mode_reported_regular
         print "Sharpness with EE mode [0,1,2,3]:", sharpness_regular
+        print ""
 
         # Get the sharpness for each reprocess format and edge mode for
         # reprocess requests.
@@ -150,60 +175,44 @@
                     continue
 
                 ret = test_edge_mode(cam, edge_mode, s, e, fd, out_surface,
-                    reprocess_format)
+                                     reprocess_format)
                 edge_mode_reported.append(ret["edge_mode"])
                 sharpnesses.append(ret["sharpness"])
 
             sharpnesses_reprocess.append(sharpnesses)
             edge_mode_reported_reprocess.append(edge_mode_reported)
 
-            print "Reported edge modes:", edge_mode_reported
-            print "Sharpness with EE mode [0,1,2,3] for %s reprocess:" % \
-                (reprocess_format) , sharpnesses
+            pylab.plot(range(4), sharpnesses,
+                       "-"+gr_color[reprocess_format]+"o")
+            print "Reported edge modes w/ request fmt %s:" % reprocess_format
+            print "Sharpness with EE mode [0,1,2,3] for %s reprocess:" % (
+                    reprocess_format), sharpnesses
+            print ""
 
-
-        # Verify HQ(2) is sharper than OFF(0)
-        assert(sharpness_regular[2] > sharpness_regular[0])
-
-        # Verify ZSL(3) is similar to OFF(0)
-        assert(numpy.isclose(sharpness_regular[3], sharpness_regular[0],
-                             THRESHOLD_RELATIVE_SHARPNESS_DIFF))
-
-        # Verify OFF(0) is not sharper than FAST(1)
-        assert(sharpness_regular[1] >
-               sharpness_regular[0] * (1.0 - THRESHOLD_RELATIVE_SHARPNESS_DIFF))
-
-        # Verify FAST(1) is not sharper than HQ(2)
-        assert(sharpness_regular[2] >
-               sharpness_regular[1] * (1.0 - THRESHOLD_RELATIVE_SHARPNESS_DIFF))
+        # Finalize plot
+        pylab.title("Red-YUV Reprocess  Green-Private Reprocess  Blue-None")
+        pylab.xlabel("Edge Enhance Mode")
+        pylab.ylabel("Sharpness")
+        pylab.xticks(range(4))
+        matplotlib.pyplot.savefig("%s_plot_EE.png" %
+                                  ("test_reprocess_edge_enhancement"))
+        print "regular requests:"
+        check_edge_modes(sharpness_regular)
 
         for reprocess_format in range(len(reprocess_formats)):
-            # Verify HQ(2) is sharper than OFF(0)
-            assert(sharpnesses_reprocess[reprocess_format][2] >
-                   sharpnesses_reprocess[reprocess_format][0])
+            print "\nreprocess format:", reprocess_format
+            check_edge_modes(sharpnesses_reprocess[reprocess_format])
 
-            # Verify ZSL(3) is similar to OFF(0)
-            assert(numpy.isclose(sharpnesses_reprocess[reprocess_format][3],
-                                 sharpnesses_reprocess[reprocess_format][0],
-                                 THRESHOLD_RELATIVE_SHARPNESS_DIFF))
+            hq_div_off_reprocess = (sharpnesses_reprocess[reprocess_format][2] /
+                                    sharpnesses_reprocess[reprocess_format][0])
+            hq_div_off_regular = sharpness_regular[2] / sharpness_regular[0]
+            e_msg = "HQ/OFF_reprocess: %.4f, HQ/OFF_reg: %.4f, RTOL: %.2f" % (
+                    hq_div_off_reprocess, hq_div_off_regular,
+                    THRESH_REL_SHARPNESS_DIFF)
+            print " Verify reprocess HQ(2) ~= reg HQ(2) relative to OFF(0)"
+            assert numpy.isclose(hq_div_off_reprocess, hq_div_off_regular,
+                                 THRESH_REL_SHARPNESS_DIFF), e_msg
 
-            # Verify OFF(0) is not sharper than FAST(1)
-            assert(sharpnesses_reprocess[reprocess_format][1] >
-                   sharpnesses_reprocess[reprocess_format][0] *
-                   (1.0 - THRESHOLD_RELATIVE_SHARPNESS_DIFF))
-
-            # Verify FAST(1) is not sharper than HQ(2)
-            assert(sharpnesses_reprocess[reprocess_format][2] >
-                   sharpnesses_reprocess[reprocess_format][1] *
-                   (1.0 - THRESHOLD_RELATIVE_SHARPNESS_DIFF))
-
-            # Verify reprocessing HQ(2) is similar to regular HQ(2) relative to
-            # OFF(0)
-            assert(numpy.isclose(sharpnesses_reprocess[reprocess_format][2] /
-                                    sharpnesses_reprocess[reprocess_format][0],
-                                 sharpness_regular[2] / sharpness_regular[0],
-                                 THRESHOLD_RELATIVE_SHARPNESS_DIFF))
-
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
 
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 245fc54..243657a 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -53,7 +53,6 @@
 LOCAL_JAVA_LIBRARIES += android.test.runner.stubs
 LOCAL_JAVA_LIBRARIES += android.test.base.stubs
 LOCAL_JAVA_LIBRARIES += android.test.mock.stubs
-LOCAL_JAVA_LIBRARIES += bouncycastle
 LOCAL_JAVA_LIBRARIES += voip-common
 
 LOCAL_PACKAGE_NAME := CtsVerifier
@@ -82,6 +81,7 @@
 LOCAL_MODULE := cts-verifier-framework
 LOCAL_AAPT_FLAGS := --auto-add-overlay --extra-packages android.support.v4
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 19
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 LOCAL_SRC_FILES := \
     $(call java-files-in, src/com/android/cts/verifier) \
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 0376cc1..db179af 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -3064,6 +3064,19 @@
                 android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch:android.hardware.type.automotive" />
         </activity>
 
+        <activity android:name=".audio.ProAudioActivity"
+                  android:label="@string/pro_audio_latency_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_audio" />
+            <meta-data android:name="test_required_features" android:value="android.hardware.usb.host" />
+        </activity>
+
+        <!-- ProAudio test invokes the "Loopback" App -->
+        <activity android:name="org.drrickorang.loopback"/>
+
         <activity android:name=".audio.AudioLoopbackActivity"
                   android:label="@string/audio_loopback_test">
             <intent-filter>
@@ -3076,6 +3089,26 @@
                        android:value="android.hardware.type.watch:android.hardware.type.television" />
         </activity>
 
+        <activity android:name=".audio.MidiActivity"
+                  android:label="@string/midi_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_audio" />
+            <meta-data android:name="test_required_features"
+                android:value="android.hardware.usb.host:android.software.midi" />
+        </activity>
+
+        <service android:name="com.android.cts.verifier.midilib.MidiEchoTestService"
+                android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE">
+            <intent-filter>
+                <action android:name="android.media.midi.MidiDeviceService" />
+            </intent-filter>
+            <meta-data android:name="android.media.midi.MidiDeviceService"
+                android:resource="@xml/echo_device_info" />
+        </service>
+
         <activity android:name=".audio.AudioFrequencyLineActivity"
                   android:label="@string/audio_frequency_line_test">
             <intent-filter>
diff --git a/apps/CtsVerifier/proguard.flags b/apps/CtsVerifier/proguard.flags
index e4249c4..adecb7a 100644
--- a/apps/CtsVerifier/proguard.flags
+++ b/apps/CtsVerifier/proguard.flags
@@ -35,7 +35,6 @@
 
 # Jack seems less rigorous than proguard when it comes to warning about
 # transitive dependencies.
--dontwarn com.android.org.bouncycastle.**
 -dontwarn com.android.okhttp.**
 -dontwarn org.opencv.**
 -dontwarn android.support.test.internal.runner.hidden.ExposedInstrumentationApi
diff --git a/apps/CtsVerifier/res/layout/midi_activity.xml b/apps/CtsVerifier/res/layout/midi_activity.xml
new file mode 100644
index 0000000..d8daee1
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/midi_activity.xml
@@ -0,0 +1,232 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:id="@+id/scrollView"
+    style="@style/RootLayoutPadding">
+
+<LinearLayout android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <TextView
+        android:text="@string/midiHasMIDILbl"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="18sp"/>
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingLeft="10dp"
+        android:paddingRight="10dp"
+        android:id="@+id/midiHasMIDILbl"
+        android:textSize="18sp"/>
+
+    <!--  USB Test -->
+    <TextView
+        android:text="@string/midiUSBTestLbl"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="24sp"/>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:text="@string/usbMidiInputLbl"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/midiUSBInputLbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+                android:text="@string/usbMidiOutputLbl"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/midiUSBOutputLbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <Button
+        android:text="@string/midiTestUSBInterfaceBtn"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:id="@+id/midiTestUSBInterfaceBtn"/>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:text="@string/midiStatusLbl"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/midiUSBTestStatusLbl"
+            android:text="@string/midiNotRunLbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <!--  Virtual Test -->
+    <TextView
+        android:text="@string/midiVirtTestLbl"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="24sp"/>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:text="@string/midiVirtInputLbl"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/midiVirtInputLbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+                android:text="@string/midiVirtOutputLbl"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/midiVirtOutputLbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <Button
+        android:text="@string/midiTestVirtInterfaceBtn"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:id="@+id/midiTestVirtInterfaceBtn"/>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:text="@string/midiStatusLbl"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/midiVirtTestStatusLbl"
+            android:text="@string/midiNotRunLbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <!--  Bluetooth Test -->
+    <TextView
+        android:text="@string/midiBTTestLbl"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="24sp"/>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:text="@string/midiBTInputLbl"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/midiBTInputLbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+                android:text="@string/midiBTOutputLbl"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/midiBTOutputLbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <Button
+        android:text="@string/midiTestBTInterfaceBtn"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:id="@+id/midiTestBTInterfaceBtn"/>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:text="@string/midiStatusLbl"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/midiBTTestStatusLbl"
+            android:text="@string/midiNotRunLbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <include layout="@layout/pass_fail_buttons"/>
+</LinearLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/pro_audio.xml b/apps/CtsVerifier/res/layout/pro_audio.xml
new file mode 100644
index 0000000..71edb71
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/pro_audio.xml
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:id="@+id/scrollView"
+    style="@style/RootLayoutPadding">
+
+<LinearLayout android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:text="@string/proAudioHasLLAlbl"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/proAudioHasLLALbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:text="@string/proAudioMidiHasMIDILbl"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/proAudioHasMIDILbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:text="@string/proAudioMidiHasUSBHostLbl"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/proAudioMidiHasUSBHostLbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:text="@string/proAudioMidiHasUSBPeripheralLbl"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/proAudioMidiHasUSBPeripheralLbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <CheckBox android:id="@+id/proAudioHasHDMICheckBox"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/proAudioHasHDMICheckBox"
+        android:onClick="onCheckboxClicked"/>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:text="@string/proAudioHDMISupportLbl"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/proAudioHDMISupportLbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <TextView
+        android:text="@string/proAudioInputLbl"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="18sp"/>
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingLeft="10dp"
+        android:paddingRight="10dp"
+        android:id="@+id/proAudioInputLbl"
+        android:textSize="18sp"/>
+
+    <TextView
+        android:text="@string/proAudioOutputLbl"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="18sp"/>
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingLeft="10dp"
+        android:paddingRight="10dp"
+        android:id="@+id/proAudioOutputLbl"
+        android:textSize="18sp"/>
+
+    <Button
+        android:text="@string/audio_proaudio_roundtrip"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:id="@+id/proAudio_runRoundtripBtn"/>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:text="@string/proAudioRoundTripLbl"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/proAudioRoundTripLbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:text="@string/proAudioConfidenceLbl"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/proAudioConfidenceLbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <include layout="@layout/pass_fail_buttons"/>
+</LinearLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index e4c0bda..6a656d3 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -2801,9 +2801,9 @@
     <string name="device_owner_disable_statusbar_test">Disable status bar</string>
     <string name="device_owner_disable_statusbar_test_info">
             Please press the below button to disable the status bar and verify that quick settings, notifications
-            and the assist gesture are no longer available.\n
+            and the assistant gesture are no longer available.\n
             Next, press the button to reenable the status bar and verify that quick settings, notification
-            and the assist gesture are available again.\n
+            and the assistant gesture are available again.\n
             Please mark the test accordingly.
     </string>
     <string name="device_owner_disable_statusbar_button">Disable status bar</string>
@@ -2815,7 +2815,7 @@
             switch off the screen. Then press the power button to switch the screen back on and verify that
             no keyguard was shown.\n
             Next, press the button to reenable the keyguard and repeat the above steps, this time verifying that
-            a keyguard was shown again.\n
+            a keyguard was shown.\n
             Please mark the test accordingly.
     </string>
     <string name="device_owner_disable_keyguard_button">Disable keyguard</string>
@@ -2845,7 +2845,7 @@
             button, etc., isn\'t shown.\n
             6) Press the power button to turn off the screen, and press it again to turn the screen
             back on. Lock screen shouldn\'t be shown.\n
-            7) The assist gesture isn\'t available.
+            7) The assistant gesture isn\'t available.
     </string>
     <string name="device_owner_lock_task_ui_system_info_test">Enable system info</string>
     <string name="device_owner_lock_task_ui_system_info_test_info">
@@ -2861,7 +2861,7 @@
             button, etc., isn\'t shown.\n
             6) Press the power button to turn off the screen, and press it again to turn the screen
             back on. Lock screen shouldn\'t be shown.\n
-            7) The assist gesture isn\'t available.\n\n
+            7) The assistant gesture isn\'t available.\n\n
             Mark the test as \'pass\' only if ALL of the above requirements are met.
     </string>
     <string name="device_owner_lock_task_ui_notifications_test">Enable notifications</string>
@@ -2878,7 +2878,7 @@
             button, etc., isn\'t shown.\n
             5) Press the power button to turn off the screen, and press it again to turn the screen
             back on. Lock screen shouldn\'t be shown.\n
-            6) The assist gesture isn\'t available.\n\n
+            6) The assistant gesture isn\'t available.\n\n
             Mark the test as \'pass\' only if ALL of the above requirements are met.
     </string>
     <string name="device_owner_lock_task_ui_home_test">Enable Home button</string>
@@ -2896,7 +2896,7 @@
             button, etc., isn\'t shown.\n
             6) Press the power button to turn off the screen, and press it again to turn the screen
             back on. Lock screen shouldn\'t be shown.\n
-            7) The assist gesture isn\'t available.\n\n
+            7) The assistant gesture isn\'t available.\n\n
             Mark the test as \'pass\' only if ALL of the above requirements are met.
     </string>
     <string name="device_owner_lock_task_ui_recents_test">Enable Overview button</string>
@@ -2913,7 +2913,7 @@
             button, etc., isn\'t shown.\n
             4) Press the power button to turn off the screen, and press it again to turn the screen
             back on. Lock screen shouldn\'t be shown.\n
-            5) The assist gesture isn\'t available.\n\n
+            5) The assistant gesture isn\'t available.\n\n
             Mark the test as \'pass\' only if ALL of the above requirements are met.
     </string>
     <string name="device_owner_lock_task_ui_global_actions_test">Enable global actions</string>
@@ -2931,7 +2931,7 @@
             4) The Overview button is hidden and the Overview gesture (swipe-up) does not work.\n
             5) Press the power button to turn off the screen, and press it again to turn the screen
             back on. Lock screen shouldn\'t be shown.\n
-            6) The assist gesture isn\'t available.\n\n
+            6) The assistant gesture isn\'t available.\n\n
             Mark the test as \'pass\' only if ALL of the above requirements are met.
     </string>
     <string name="device_owner_lock_task_ui_keyguard_test">Enable keyguard</string>
@@ -2948,7 +2948,7 @@
             4) The Overview button is hidden and the Overview gesture (swipe-up) does not work.\n
             5) Long-press the power button. The power button menu, which usually shows the power-off
             button, etc., isn\'t shown.\n
-            6) The assist gesture isn\'t available.\n\n
+            6) The assistant gesture isn\'t available.\n\n
             Mark the test as \'pass\' only if ALL of the above requirements are met.
     </string>
     <string name="device_owner_lock_task_ui_stop_lock_task_test">Stop LockTask mode</string>
@@ -3992,7 +3992,6 @@
     <string name="connectedPeripheral">Connected Peripheral</string>
     <string name="audio_uap_attribs_test">USB Audio Peripheral Attributes Test</string>
     <string name="uapPeripheralProfileStatus">Peripheral Profile Status</string>
-
     <string name="audio_uap_play_test">USB Audio Peripheral Play Test</string>
     <string name="uapPlayTestInstructions">Connect the USB Audio Interface Peripheral and press the
         PLAY button below. Verify that a tone is correctly played.</string>
@@ -4016,6 +4015,69 @@
     <string name="uapButtonsBtnDLbl">Button D - voice assist</string>
     <string name="uapButtonsRecognized">Recognized</string>
     <string name="uapButtonsNotRecognized">Not Recognized</string>
+    <string name="uapButtonsDisableAssistantTitle">Disable Google Assistant</string>
+    <string name="uapButtonsDisableAssistant">For this test to succeed it may be necessary
+        to disable the Google Assistant (Settings / Google / Search / Google Assistant Settings /
+        Devices / &lt;device name&gt; / Google Assistant)</string>
+
+    <!--  Pro Audio Tests -->
+    <string name="pro_audio_latency_test">Pro Audio Test</string>
+
+    <string name="proAudioHasLLAlbl">Has Low-Latency Audio</string>
+    <string name="proAudioInputLbl">Audio Input:</string>
+    <string name="proAudioOutputLbl">Audio Output:</string>
+    <string name="proAudioRoundTripLbl">Round Trip Latency:</string>
+    <string name="proAudioConfidenceLbl">Confidence:</string>
+
+    <string name="proAudioMidiHasMIDILbl">Has MIDI Support</string>
+    <string name="proAudioMIDIInputLbl">MIDI Input:</string>
+    <string name="proAudioMIDIOutputLbl">MIDI Output:</string>
+    <string name="proAudioMidiHasUSBHostLbl">USB Host Mode:</string>
+    <string name="proAudioMidiHasUSBPeripheralLbl">USB Peripheral Mode:</string>
+    <string name="proAudioHDMISupportLbl">HDMI Support:</string>
+    <string name="proAudioHasHDMICheckBox">Has HDMI Support</string>
+
+    <string name="audio_proaudio_loopbackbtn">Start Loopback</string>
+    <string name="audio_proaudio_loopbackInfoBtn">Loopback Instructions</string>
+    <string name="audio_proaudio_roundtrip">Round-Trip Test</string>
+    <string name="audio_proaudio_NA">N/A</string>
+    <string name="audio_proaudio_pending">pending...</string>
+
+    <!--  MIDI Test -->
+    <string name="midi_test">MIDI Test</string>
+    <string name="midi_info">
+       For the USB MIDI interface test it is required that you have connected a supported USB
+       Audio Peripheral device with standard MIDI 5-pin, DIN (round) connectors and a standard
+       MIDI cable. The cable must be connected to the MIDI input and output plugs on the
+       peripheral.
+       \nFor the USB Bluetooth test it is required that you connect a Yamaha MT-BT301 to the
+       correct MIDI plugs on the USB peripheral, the BT301 output jack to the USB interface
+       input jack and BT301 input plug to the USB interface output jack.
+       \nThe Virtual MIDI test does not require any MIDI interface hardware.
+    </string>
+
+    <string name="midiHasMIDILbl">Has MIDI Support</string>
+
+    <string name="midiUSBTestLbl">USB MIDI Loopback Test:</string>
+    <string name="usbMidiInputLbl">USB Input:</string>
+    <string name="usbMidiOutputLbl">USB Output:</string>
+    <string name="midiTestUSBInterfaceBtn">Test USB MIDI Interface</string>
+
+    <string name="midiVirtTestLbl">Virtual MIDI Loopback Test:</string>
+    <string name="midiVirtInputLbl">Virtual Input:</string>
+    <string name="midiVirtOutputLbl">Virtual Output:</string>
+    <string name="midiTestVirtInterfaceBtn">Test Virtual MIDI Interface</string>
+
+    <string name="midiBTTestLbl">Bluetooth MIDI Loopback Test:</string>
+    <string name="midiBTInputLbl">Bluetooth Input:</string>
+    <string name="midiBTOutputLbl">Bluetooth Output:</string>
+    <string name="midiTestBTInterfaceBtn">Test Bluetooth MIDI Interface</string>
+
+    <string name="midiStatusLbl">Status</string>
+    <string name="midiNotRunLbl">Not Run.</string>
+    <string name="midiPassedLbl">Passed.</string>
+    <string name="midiFailedMismatchLbl">Failed - Data Mismatch.</string>
+    <string name="midiFailedTimeoutLbl">Failed - Timeout.</string>
 
     <!-- Audio general text -->
     <string name="audio_general_headset_port_exists">Does this device have a headset port?</string>
@@ -4409,6 +4471,23 @@
         \"transport\" button and verify that it is recognized by the test.
        </string>
 
+    <!-- Pro Audio Test -->
+    <string name="proaudio_test">Pro Audio Test</string>
+    <string name="proaudio_info">
+       This test requires that you have connected a supported USB Audio Peripheral device
+       (not a headset) and that peripheral\'s audio outputs are connected to the peripherals\'s
+       audio inputs. Alternatively, for devices with an analog audio jack or USB-c Digital
+       to Analog dongle, a Loopback Plug can be used. Also, any if there is an input level
+       control on the peripheral, it must be set to a non-zero value. When the test has
+       verified support for a valid audio peripheral, press the \"Round-Trip Test\" button
+       to complete the test. Note that it may be necessary to run the latency test more than
+       once to get a sufficient confidence value.
+    </string>
+    <string name="proaudio_hdmi_infotitle">HDMI Support</string>
+    <string name="proaudio_hdmi_message">Please connect an HDMI peripheral to validate
+        HDMI output attributes.
+    </string>
+
     <!-- Telecom tests -->
     <string name="telecom_enable_phone_account_test"> Telecom Enable Phone Account Test</string>
     <string name="telecom_enable_phone_account_info">
@@ -4492,4 +4571,5 @@
         Click the button below to confirm that the incoming call was answered.
     </string>
     <string name="telecom_incoming_self_mgd_confirm_answer_button">Confirm Answer</string>
+
 </resources>
diff --git a/apps/CtsVerifier/res/xml/echo_device_info.xml b/apps/CtsVerifier/res/xml/echo_device_info.xml
new file mode 100644
index 0000000..936216a
--- /dev/null
+++ b/apps/CtsVerifier/res/xml/echo_device_info.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<devices>
+    <device manufacturer="AndroidCTS" product="MidiEcho" tags="echo,test">
+        <input-port name="input" />
+        <output-port name="output" />
+    </device>
+</devices>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/MidiActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/MidiActivity.java
new file mode 100644
index 0000000..d3f118d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/MidiActivity.java
@@ -0,0 +1,817 @@
+/*
+ * 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.verifier.audio;
+
+import java.io.IOException;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.pm.PackageManager;
+import android.media.midi.MidiDevice;
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiDeviceStatus;
+import android.media.midi.MidiInputPort;
+import android.media.midi.MidiManager;
+import android.media.midi.MidiOutputPort;
+import android.media.midi.MidiReceiver;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.cts.verifier.midilib.MidiEchoTestService;
+import com.android.cts.verifier.PassFailButtons;
+
+import com.android.cts.verifier.R;  // needed to access resource in CTSVerifier project namespace.
+
+/*
+ * A note about the USB MIDI device.
+ * Any USB MIDI peripheral with standard female DIN jacks can be used. A standard MIDI cable
+ * plugged into both input and output is required for the USB Loopback Test. A Bluetooth MIDI
+ * device like the Yamaha MD-BT01 plugged into both input and output is required for the
+ * Bluetooth Loopback test.
+ */
+
+/*
+ *  A note about the "virtual MIDI" device...
+ * See file MidiEchoTestService for implementation of the echo server itself.
+ * This service is started by the main manifest file (AndroidManifest.xml).
+ */
+
+/*
+ * A note about Bluetooth MIDI devices...
+ * Any Bluetooth MIDI device needs to be paired with the DUT with the "MIDI+BTLE" application
+ * available in the Play Store:
+ * (https://play.google.com/store/apps/details?id=com.mobileer.example.midibtlepairing).
+ */
+
+/**
+ * CTS Verifier Activity for MIDI test
+ */
+public class MidiActivity extends PassFailButtons.Activity implements View.OnClickListener {
+
+    private static final String TAG = "MidiActivity";
+    private static final boolean DEBUG = false;
+
+    private MidiManager mMidiManager;
+
+    // Flags
+    private boolean mHasMIDI;
+
+    // Test "Modules"
+    private MidiTestModule      mUSBTestModule;
+    private MidiTestModule      mVirtTestModule;
+    private BTMidiTestModule    mBTTestModule;
+
+    // Test State
+    private final Object    mTestLock = new Object();
+    private boolean     mTestRunning;
+
+    // Timeout handling
+    private static final int TEST_TIMEOUT_MS = 1000;
+    private final Timer mTimeoutTimer = new Timer();
+
+    // Widgets
+    private Button      mUSBTestBtn;
+    private Button      mVirtTestBtn;
+    private Button      mBTTestBtn;
+
+    private TextView    mUSBIInputDeviceLbl;
+    private TextView    mUSBOutputDeviceLbl;
+    private TextView    mUSBTestStatusTxt;
+
+    private TextView    mVirtInputDeviceLbl;
+    private TextView    mVirtOutputDeviceLbl;
+    private TextView    mVirtTestStatusTxt;
+
+    private TextView    mBTInputDeviceLbl;
+    private TextView    mBTOutputDeviceLbl;
+    private TextView    mBTTestStatusTxt;
+
+    public MidiActivity() {
+        super();
+    }
+
+    private boolean hasMIDI() {
+        // CDD Section C-1-4: android.software.midi
+        return getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI);
+    }
+
+    private void showConnectedMIDIPeripheral() {
+        // USB
+        mUSBIInputDeviceLbl.setText(mUSBTestModule.getInputName());
+        mUSBOutputDeviceLbl.setText(mUSBTestModule.getOutputName());
+        mUSBTestBtn.setEnabled(mUSBTestModule.isTestReady());
+
+        // Virtual MIDI
+        mVirtInputDeviceLbl.setText(mVirtTestModule.getInputName());
+        mVirtOutputDeviceLbl.setText(mVirtTestModule.getOutputName());
+        mVirtTestBtn.setEnabled(mVirtTestModule.isTestReady());
+
+        // Bluetooth
+        mBTInputDeviceLbl.setText(mBTTestModule.getInputName());
+        mBTOutputDeviceLbl.setText(mBTTestModule.getOutputName());
+        mBTTestBtn.setEnabled(mBTTestModule.isTestReady());
+    }
+
+    private boolean calcTestPassed() {
+        boolean hasPassed = false;
+        if (!mHasMIDI) {
+            // if it doesn't report MIDI support, then it doesn't have to pass the other tests.
+            hasPassed = true;
+        } else {
+            hasPassed = mUSBTestModule.hasTestPassed() &&
+                    mVirtTestModule.hasTestPassed() &&
+                    mBTTestModule.hasTestPassed();
+        }
+
+        getPassButton().setEnabled(hasPassed);
+        return hasPassed;
+    }
+
+    private void scanMidiDevices() {
+        if (DEBUG) {
+            Log.i(TAG, "scanMidiDevices()....");
+        }
+
+        MidiDeviceInfo[] devInfos = mMidiManager.getDevices();
+        mUSBTestModule.scanDevices(devInfos, MidiDeviceInfo.TYPE_USB);
+        mVirtTestModule.scanDevices(devInfos, MidiDeviceInfo.TYPE_VIRTUAL);
+        mBTTestModule.scanDevices(devInfos, MidiDeviceInfo.TYPE_BLUETOOTH);
+
+        showConnectedMIDIPeripheral();
+    }
+
+    //
+    // UI Updaters
+    //
+    private void enableTestButtons(boolean enable) {
+        if (DEBUG) {
+            Log.i(TAG, "enableTestButtons" + enable + ")");
+        }
+
+        runOnUiThread(new Runnable() {
+            public void run() {
+                if (enable) {
+                    // remember, a given test might not be enabled, so we can't just enable
+                    // all of the buttons
+                    showConnectedMIDIPeripheral();
+                } else {
+                    mUSBTestBtn.setEnabled(enable);
+                    mVirtTestBtn.setEnabled(enable);
+                    mBTTestBtn.setEnabled(enable);
+                }
+            }
+        });
+    }
+
+    private void showUSBTestStatus() {
+        mUSBTestStatusTxt.setText(mUSBTestModule.getTestStatusString());
+    }
+
+    private void showVirtTestStatus() {
+        mVirtTestStatusTxt.setText(mVirtTestModule.getTestStatusString());
+    }
+
+    private void showBTTestStatus() {
+        mBTTestStatusTxt.setText(mBTTestModule.getTestStatusString());
+    }
+
+    // Need this to update UI from MIDI read thread
+    public void updateTestStateUI() {
+        runOnUiThread(new Runnable() {
+            public void run() {
+                calcTestPassed();
+                showUSBTestStatus();
+                showVirtTestStatus();
+                showBTTestStatus();
+            }
+        });
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.midi_activity);
+
+        // Standard PassFailButtons.Activity initialization
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.midi_test, R.string.midi_info, -1);
+
+        // May as well calculate this right off the bat.
+        mHasMIDI = hasMIDI();
+        ((TextView)findViewById(R.id.midiHasMIDILbl)).setText("" + mHasMIDI);
+
+        // USB Test Widgets
+        mUSBIInputDeviceLbl = (TextView)findViewById(R.id.midiUSBInputLbl);
+        mUSBOutputDeviceLbl = (TextView)findViewById(R.id.midiUSBOutputLbl);
+        mUSBTestBtn = (Button)findViewById(R.id.midiTestUSBInterfaceBtn);
+        mUSBTestBtn.setOnClickListener(this);
+        mUSBTestStatusTxt = (TextView)findViewById(R.id.midiUSBTestStatusLbl);
+
+        // Virtual MIDI Test Widgets
+        mVirtInputDeviceLbl = (TextView)findViewById(R.id.midiVirtInputLbl);
+        mVirtOutputDeviceLbl = (TextView)findViewById(R.id.midiVirtOutputLbl);
+        mVirtTestBtn = (Button)findViewById(R.id.midiTestVirtInterfaceBtn);
+        mVirtTestBtn.setOnClickListener(this);
+        mVirtTestStatusTxt = (TextView)findViewById(R.id.midiVirtTestStatusLbl);
+
+        // Bluetooth MIDI Test Widgets
+        mBTInputDeviceLbl = (TextView)findViewById(R.id.midiBTInputLbl);
+        mBTOutputDeviceLbl = (TextView)findViewById(R.id.midiBTOutputLbl);
+        mBTTestBtn = (Button)findViewById(R.id.midiTestBTInterfaceBtn);
+        mBTTestBtn.setOnClickListener(this);
+        mBTTestStatusTxt = (TextView)findViewById(R.id.midiBTTestStatusLbl);
+
+        // Setup Test Modules
+        mUSBTestModule = new MidiTestModule();
+        mVirtTestModule = new MidiTestModule();
+        mBTTestModule = new BTMidiTestModule();
+
+        // Init MIDI Stuff
+        mMidiManager = (MidiManager) getSystemService(Context.MIDI_SERVICE);
+
+        // Initial MIDI Device Scan
+        scanMidiDevices();
+
+        // Plug in device connect/disconnect callback
+        mMidiManager.registerDeviceCallback(new MidiDeviceCallback(), new Handler(getMainLooper()));
+    }
+
+    /**
+     * Callback class for MIDI device connect/disconnect.
+     */
+    private class MidiDeviceCallback extends MidiManager.DeviceCallback {
+        private static final String TAG = "MidiDeviceCallback";
+
+        @Override
+        public void onDeviceAdded(MidiDeviceInfo device) {
+            scanMidiDevices();
+        }
+
+        @Override
+        public void onDeviceRemoved(MidiDeviceInfo device) {
+            scanMidiDevices();
+        }
+    } /* class MidiDeviceCallback */
+
+    //
+    // View.OnClickListener Override - Handles button clicks
+    //
+    @Override
+    public void onClick(View view) {
+        switch (view.getId()) {
+        case R.id.midiTestUSBInterfaceBtn:
+            mUSBTestModule.startLoopbackTest();
+            break;
+
+        case R.id.midiTestVirtInterfaceBtn:
+            mVirtTestModule.startLoopbackTest();
+            break;
+
+        case R.id.midiTestBTInterfaceBtn:
+            mBTTestModule.startLoopbackTest();
+            break;
+
+        default:
+            assert false : "Unhandled button click";
+        }
+    }
+
+    /**
+     * A class to hold the MidiDeviceInfo and ports objects associated with a MIDI I/O peripheral.
+     */
+    private class MidiIODevice {
+        private static final String TAG = "MidiIODevice";
+
+        public MidiDeviceInfo mSendDevInfo;
+        public MidiDeviceInfo mReceiveDevInfo;
+
+        public MidiInputPort   mSendPort;
+        public MidiOutputPort  mReceivePort;
+
+        public void scanDevices(MidiDeviceInfo[] devInfos, int typeID) {
+            mSendDevInfo = null;
+            mReceiveDevInfo = null;
+            mSendPort = null;
+            mReceivePort = null;
+
+            for(MidiDeviceInfo devInfo : devInfos) {
+                // Inputs?
+                int numInPorts = devInfo.getInputPortCount();
+                if (numInPorts <= 0) {
+                    continue; // none?
+                }
+                if (devInfo.getType() == typeID && mSendDevInfo == null) {
+                    mSendDevInfo = devInfo;
+                }
+
+                // Outputs?
+                int numOutPorts = devInfo.getOutputPortCount();
+                if (numOutPorts <= 0) {
+                    continue; // none?
+                }
+                if (devInfo.getType() == typeID && mReceiveDevInfo == null) {
+                    mReceiveDevInfo = devInfo;
+                }
+                if (mSendDevInfo != null && mReceiveDevInfo != null) {
+                    break;  // we have an in and out device, so we can stop scanning
+                }
+            }
+
+            if (DEBUG) {
+                if (mSendDevInfo != null) {
+                    Log.i(TAG, "---- mSendDevInfo: " +
+                        mSendDevInfo.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME));
+                }
+                if (mReceiveDevInfo != null) {
+                    Log.i(TAG, "---- mReceiveDevInfo: " +
+                        mReceiveDevInfo.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME));
+                }
+            }
+        }
+
+        protected void openPorts(MidiDevice device, MidiReceiver receiver) {
+            if (DEBUG) {
+                Log.i(TAG, "---- openPorts()");
+            }
+            MidiDeviceInfo deviceInfo = device.getInfo();
+            int numOutputs = deviceInfo.getOutputPortCount();
+            if (numOutputs > 0) {
+                mReceivePort = device.openOutputPort(0);
+                mReceivePort.connect(receiver);
+            }
+
+            int numInputs = deviceInfo.getInputPortCount();
+            if (numInputs != 0) {
+                mSendPort = device.openInputPort(0);
+            }
+        }
+
+        public void closePorts() {
+            if (DEBUG) {
+                Log.i(TAG, "---- closePorts()");
+            }
+            try {
+                if (mSendPort != null) {
+                    mSendPort.close();
+                    mSendPort = null;
+                }
+                if (mReceivePort != null) {
+                    mReceivePort.close();
+                    mReceivePort = null;
+                }
+            } catch (IOException ex) {
+                Log.e(TAG, "IOException Closing MIDI ports: " + ex);
+            }
+        }
+
+        public String getInputName() {
+            return mReceiveDevInfo != null
+                    ? mReceiveDevInfo.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME) : "";
+        }
+
+        public String getOutputName() {
+            return mSendDevInfo != null
+                    ? mSendDevInfo.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME) : "";
+        }
+    }   /* class MidiIODevice */
+
+    //
+    // MIDI Messages
+    //
+    public static final int kMIDICmd_KeyDown = 9;
+    public static final int kMIDICmd_KeyUp = 8;
+    public static final int kMIDICmd_PolyPress = 10;
+    public static final int kMIDICmd_Control = 11;
+    public static final int kMIDICmd_ProgramChange = 12;
+    public static final int kMIDICmd_ChannelPress = 13;
+    public static final int kMIDICmd_PitchWheel = 14;
+    public static final int kMIDICmd_SysEx = 15;
+    public static final int kMIDICmd_EndOfSysEx = 0b11110111;
+
+    private class TestMessage {
+        public byte[]   mMsgBytes;
+
+        public boolean matches(byte[] msg, int offset, int count) {
+            // Length
+            // Log.i(TAG, "  count [" + count + " : " + mMsgBytes.length + "]");
+            if (count != mMsgBytes.length) {
+                return false;
+            }
+
+            // Data
+            for(int index = 0; index < count; index++) {
+                // Log.i(TAG, "  [" + msg[offset + index] + " : " + mMsgBytes[index] + "]");
+                if (msg[offset + index] != mMsgBytes[index]) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    } /* class TestMessage */
+
+    private static byte makeMIDICmd(int cmd, int channel) {
+        return (byte)((cmd << 4) | (channel & 0x0F));
+    }
+
+    /**
+     * A class to control and represent the state of a given test.
+     * It hold the data needed for IO, and the logic for sending, receiving and matching
+     * the MIDI data stream.
+     */
+    private class MidiTestModule {
+        private static final String TAG = "MidiTestModule";
+
+        // Test Peripheral
+        MidiIODevice mIODevice = new MidiIODevice();
+
+        // Test Status
+        protected static final int TESTSTATUS_NOTRUN = 0;
+        protected static final int TESTSTATUS_PASSED = 1;
+        protected static final int TESTSTATUS_FAILED_MISMATCH = 2;
+        protected static final int TESTSTATUS_FAILED_TIMEOUT = 3;
+
+        protected int mTestStatus = TESTSTATUS_NOTRUN;
+        protected boolean mTestMismatched;
+
+        // Test Data
+        // - The set of messages to send
+        private TestMessage[] mTestMessages;
+
+        // - The stream of message data to walk through when MIDI data is received.
+        private byte[] mMIDIDataStream;
+        private int mReceiveStreamPos;
+
+        public MidiTestModule() {
+            setupTestMessages();
+        }
+
+        // UI Helper
+        public String getTestStatusString() {
+            Resources appResources = getApplicationContext().getResources();
+            switch (mTestStatus) {
+            case TESTSTATUS_NOTRUN:
+                return appResources.getString(R.string.midiNotRunLbl);
+
+            case TESTSTATUS_PASSED:
+                return appResources.getString(R.string.midiPassedLbl);
+
+            case TESTSTATUS_FAILED_MISMATCH:
+                return appResources.getString(R.string.midiFailedMismatchLbl);
+
+            case TESTSTATUS_FAILED_TIMEOUT:
+            {
+                String messageStr = appResources.getString(R.string.midiFailedTimeoutLbl);
+                return messageStr + " @" + mReceiveStreamPos;
+            }
+
+            default:
+                return "Unknown Test Status.";
+            }
+        }
+
+        public void scanDevices(MidiDeviceInfo[] devInfos, int typeID) {
+            mIODevice.scanDevices(devInfos, typeID);
+        }
+
+        public void showTimeoutMessage() {
+            runOnUiThread(new Runnable() {
+                public void run() {
+                    synchronized (mTestLock) {
+                        if (mTestRunning) {
+                            if (DEBUG) {
+                                Log.i(TAG, "---- Test Failed - TIMEOUT");
+                            }
+                            mTestStatus = TESTSTATUS_FAILED_TIMEOUT;
+                            updateTestStateUI();
+                        }
+                    }
+                }
+            });
+        }
+
+        public void startLoopbackTest() {
+            synchronized (mTestLock) {
+                mTestRunning = true;
+                enableTestButtons(false);
+            }
+
+            if (DEBUG) {
+                Log.i(TAG, "---- startLoopbackTest()");
+            }
+
+            mTestStatus = TESTSTATUS_NOTRUN;
+            mTestMismatched = false;
+            mReceiveStreamPos = 0;
+
+            // These might be left open due to a failing, previous test
+            // so just to be sure...
+            closePorts();
+
+            if (mIODevice.mSendDevInfo != null) {
+                mMidiManager.openDevice(mIODevice.mSendDevInfo, new TestModuleOpenListener(), null);
+            }
+
+            // Start the timeout timer
+            TimerTask task = new TimerTask() {
+                @Override
+                public void run() {
+                    synchronized (mTestLock) {
+                        if (mTestRunning) {
+                            // Timeout
+                            showTimeoutMessage();
+                            enableTestButtons(true);
+                        }
+                    }
+                }
+            };
+            mTimeoutTimer.schedule(task, TEST_TIMEOUT_MS);
+        }
+
+        protected void openPorts(MidiDevice device) {
+            mIODevice.openPorts(device, new MidiMatchingReceiver());
+        }
+
+        protected void closePorts() {
+            mIODevice.closePorts();
+        }
+
+        public String getInputName() {
+            return mIODevice.getInputName();
+        }
+
+        public String getOutputName() {
+            return mIODevice.getOutputName();
+        }
+
+        public boolean isTestReady() {
+            return mIODevice.mReceiveDevInfo != null && mIODevice.mSendDevInfo != null;
+        }
+
+        public boolean hasTestPassed() {
+            return mTestStatus == TESTSTATUS_PASSED;
+        }
+
+        // A little explanation here... It seems reasonable to send complete MIDI messages, i.e.
+        // as a set of discrete pakages.
+        // However the looped-back data may not be delivered in message-size packets, so it makes more
+        // sense to look at that as a stream of bytes.
+        // So we build a set of messages to send, and then create the equivalent stream of bytes
+        // from that to match against when received back in from the looped-back device.
+        private void setupTestMessages() {
+            int NUM_TEST_MESSAGES = 3;
+            mTestMessages = new TestMessage[NUM_TEST_MESSAGES];
+
+            //
+            // Set up any set of messages you want
+            // Except for the command IDs, the data values are purely arbitrary and meaningless
+            // outside of being matched.
+            // KeyDown
+            mTestMessages[0] = new TestMessage();
+            mTestMessages[0].mMsgBytes = new byte[]{makeMIDICmd(kMIDICmd_KeyDown, 0), 64, 12};
+
+            // KeyUp
+            mTestMessages[1] = new TestMessage();
+            mTestMessages[1].mMsgBytes = new byte[]{makeMIDICmd(kMIDICmd_KeyDown, 0), 64, 45};
+
+            // SysEx
+            // NOTE: A sysex on the MT-BT01 seems to top out at sometimes as low as 40 bytes.
+            // It is not clear, but needs more research. For now choose a conservative size.
+            int sysExSize = 32;
+            byte[] sysExMsg = new byte[sysExSize];
+            sysExMsg[0] = makeMIDICmd(kMIDICmd_SysEx, 0);
+            for(int index = 1; index < sysExSize-1; index++) {
+                sysExMsg[index] = (byte)index;
+            }
+            sysExMsg[sysExSize-1] = (byte)kMIDICmd_EndOfSysEx;
+            mTestMessages[2] = new TestMessage();
+            mTestMessages[2].mMsgBytes = sysExMsg;
+
+            //
+            // Now build the stream to match against
+            //
+            int streamSize = 0;
+            for (int msgIndex = 0; msgIndex < mTestMessages.length; msgIndex++) {
+                streamSize += mTestMessages[msgIndex].mMsgBytes.length;
+            }
+
+            mMIDIDataStream = new byte[streamSize];
+
+            int offset = 0;
+            for (int msgIndex = 0; msgIndex < mTestMessages.length; msgIndex++) {
+                int numBytes = mTestMessages[msgIndex].mMsgBytes.length;
+                System.arraycopy(mTestMessages[msgIndex].mMsgBytes, 0,
+                        mMIDIDataStream, offset, numBytes);
+                offset += numBytes;
+            }
+            mReceiveStreamPos = 0;
+        }
+
+        /**
+         * Compares the supplied bytes against the sent message stream at the current postion
+         * and advances the stream position.
+         */
+        private boolean matchStream(byte[] bytes, int offset, int count) {
+            if (DEBUG) {
+                Log.i(TAG, "---- matchStream() offset:" + offset + " count:" + count);
+            }
+            boolean matches = true;
+
+            for (int index = 0; index < count; index++) {
+                if (bytes[offset + index] != mMIDIDataStream[mReceiveStreamPos]) {
+                    matches = false;
+                    if (DEBUG) {
+                        Log.i(TAG, "---- mismatch @" + index + " [" + bytes[offset + index] +
+                                " : " + mMIDIDataStream[mReceiveStreamPos] + "]");
+                    }
+                }
+                mReceiveStreamPos++;
+            }
+
+            if (DEBUG) {
+                Log.i(TAG, "  returns:" + matches);
+            }
+            return matches;
+        }
+
+        /**
+         * Writes out the list of MIDI messages to the output port.
+         */
+        private void sendMessages() {
+            if (DEBUG) {
+                Log.i(TAG, "---- sendMessages()...");
+            }
+            int totalSent = 0;
+            if (mIODevice.mSendPort != null) {
+                try {
+                    for(TestMessage msg : mTestMessages) {
+                        mIODevice.mSendPort.send(msg.mMsgBytes, 0, msg.mMsgBytes.length);
+                        totalSent += msg.mMsgBytes.length;
+                    }
+                } catch (IOException ex) {
+                    Log.i(TAG, "---- IOException " + ex);
+                }
+            }
+            if (DEBUG) {
+                Log.i(TAG, "---- totalSent:" + totalSent);
+            }
+        }
+
+        /**
+         * Listens for MIDI device opens. Opens I/O ports and sends out the apriori
+         * setup messages.
+         */
+        class TestModuleOpenListener implements MidiManager.OnDeviceOpenedListener {
+            @Override
+            public void onDeviceOpened(MidiDevice device) {
+                if (DEBUG) {
+                    Log.i(TAG, "---- onDeviceOpened()");
+                }
+                openPorts(device);
+                sendMessages();
+            }
+        }
+
+        /**
+         * A MidiReceiver subclass whose job it is to monitor incomming messages
+         * and match them against the stream sent by the test.
+         */
+        private class MidiMatchingReceiver extends MidiReceiver {
+            private static final String TAG = "MidiMatchingReceiver";
+
+            @Override
+            public void onSend(byte[] msg, int offset, int count, long timestamp) throws IOException {
+                if (!matchStream(msg, offset, count)) {
+                    mTestMismatched = true;
+                }
+                if (DEBUG) {
+                    Log.i(TAG, "onSend() @" + mReceiveStreamPos);
+                }
+                if (mReceiveStreamPos == mMIDIDataStream.length) {
+                    synchronized (mTestLock) {
+                        mTestRunning = false;
+                    }
+
+                    if (DEBUG) {
+                        Log.i(TAG, "---- Test Complete");
+                    }
+                    // defer closing the ports to outside of this callback.
+                    new Thread(new Runnable() {
+                        public void run() {
+                            mTestStatus = mTestMismatched
+                                    ? TESTSTATUS_FAILED_MISMATCH : TESTSTATUS_PASSED;
+                            Log.i(TAG, "---- mTestStatus:" + mTestStatus);
+                            closePorts();
+                        }
+                    }).start();
+
+                    enableTestButtons(true);
+                    updateTestStateUI();
+                }
+            }
+        } /* class MidiMatchingReceiver */
+    } /* class MidiTestModule */
+
+    /**
+     * Test Module for Bluetooth Loopback.
+     * This is a specialization of MidiTestModule (which has the connections for the BL device
+     * itself) with and added MidiIODevice object for the USB audio device which does the
+     * "looping back".
+     */
+    private class BTMidiTestModule extends MidiTestModule {
+        private static final String TAG = "BTMidiTestModule";
+        private MidiIODevice mUSBLoopbackDevice = new MidiIODevice();
+
+        @Override
+        public void scanDevices(MidiDeviceInfo[] devInfos, int typeID) {
+            // (normal) Scan for BT MIDI device
+            super.scanDevices(devInfos, typeID);
+            // Find a USB Loopback Device
+            mUSBLoopbackDevice.scanDevices(devInfos, MidiDeviceInfo.TYPE_USB);
+        }
+
+        private void openUSBEchoDevice(MidiDevice device) {
+            MidiDeviceInfo deviceInfo = device.getInfo();
+            int numOutputs = deviceInfo.getOutputPortCount();
+            if (numOutputs > 0) {
+                mUSBLoopbackDevice.mReceivePort = device.openOutputPort(0);
+                mUSBLoopbackDevice.mReceivePort.connect(new USBMidiEchoReceiver());
+            }
+
+            int numInputs = deviceInfo.getInputPortCount();
+            if (numInputs != 0) {
+                mUSBLoopbackDevice.mSendPort = device.openInputPort(0);
+            }
+        }
+
+        public void startLoopbackTest() {
+            if (DEBUG) {
+                Log.i(TAG, "---- startLoopbackTest()");
+            }
+            // Setup the USB Loopback Device
+            mUSBLoopbackDevice.closePorts();
+
+            if (mIODevice.mSendDevInfo != null) {
+                mMidiManager.openDevice(
+                        mUSBLoopbackDevice.mSendDevInfo, new USBLoopbackOpenListener(), null);
+            }
+
+            // Now start the test as usual
+            super.startLoopbackTest();
+        }
+
+        /**
+         * We need this OnDeviceOpenedListener to open the USB-Loopback device
+         */
+        private class USBLoopbackOpenListener implements MidiManager.OnDeviceOpenedListener {
+            @Override
+            public void onDeviceOpened(MidiDevice device) {
+                if (DEBUG) {
+                    Log.i("USBLoopbackOpenListener", "---- onDeviceOpened()");
+                }
+                mUSBLoopbackDevice.openPorts(device, new USBMidiEchoReceiver());
+            }
+        } /* class USBLoopbackOpenListener */
+
+        /**
+         * MidiReceiver subclass for BlueTooth Loopback Test
+         *
+         * This class receives bytes from the USB Interface (presumably coming from the
+         * Bluetooth MIDI peripheral) and echoes them back out (presumably to the Bluetooth
+         * MIDI peripheral).
+         */
+        private class USBMidiEchoReceiver extends MidiReceiver {
+            private int mTotalBytesEchoed;
+
+            @Override
+            public void onSend(byte[] msg, int offset, int count, long timestamp) throws IOException {
+                mTotalBytesEchoed += count;
+                if (DEBUG) {
+                    Log.i(TAG, "---- USBMidiEchoReceiver.onSend() count:" + count +
+                            " total:" + mTotalBytesEchoed);
+                }
+                mUSBLoopbackDevice.mSendPort.onSend(msg, offset, count, timestamp);
+            }
+        } /* class USBMidiEchoReceiver */
+    } /* class BTMidiTestModule */
+} /* class MidiActivity */
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
new file mode 100644
index 0000000..30bc5fc
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
@@ -0,0 +1,379 @@
+/*
+ * 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.verifier.audio;
+
+import android.app.AlertDialog;
+import android.content.ComponentName;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.media.AudioDeviceCallback;
+import android.media.AudioDeviceInfo;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;  // needed to access resource in CTSVerifier project namespace.
+
+import java.util.List;
+
+public class ProAudioActivity
+        extends PassFailButtons.Activity
+        implements View.OnClickListener {
+    private static final String TAG = ProAudioActivity.class.getName();
+    private static final boolean DEBUG = false;
+
+    // Flags
+    private boolean mClaimsLowLatencyAudio;    // CDD ProAudio section C-1-1
+    private boolean mClaimsMIDI;               // CDD ProAudio section C-1-4
+    private boolean mClaimsUSBHostMode;        // CDD ProAudio section C-1-3
+    private boolean mClaimsUSBPeripheralMode;  // CDD ProAudio section C-1-3
+    private boolean mClaimsHDMI;               // CDD ProAudio section C-1-3
+
+    // Values
+    private static final double LATENCY_MS_LIMIT = 20.0; // CDD ProAudio section C-1-2
+    private double mRoundTripLatency;
+    private static final double CONFIDENCE_LIMIT = 0.75; // TBD
+    private double mRoundTripConfidence;
+
+    // Peripheral(s)
+    AudioManager mAudioManager;
+    private boolean mIsPeripheralAttached;  // CDD ProAudio section C-1-3
+    private AudioDeviceInfo mOutputDevInfo;
+    private AudioDeviceInfo mInputDevInfo;
+
+    private AudioDeviceInfo mHDMIDeviceInfo;
+
+    // Widgets
+    TextView mInputDeviceTxt;
+    TextView mOutputDeviceTxt;
+    TextView mRoundTripLatencyTxt;
+    TextView mRoundTripConfidenceTxt;
+    TextView mHDMISupportLbl;
+
+    CheckBox mClaimsHDMICheckBox;
+
+    public ProAudioActivity() {
+        super();
+    }
+
+    private boolean claimsLowLatencyAudio() {
+        // CDD Section C-1-1: android.hardware.audio.low_latency
+        return getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY);
+    }
+
+    private boolean claimsMIDI() {
+        // CDD Section C-1-4: android.software.midi
+        return getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI);
+    }
+
+    private boolean claimsUSBHostMode() {
+        return getPackageManager().hasSystemFeature(PackageManager.FEATURE_USB_HOST);
+    }
+
+    private boolean claimsUSBPeripheralMode() {
+        return getPackageManager().hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
+    }
+
+    private void showConnectedAudioPeripheral() {
+        mInputDeviceTxt.setText(
+                mInputDevInfo != null ? mInputDevInfo.getProductName().toString()
+                        : "");
+        mOutputDeviceTxt.setText(
+                mOutputDevInfo != null ? mOutputDevInfo.getProductName().toString()
+                        : "");
+    }
+
+    // HDMI Stuff
+    private boolean isHDMIValid() {
+        if (mHDMIDeviceInfo == null) {
+            return false;
+        }
+
+        // MUST support output in stereo and eight channels...
+        boolean has2Chans = false;
+        boolean has8Chans = false;
+        int[] channelCounts = mHDMIDeviceInfo.getChannelCounts();
+        for (int count : channelCounts) {
+            if (count == 2) {
+                has2Chans = true;
+            } else if (count == 8) {
+                has8Chans = true;
+            }
+        }
+        if (!has2Chans || !has8Chans) {
+            return false;
+        }
+
+        // at 20-bit or 24-bit depth
+        boolean hasFloatEncoding = false;
+        int[] encodings = mHDMIDeviceInfo.getEncodings();
+        for (int encoding : encodings) {
+            if (encoding == AudioFormat.ENCODING_PCM_FLOAT) {
+                hasFloatEncoding = true;
+                break;
+            }
+        }
+        if (!hasFloatEncoding) {
+            return false;
+        }
+
+         // and 192 kHz
+        boolean has192K = false;
+        int[] sampleRates = mHDMIDeviceInfo.getSampleRates();
+        for (int rate : sampleRates) {
+            if (rate >= 192000) {
+                has192K = true;
+            }
+        }
+        if (!has192K) {
+            return false;
+        }
+
+        // without bit-depth loss or resampling (hmmmmm....).
+
+        return true;
+    }
+
+    private void calculatePass() {
+        boolean hasPassed =
+                mClaimsLowLatencyAudio && mClaimsMIDI &&
+                mClaimsUSBHostMode && mClaimsUSBPeripheralMode &&
+                (!mClaimsHDMI || isHDMIValid()) &&
+                mOutputDevInfo != null && mInputDevInfo != null &&
+                mRoundTripLatency != 0.0 && mRoundTripLatency <= LATENCY_MS_LIMIT &&
+                mRoundTripConfidence >= CONFIDENCE_LIMIT;
+        getPassButton().setEnabled(hasPassed);
+    }
+
+    //
+    // Loopback App Stuff
+    //
+    private final static String LOOPBACK_PACKAGE_NAME = "org.drrickorang.loopback";
+
+    // Test Intents
+    // From Loopback App LoobackActivity.java
+    private static final String INTENT_TEST_TYPE = "TestType";
+
+    // from Loopback App Constant.java
+    public static final int LOOPBACK_PLUG_AUDIO_THREAD_TEST_TYPE_LATENCY = 222;
+
+    public boolean isLoopbackAppInstalled() {
+        try {
+            getPackageManager().getPackageInfo(
+                    LOOPBACK_PACKAGE_NAME, PackageManager.GET_ACTIVITIES);
+            return true;
+        } catch (PackageManager.NameNotFoundException e) {
+            // This indicates that the specified app (Loopback in this case) is NOT installed
+            // fall through...
+        }
+        return false;
+    }
+
+    // arbitrary request code
+    private static final int LATENCY_RESULTS_REQUEST_CODE = 1;
+    private static final String KEY_CTSINVOCATION = "CTS-Test";
+    private static final String KEY_ROUND_TRIP_TIME = "RoundTripTime";
+    private static final String KEY_ROUND_TRIP_CONFIDENCE = "Confidence";
+
+    // We may need to iterate and average round-trip measurements
+    // So add this plumbing though NOT USED.
+    private static final String KEY_NUMITERATIONS = "NumIterations";
+    private static final int NUM_ROUNDTRIPITERATIONS = 3;
+
+    private void runRoundTripTest() {
+        if (!isLoopbackAppInstalled()) {
+            Toast.makeText(this, "Loopback App not installed", Toast.LENGTH_SHORT).show();
+            return;
+        }
+
+        if (!mIsPeripheralAttached) {
+            Toast.makeText(this, "Please connect a USB audio peripheral with loopback cables" +
+                    " before running the latency test.",
+                    Toast.LENGTH_SHORT).show();
+            return;
+        }
+
+        mRoundTripLatency = 0.0;
+        mRoundTripConfidence = 0.0;
+        Intent intent = new Intent(Intent.CATEGORY_LAUNCHER);
+        intent.setComponent(
+            new ComponentName(LOOPBACK_PACKAGE_NAME,LOOPBACK_PACKAGE_NAME + ".LoopbackActivity"));
+
+        intent.putExtra(KEY_CTSINVOCATION, "CTS-Verifier Invocation");
+        intent.putExtra(INTENT_TEST_TYPE, LOOPBACK_PLUG_AUDIO_THREAD_TEST_TYPE_LATENCY);
+        intent.putExtra(KEY_NUMITERATIONS, NUM_ROUNDTRIPITERATIONS);
+
+        startActivityForResult(intent, LATENCY_RESULTS_REQUEST_CODE);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        // Check which request we're responding to
+        if (resultCode == RESULT_OK) {
+            Toast.makeText(this, "Round Trip Test Complete.", Toast.LENGTH_SHORT).show();
+            if (requestCode == LATENCY_RESULTS_REQUEST_CODE) {
+                Bundle extras = data != null ? data.getExtras() : null;
+                if (extras != null) {
+                    mRoundTripLatency =  extras.getDouble(KEY_ROUND_TRIP_TIME);
+                    mRoundTripLatencyTxt.setText(String.format("%.2f ms", mRoundTripLatency));
+                    mRoundTripConfidence = extras.getDouble(KEY_ROUND_TRIP_CONFIDENCE);
+                    mRoundTripConfidenceTxt.setText(String.format("%.2f", mRoundTripConfidence));
+                }
+            }
+            calculatePass();
+        } else {
+            Toast.makeText(this, "Round Trip Test Canceled.", Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.pro_audio);
+
+        mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE);
+        mAudioManager.registerAudioDeviceCallback(new ConnectListener(), new Handler());
+
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.proaudio_test, R.string.proaudio_info, -1);
+
+        mClaimsLowLatencyAudio = claimsLowLatencyAudio();
+        ((TextView)findViewById(R.id.proAudioHasLLALbl)).setText("" + mClaimsLowLatencyAudio);
+
+        mClaimsMIDI = claimsMIDI();
+        ((TextView)findViewById(R.id.proAudioHasMIDILbl)).setText("" + mClaimsMIDI);
+
+        mClaimsUSBHostMode = claimsUSBHostMode();
+        ((TextView)findViewById(R.id.proAudioMidiHasUSBHostLbl)).setText("" + mClaimsUSBHostMode);
+
+        mClaimsUSBPeripheralMode = claimsUSBPeripheralMode();
+        ((TextView)findViewById(
+                R.id.proAudioMidiHasUSBPeripheralLbl)).setText("" + mClaimsUSBPeripheralMode);
+
+        // Connected Device
+        mInputDeviceTxt = ((TextView)findViewById(R.id.proAudioInputLbl));
+        mOutputDeviceTxt = ((TextView)findViewById(R.id.proAudioOutputLbl));
+
+        // Round-trip Latency
+        mRoundTripLatencyTxt = (TextView)findViewById(R.id.proAudioRoundTripLbl);
+        mRoundTripConfidenceTxt = (TextView)findViewById(R.id.proAudioConfidenceLbl);
+        ((Button)findViewById(R.id.proAudio_runRoundtripBtn)).setOnClickListener(this);
+
+        // HDMI
+        mHDMISupportLbl = (TextView)findViewById(R.id.proAudioHDMISupportLbl);
+        mClaimsHDMICheckBox = (CheckBox)findViewById(R.id.proAudioHasHDMICheckBox);
+        mClaimsHDMICheckBox.setOnClickListener(this);
+
+        calculatePass();
+    }
+
+    private void scanPeripheralList(AudioDeviceInfo[] devices) {
+        // CDD Section C-1-3: USB port, host-mode support
+
+        // Can't just use the first record because then we will only get
+        // Source OR sink, not both even on devices that are both.
+        mOutputDevInfo = null;
+        mInputDevInfo = null;
+
+        // Any valid peripherals
+        // Do we leave in the Headset test to support a USB-Dongle?
+        for (AudioDeviceInfo devInfo : devices) {
+            if (devInfo.getType() == AudioDeviceInfo.TYPE_USB_DEVICE ||     // USB Peripheral
+                devInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET ||    // USB dongle+LBPlug
+                devInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET || // Loopback Plug?
+                devInfo.getType() == AudioDeviceInfo.TYPE_AUX_LINE) { // Aux-cable loopback?
+                if (devInfo.isSink()) {
+                    mOutputDevInfo = devInfo;
+                }
+                if (devInfo.isSource()) {
+                    mInputDevInfo = devInfo;
+                }
+            } else if (devInfo.isSink() && devInfo.getType() == AudioDeviceInfo.TYPE_HDMI) {
+                mHDMIDeviceInfo = devInfo;
+            }
+        }
+
+        mIsPeripheralAttached = mOutputDevInfo != null || mInputDevInfo != null;
+        showConnectedAudioPeripheral();
+
+        if (mHDMIDeviceInfo != null) {
+            mClaimsHDMICheckBox.setChecked(true);
+            mHDMISupportLbl.setText(getResources().getString(
+                    isHDMIValid() ? R.string.pass_button_text : R.string.fail_button_text));
+        }
+        mHDMISupportLbl.setText(getResources().getString(R.string.audio_proaudio_NA));
+
+        calculatePass();
+    }
+
+    private class ConnectListener extends AudioDeviceCallback {
+        /*package*/ ConnectListener() {}
+
+        //
+        // AudioDevicesManager.OnDeviceConnectionListener
+        //
+        @Override
+        public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
+            scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
+        }
+
+        @Override
+        public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
+            scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
+        }
+    }
+
+    @Override
+    public void onClick(View view) {
+        switch (view.getId()) {
+        case R.id.proAudio_runRoundtripBtn:
+            runRoundTripTest();
+            break;
+
+        case R.id.proAudioHasHDMICheckBox:
+            if (mClaimsHDMICheckBox.isChecked()) {
+                AlertDialog.Builder builder =
+                        new AlertDialog.Builder(this, android.R.style.Theme_Material_Dialog_Alert);
+                builder.setTitle(getResources().getString(R.string.proaudio_hdmi_infotitle));
+                builder.setMessage(getResources().getString(R.string.proaudio_hdmi_message));
+                builder.setPositiveButton(android.R.string.yes,
+                    new DialogInterface.OnClickListener() {
+                        public void onClick(DialogInterface dialog, int which) {}
+                 });
+                builder.setIcon(android.R.drawable.ic_dialog_alert);
+                builder.show();
+
+                mClaimsHDMI = true;
+                mHDMISupportLbl.setText(getResources().getString(R.string.audio_proaudio_pending));
+            } else {
+                mClaimsHDMI = false;
+                mHDMISupportLbl.setText(getResources().getString(R.string.audio_proaudio_NA));
+            }
+            break;
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralButtonsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralButtonsActivity.java
index 87b2149..7ee340a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralButtonsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralButtonsActivity.java
@@ -16,6 +16,8 @@
 
 package com.android.cts.verifier.audio;
 
+import android.app.AlertDialog;
+import android.content.DialogInterface;
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.os.Bundle;
@@ -45,6 +47,19 @@
     private TextView mBtnBStatusTxt;
     private TextView mBtnCStatusTxt;
 
+    private void showDisableAssistantDialog() {
+        AlertDialog.Builder builder =
+                new AlertDialog.Builder(this, android.R.style.Theme_Material_Dialog_Alert);
+        builder.setTitle(getResources().getString(R.string.uapButtonsDisableAssistantTitle));
+        builder.setMessage(getResources().getString(R.string.uapButtonsDisableAssistant));
+        builder.setPositiveButton(android.R.string.yes,
+            new DialogInterface.OnClickListener() {
+                public void onClick(DialogInterface dialog, int which) {}
+         });
+        builder.setIcon(android.R.drawable.ic_dialog_alert);
+        builder.show();
+    }
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -62,6 +77,8 @@
 
         setPassFailButtonClickListeners();
         setInfoResources(R.string.usbaudio_buttons_test, R.string.usbaudio_buttons_info, -1);
+
+        showDisableAssistantDialog();
     }
 
     private void showButtonsState() {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/midilib/MidiEchoTestService.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/midilib/MidiEchoTestService.java
new file mode 100644
index 0000000..b1d8009
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/midilib/MidiEchoTestService.java
@@ -0,0 +1,86 @@
+/*
+ * 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.verifier.midilib;
+
+import android.media.midi.MidiDeviceService;
+import android.media.midi.MidiDeviceStatus;
+import android.media.midi.MidiReceiver;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Virtual MIDI Device that copies its input to its output.
+ * This is used for loop-back testing of MIDI I/O.
+ */
+
+public class MidiEchoTestService extends MidiDeviceService {
+    private static final String TAG = "MidiEchoTestService";
+
+    // Other apps will write to this port.
+    private MidiReceiver mInputReceiver = new MyReceiver();
+    // This app will copy the data to this port.
+    private MidiReceiver mOutputReceiver;
+    private static MidiEchoTestService mInstance;
+
+    // These are public so we can easily read them from CTS test.
+    public int statusChangeCount;
+    public boolean inputOpened;
+    public int outputOpenCount;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.i(TAG, "#### onCreate()");
+        mInstance = this;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        Log.i(TAG, "#### onDestroy()");
+    }
+
+    // For CTS testing, so I can read test fields.
+    public static MidiEchoTestService getInstance() {
+        return mInstance;
+    }
+
+    @Override
+    public MidiReceiver[] onGetInputPortReceivers() {
+        return new MidiReceiver[] { mInputReceiver };
+    }
+
+    class MyReceiver extends MidiReceiver {
+        @Override
+        public void onSend(byte[] data, int offset, int count, long timestamp)
+                throws IOException {
+            if (mOutputReceiver == null) {
+                mOutputReceiver = getOutputPortReceivers()[0];
+            }
+            // Copy input to output.
+            mOutputReceiver.send(data, offset, count, timestamp);
+        }
+    }
+
+    @Override
+    public void onDeviceStatusChanged(MidiDeviceStatus status) {
+        statusChangeCount++;
+        inputOpened = status.isInputPortOpen(0);
+        outputOpenCount = status.getOutputPortOpenCount(0);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/peripheralprofile/ProfileManager.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/peripheralprofile/ProfileManager.java
index 9cec7fa..23a94f2 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/peripheralprofile/ProfileManager.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/peripheralprofile/ProfileManager.java
@@ -59,6 +59,18 @@
                 "<OutputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"44100,48000\" />" +
                 "<InputDevInfo ChanCounts=\"1,2\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"44100,48000\" />" +
             "</PeripheralProfile>" +
+            "<PeripheralProfile ProfileName=\"Focusrite 2i4\" ProfileDescription=\"Focusrite Scarlett 2i4\" ProductName=\"USB-Audio - Scarlett 2i4 USB\">" +
+                "<OutputDevInfo ChanCounts=\"2,3,4\" ChanPosMasks=\"12\" ChanIndexMasks=\"3,7,15\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000\"/>" +
+                "<InputDevInfo ChanCounts=\"1,2\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000\"/>" +
+            "</PeripheralProfile>" +
+            "<PeripheralProfile ProfileName=\"Behringer UMC204HD\" ProfileDescription=\"Behringer UMC204HD\" ProductName=\"USB-Audio - UMC204HD 192k\">" +
+                "<OutputDevInfo ChanCounts=\"2,4\" ChanPosMasks=\"12\" ChanIndexMasks=\"15\" Encodings=\"2,4\" SampleRates=\"44100,48000,88200,96000,176400,192000\"/>" +
+                "<InputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"44100,48000,88200,96000,176400,192000\"/>" +
+            "</PeripheralProfile>" +
+            "<PeripheralProfile ProfileName=\"Roland Rubix24\" ProfileDescription=\"Roland Rubix24\" ProductName=\"USB-Audio - Rubix24\">" +
+                "<OutputDevInfo ChanCounts=\"2,4\" ChanPosMasks=\"12\" ChanIndexMasks=\"15\" Encodings=\"4\" SampleRates=\"44100,48000,96000,192000\"/>" +
+                "<InputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"44100,48000,96000,192000\"/>" +
+            "</PeripheralProfile>" +
             "<PeripheralProfile ProfileName=\"Pixel USB-C Dongle + Wired Analog Headset\" ProfileDescription=\"Reference USB Dongle\" ProductName=\"USB-Audio - USB-C to 3.5mm-Headphone Adapte\">" +
                 "<OutputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"48000\" />" +
                 "<InputDevInfo ChanCounts=\"1,2\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"3\" Encodings=\"4\" SampleRates=\"48000\" />" +
@@ -74,6 +86,11 @@
                 "<InputDevInfo ChanCounts=\"1\" ChanPosMasks=\"16\" ChanIndexMasks=\"1\" Encodings=\"2\" SampleRates=\"44100,48000\" />" +
                 "<ButtonInfo HasBtnA=\"1\" HasBtnB=\"1\" HasBtnC=\"1\" />" +
             "</PeripheralProfile>" +
+            "<PeripheralProfile ProfileName=\"Libratone Q Adapt\" ProfileDescription=\"Libratone Q Adapt Headset\" ProductName=\"USB-Audio - Libratone_INEAR\">" +
+                "<OutputDevInfo ChanCounts=\"2\" ChanPosMasks=\"12\" ChanIndexMasks=\"3\" Encodings=\"2\" SampleRates=\"48000\" />" +
+                "<InputDevInfo ChanCounts=\"1,2\" ChanPosMasks=\"12,16\" ChanIndexMasks=\"1\" Encodings=\"2\" SampleRates=\"48000\" />"+
+                "<ButtonInfo HasBtnA=\"1\" HasBtnB=\"1\" HasBtnC=\"1\" />" +
+            "</PeripheralProfile>" +
         "</ProfileList>";
 
     // XML Tags and Attributes
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/OWNERS
new file mode 100644
index 0000000..008649d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/OWNERS
@@ -0,0 +1,3 @@
+etancohen@google.com
+mplass@google.com
+satk@google.com
diff --git a/apps/CtsVerifierUSBCompanion/Android.mk b/apps/CtsVerifierUSBCompanion/Android.mk
index 2e081426..c505b13 100644
--- a/apps/CtsVerifierUSBCompanion/Android.mk
+++ b/apps/CtsVerifierUSBCompanion/Android.mk
@@ -28,6 +28,7 @@
 LOCAL_PACKAGE_NAME := CtsVerifierUSBCompanion
 
 LOCAL_SDK_VERSION := 25
+#LOCAL_MIN_SDK_VERSION := 12
 
 LOCAL_DEX_PREOPT := false
 
diff --git a/apps/EmptyDeviceAdmin/Android.mk b/apps/EmptyDeviceAdmin/Android.mk
index 1d68fbb..ec4c4a3 100644
--- a/apps/EmptyDeviceAdmin/Android.mk
+++ b/apps/EmptyDeviceAdmin/Android.mk
@@ -27,6 +27,7 @@
 LOCAL_RESOURCE_DIR += $(LOCAL_PATH)/res
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 12
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/apps/EmptyDeviceOwner/Android.mk b/apps/EmptyDeviceOwner/Android.mk
index 714e996..0d85bb7 100644
--- a/apps/EmptyDeviceOwner/Android.mk
+++ b/apps/EmptyDeviceOwner/Android.mk
@@ -27,6 +27,7 @@
 LOCAL_RESOURCE_DIR += $(LOCAL_PATH)/res
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 12
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/apps/NotificationBot/Android.mk b/apps/NotificationBot/Android.mk
index 9d9c9f9..8a89b1a 100644
--- a/apps/NotificationBot/Android.mk
+++ b/apps/NotificationBot/Android.mk
@@ -28,6 +28,7 @@
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 19
 
 LOCAL_DEX_PREOPT := false
 
diff --git a/apps/PermissionApp/Android.mk b/apps/PermissionApp/Android.mk
index cf4186d..026c803 100644
--- a/apps/PermissionApp/Android.mk
+++ b/apps/PermissionApp/Android.mk
@@ -27,6 +27,7 @@
 LOCAL_PACKAGE_NAME := CtsPermissionApp
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 23
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := arcts cts vts general-tests
diff --git a/apps/VpnApp/api23/Android.mk b/apps/VpnApp/api23/Android.mk
index 9f3f2e7..3167888 100644
--- a/apps/VpnApp/api23/Android.mk
+++ b/apps/VpnApp/api23/Android.mk
@@ -27,6 +27,7 @@
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../res
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 22
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/apps/VpnApp/api24/Android.mk b/apps/VpnApp/api24/Android.mk
index fc2761c..d7fb82c 100644
--- a/apps/VpnApp/api24/Android.mk
+++ b/apps/VpnApp/api24/Android.mk
@@ -27,6 +27,7 @@
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../res
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 22
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/apps/VpnApp/latest/Android.mk b/apps/VpnApp/latest/Android.mk
index a8db2d2..4b33303 100644
--- a/apps/VpnApp/latest/Android.mk
+++ b/apps/VpnApp/latest/Android.mk
@@ -27,6 +27,7 @@
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../res
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 22
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/apps/VpnApp/notalwayson/Android.mk b/apps/VpnApp/notalwayson/Android.mk
index cc1bfec..f9adadf 100644
--- a/apps/VpnApp/notalwayson/Android.mk
+++ b/apps/VpnApp/notalwayson/Android.mk
@@ -27,6 +27,7 @@
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../res
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 22
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfo.java
index c48d16f..0268e86 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfo.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfo.java
@@ -83,14 +83,20 @@
         } else if ((dir = makeResultDir()) == null) {
             failed("Cannot create directory for device info files");
         } else {
+            File jsonFile;
             try {
-                File jsonFile = new File(dir, getClass().getSimpleName() + ".deviceinfo.json");
+                jsonFile = new File(dir, getClass().getSimpleName() + ".deviceinfo.json");
                 jsonFile.createNewFile();
                 mResultFilePath = jsonFile.getAbsolutePath();
-                DeviceInfoStore store = new DeviceInfoStore(jsonFile);
-                store.open();
-                collectDeviceInfo(store);
-                store.close();
+                try (DeviceInfoStore store = new DeviceInfoStore(jsonFile)) {
+                    store.open();
+                    collectDeviceInfo(store);
+                } finally {
+                    if (jsonFile != null && jsonFile.exists() &&
+                            jsonFile.length() == 0) {
+                        jsonFile.delete();
+                    }
+                }
                 if (mResultCode == ResultCode.STARTED) {
                     mResultCode = ResultCode.COMPLETED;
                 }
diff --git a/common/device-side/util/Android.bp b/common/device-side/util/Android.bp
index 2000cb8..be5e520 100644
--- a/common/device-side/util/Android.bp
+++ b/common/device-side/util/Android.bp
@@ -17,7 +17,7 @@
     sdk_version: "test_current",
 
     srcs: [
-       "src/**/*.java",
+        "src/**/*.java",
         "src/**/*.aidl",
     ],
 
@@ -33,4 +33,6 @@
         "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/protobuf-jarjar-rules.txt b/common/device-side/util/protobuf-jarjar-rules.txt
new file mode 100644
index 0000000..9914809
--- /dev/null
+++ b/common/device-side/util/protobuf-jarjar-rules.txt
@@ -0,0 +1,3 @@
+rule com.google.protobuf.** com.google.compatibility.device.util.com.google.protobuf.@1
+rule android.**.nano.** com.google.compatibility.device.util.android.@1.nano.@2
+rule **.android.**.nano.** com.google.compatibility.device.util.@1.android.@2.nano.@3
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
new file mode 100644
index 0000000..23e22c6
--- /dev/null
+++ b/common/device-side/util/src/com/android/compatibility/common/util/AdoptShellPermissionsRule.java
@@ -0,0 +1,62 @@
+/*
+ * 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 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(Runnable)}
+ * or {@link SystemUtil#callWithShellPermissionIdentity(java.util.concurrent.Callable)} instead.
+ */
+public class AdoptShellPermissionsRule implements TestRule {
+
+    private final UiAutomation mUiAutomation;
+
+    public AdoptShellPermissionsRule() {
+        this(InstrumentationRegistry.getInstrumentation().getUiAutomation());
+    }
+
+    public AdoptShellPermissionsRule(@NonNull UiAutomation uiAutomation) {
+        mUiAutomation = uiAutomation;
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return new Statement() {
+
+            @Override
+            public void evaluate() throws Throwable {
+                mUiAutomation.adoptShellPermissionIdentity();
+                try {
+                    base.evaluate();
+                } finally {
+                    mUiAutomation.dropShellPermissionIdentity();
+                }
+            }
+        };
+    }
+}
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
index a1adc02..6eeaae2 100644
--- 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
@@ -59,7 +59,7 @@
     public static boolean isAppStandbyEnabledAtRuntime() {
         final String result =
                 SystemUtil.runShellCommand("settings get global app_standby_enabled").trim();
-        final boolean boolResult = result.equals("1");
+        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
index 792db40..ba357a16 100644
--- 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
@@ -18,12 +18,15 @@
 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";
 
@@ -103,4 +106,15 @@
         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/DeviceInfoStore.java b/common/device-side/util/src/com/android/compatibility/common/util/DeviceInfoStore.java
index 966ac1a..03f69fa 100644
--- 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
@@ -54,7 +54,7 @@
      * Closes the writer.
      */
     @Override
-    public void close() throws IOException {
+    public void close() throws Exception {
         mJsonWriter.endObject();
         mJsonWriter.flush();
         mJsonWriter.close();
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
index 9c75975..d235c03 100644
--- 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
@@ -22,14 +22,18 @@
 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 {
@@ -118,7 +122,7 @@
     }
 
     /**
-     * Run a command and print the result on logcat.
+     * Runs a command and print the result on logcat.
      */
     public static void runCommandAndPrintOnLogcat(String logtag, String cmd) {
         Log.i(logtag, "Executing: " + cmd);
@@ -129,7 +133,7 @@
     }
 
     /**
-     * Run a command and return the section matching the patterns.
+     * Runs a command and return the section matching the patterns.
      *
      * @see TextUtils#extractSection
      */
@@ -139,4 +143,31 @@
         return TextUtils.extractSection(runShellCommand(cmd), extractionStartRegex, startInclusive,
                 extractionEndRegex, endInclusive);
     }
+
+    /**
+     * Runs a {@link Runnable} adopting Shell's permissions.
+     */
+    public static void runWithShellPermissionIdentity(@NonNull Runnable runnable) {
+        final UiAutomation automan = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        automan.adoptShellPermissionIdentity();
+        try {
+            runnable.run();
+        } 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/TextUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/TextUtils.java
index 639dc9c..cca2652 100644
--- 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
@@ -63,4 +63,15 @@
         }
         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/hostsidetests/appsecurity/Android.mk b/hostsidetests/appsecurity/Android.mk
index 10ed05f..c224903 100644
--- a/hostsidetests/appsecurity/Android.mk
+++ b/hostsidetests/appsecurity/Android.mk
@@ -32,6 +32,11 @@
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
+LOCAL_REQUIRED_MODULES := \
+	CtsCorruptApkTests_b71360999 \
+	CtsCorruptApkTests_b71361168 \
+	CtsCorruptApkTests_b79488511
+
 include $(BUILD_CTS_HOST_JAVA_LIBRARY)
 
 # Build the test APKs using their own makefiles
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
index aa7e4a0..bbfc62f 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
@@ -25,6 +25,8 @@
 
 import static org.junit.Assert.fail;
 
+import android.platform.test.annotations.AppModeFull;
+
 import com.android.tradefed.device.CollectingOutputReceiver;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -42,6 +44,7 @@
  * Set of tests that verify behavior of adopted storage media, if supported.
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
+@AppModeFull(reason = "Instant applications can only be installed on internal storage")
 public class AdoptableHostTest extends BaseHostJUnit4Test {
 
     public static final String FEATURE_ADOPTABLE_STORAGE = "feature:android.software.adoptable_storage";
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java
index 02e62d3..1754dcd 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java
@@ -16,31 +16,24 @@
 
 package android.appsecurity.cts;
 
-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 com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AppModeInstant;
 import com.android.ddmlib.Log;
-import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-import com.android.tradefed.util.AbiUtils;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.File;
-import java.io.FileNotFoundException;
-
 /**
  * Set of tests that verify various security checks involving multiple apps are
  * properly enforced.
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class AppSecurityTests extends BaseHostJUnit4Test {
+public class AppSecurityTests extends BaseAppSecurityTest {
 
     // testSharedUidDifferentCerts constants
     private static final String SHARED_UI_APK = "CtsSharedUidInstall.apk";
@@ -71,7 +64,9 @@
     private static final String TARGET_INSTRUMENT_PKG = "com.android.cts.targetinstrumentationapp";
     private static final String INSTRUMENT_DIFF_CERT_APK = "CtsInstrumentationAppDiffCert.apk";
     private static final String INSTRUMENT_DIFF_CERT_PKG =
-        "com.android.cts.instrumentationdiffcertapp";
+            "com.android.cts.instrumentationdiffcertapp";
+    private static final String INSTRUMENT_DIFF_CERT_CLASS =
+            "com.android.cts.instrumentationdiffcertapp.InstrumentationFailToRunTest";
 
     // testPermissionDiffCert constants
     private static final String DECLARE_PERMISSION_APK = "CtsPermissionDeclareApp.apk";
@@ -85,11 +80,6 @@
 
     private static final String LOG_TAG = "AppSecurityTests";
 
-    private File getTestAppFile(String fileName) throws FileNotFoundException {
-        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
-        return buildHelper.getTestFile(fileName);
-    }
-
     @Before
     public void setUp() throws Exception {
         Utils.prepareSingleUser(getDevice());
@@ -101,24 +91,16 @@
      * if it is signed with a different certificate.
      */
     @Test
+    @AppModeFull(reason = "Instant applications can't define shared UID")
     public void testSharedUidDifferentCerts() throws Exception {
         Log.i(LOG_TAG, "installing apks with shared uid, but different certs");
         try {
-            // cleanup test apps that might be installed from previous partial test run
             getDevice().uninstallPackage(SHARED_UI_PKG);
             getDevice().uninstallPackage(SHARED_UI_DIFF_CERT_PKG);
 
-            String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
-            String installResult = getDevice().installPackage(getTestAppFile(SHARED_UI_APK),
-                    false, options);
-            assertNull(String.format("failed to install shared uid app, Reason: %s", installResult),
-                    installResult);
-            installResult = getDevice().installPackage(getTestAppFile(SHARED_UI_DIFF_CERT_APK),
-                    false, options);
-            assertNotNull("shared uid app with different cert than existing app installed " +
-                    "successfully", installResult);
-            assertEquals("INSTALL_FAILED_SHARED_USER_INCOMPATIBLE",
-                    installResult.substring(0, installResult.indexOf(':')));
+            new InstallMultiple().addApk(SHARED_UI_APK).run();
+            new InstallMultiple().addApk(SHARED_UI_DIFF_CERT_APK)
+                    .runExpectingFailure("INSTALL_FAILED_SHARED_USER_INCOMPATIBLE");
         } finally {
             getDevice().uninstallPackage(SHARED_UI_PKG);
             getDevice().uninstallPackage(SHARED_UI_DIFF_CERT_PKG);
@@ -130,25 +112,27 @@
      * certificate.
      */
     @Test
-    public void testAppUpgradeDifferentCerts() throws Exception {
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testAppUpgradeDifferentCerts_full() throws Exception {
+        testAppUpgradeDifferentCerts(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testAppUpgradeDifferentCerts_instant() throws Exception {
+        testAppUpgradeDifferentCerts(true);
+    }
+    private void testAppUpgradeDifferentCerts(boolean instant) throws Exception {
         Log.i(LOG_TAG, "installing app upgrade with different certs");
         try {
-            // cleanup test app that might be installed from previous partial test run
             getDevice().uninstallPackage(SIMPLE_APP_PKG);
+            getDevice().uninstallPackage(SIMPLE_APP_DIFF_CERT_APK);
 
-            String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
-            String installResult = getDevice().installPackage(getTestAppFile(SIMPLE_APP_APK),
-                    false, options);
-            assertNull(String.format("failed to install simple app. Reason: %s", installResult),
-                    installResult);
-            installResult = getDevice().installPackage(getTestAppFile(SIMPLE_APP_DIFF_CERT_APK),
-                    true /* reinstall */, options);
-            assertNotNull("app upgrade with different cert than existing app installed " +
-                    "successfully", installResult);
-            assertEquals("INSTALL_FAILED_UPDATE_INCOMPATIBLE",
-                    installResult.substring(0, installResult.indexOf(':')));
+            new InstallMultiple(instant).addApk(SIMPLE_APP_APK).run();
+            new InstallMultiple(instant).addApk(SIMPLE_APP_DIFF_CERT_APK)
+                    .runExpectingFailure("INSTALL_FAILED_UPDATE_INCOMPATIBLE");
         } finally {
             getDevice().uninstallPackage(SIMPLE_APP_PKG);
+            getDevice().uninstallPackage(SIMPLE_APP_DIFF_CERT_APK);
         }
     }
 
@@ -156,27 +140,27 @@
      * Test that an app cannot access another app's private data.
      */
     @Test
-    public void testAppFailAccessPrivateData() throws Exception {
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testAppFailAccessPrivateData_full() throws Exception {
+        testAppFailAccessPrivateData(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testAppFailAccessPrivateData_instant() throws Exception {
+        testAppFailAccessPrivateData(true);
+    }
+    private void testAppFailAccessPrivateData(boolean instant)
+            throws Exception {
         Log.i(LOG_TAG, "installing app that attempts to access another app's private data");
         try {
-            // cleanup test app that might be installed from previous partial test run
             getDevice().uninstallPackage(APP_WITH_DATA_PKG);
             getDevice().uninstallPackage(APP_ACCESS_DATA_PKG);
 
-            String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
-            String installResult = getDevice().installPackage(getTestAppFile(APP_WITH_DATA_APK),
-                    false, options);
-            assertNull(String.format("failed to install app with data. Reason: %s", installResult),
-                    installResult);
-            // run appwithdata's tests to create private data
+            new InstallMultiple().addApk(APP_WITH_DATA_APK).run();
             runDeviceTests(APP_WITH_DATA_PKG, APP_WITH_DATA_CLASS, APP_WITH_DATA_CREATE_METHOD);
 
-            installResult = getDevice().installPackage(getTestAppFile(APP_ACCESS_DATA_APK),
-                    false, options);
-            assertNull(String.format("failed to install app access data. Reason: %s",
-                    installResult), installResult);
-            // run appaccessdata's tests which attempt to access appwithdata's private data
-            runDeviceTests(APP_ACCESS_DATA_PKG);
+            new InstallMultiple(instant).addApk(APP_ACCESS_DATA_APK).run();
+            runDeviceTests(APP_ACCESS_DATA_PKG, null, null, instant);
         } finally {
             getDevice().uninstallPackage(APP_WITH_DATA_PKG);
             getDevice().uninstallPackage(APP_ACCESS_DATA_PKG);
@@ -187,29 +171,29 @@
      * Test that uninstall of an app removes its private data.
      */
     @Test
-    public void testUninstallRemovesData() throws Exception {
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testUninstallRemovesData_full() throws Exception {
+        testUninstallRemovesData(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testUninstallRemovesData_instant() throws Exception {
+        testUninstallRemovesData(true);
+    }
+    private void testUninstallRemovesData(boolean instant) throws Exception {
         Log.i(LOG_TAG, "Uninstalling app, verifying data is removed.");
         try {
-            // cleanup test app that might be installed from previous partial test run
             getDevice().uninstallPackage(APP_WITH_DATA_PKG);
 
-            String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
-            String installResult = getDevice().installPackage(getTestAppFile(APP_WITH_DATA_APK),
-                    false, options);
-            assertNull(String.format("failed to install app with data. Reason: %s", installResult),
-                    installResult);
-            // run appwithdata's tests to create private data
-            runDeviceTests(APP_WITH_DATA_PKG, APP_WITH_DATA_CLASS, APP_WITH_DATA_CREATE_METHOD);
+            new InstallMultiple(instant).addApk(APP_WITH_DATA_APK).run();
+            runDeviceTests(
+                    APP_WITH_DATA_PKG, APP_WITH_DATA_CLASS, APP_WITH_DATA_CREATE_METHOD);
 
             getDevice().uninstallPackage(APP_WITH_DATA_PKG);
 
-            installResult = getDevice().installPackage(getTestAppFile(APP_WITH_DATA_APK),
-                    false, options);
-            assertNull(String.format("failed to install app with data second time. Reason: %s",
-                    installResult), installResult);
-            // run appwithdata's 'check if file exists' test
-            runDeviceTests(APP_WITH_DATA_PKG, APP_WITH_DATA_CLASS,
-                    APP_WITH_DATA_CHECK_NOEXIST_METHOD);
+            new InstallMultiple(instant).addApk(APP_WITH_DATA_APK).run();
+            runDeviceTests(
+                    APP_WITH_DATA_PKG, APP_WITH_DATA_CLASS, APP_WITH_DATA_CHECK_NOEXIST_METHOD);
         } finally {
             getDevice().uninstallPackage(APP_WITH_DATA_PKG);
         }
@@ -219,29 +203,35 @@
      * Test that an app cannot instrument another app that is signed with different certificate.
      */
     @Test
-    public void testInstrumentationDiffCert() throws Exception {
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testInstrumentationDiffCert_full() throws Exception {
+        testInstrumentationDiffCert(false, false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testInstrumentationDiffCert_instant() throws Exception {
+        testInstrumentationDiffCert(false, true);
+        testInstrumentationDiffCert(true, false);
+        testInstrumentationDiffCert(true, true);
+    }
+    private void testInstrumentationDiffCert(boolean targetInstant, boolean instrumentInstant)
+            throws Exception {
         Log.i(LOG_TAG, "installing app that attempts to instrument another app");
         try {
             // cleanup test app that might be installed from previous partial test run
             getDevice().uninstallPackage(TARGET_INSTRUMENT_PKG);
             getDevice().uninstallPackage(INSTRUMENT_DIFF_CERT_PKG);
 
-            String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
-            String installResult = getDevice().installPackage(
-                    getTestAppFile(TARGET_INSTRUMENT_APK), false, options);
-            assertNull(String.format("failed to install target instrumentation app. Reason: %s",
-                    installResult), installResult);
+            new InstallMultiple(targetInstant).addApk(TARGET_INSTRUMENT_APK).run();
+            new InstallMultiple(instrumentInstant).addApk(INSTRUMENT_DIFF_CERT_APK).run();
 
-            // the app will install, but will get error at runtime when starting instrumentation
-            installResult = getDevice().installPackage(getTestAppFile(INSTRUMENT_DIFF_CERT_APK),
-                    false, options);
-            assertNull(String.format(
-                    "failed to install instrumentation app with diff cert. Reason: %s",
-                    installResult), installResult);
-            // run INSTRUMENT_DIFF_CERT_PKG tests
-            // this test will attempt to call startInstrumentation directly and verify
-            // SecurityException is thrown
-            runDeviceTests(INSTRUMENT_DIFF_CERT_PKG);
+            // if we've installed either the instrumentation or target as an instant application,
+            // starting an instrumentation will just fail instead of throwing a security exception
+            // because neither the target nor instrumentation packages can see one another
+            final String methodName = (targetInstant|instrumentInstant)
+                    ? "testInstrumentationNotAllowed_fail"
+                    : "testInstrumentationNotAllowed_exception";
+            runDeviceTests(INSTRUMENT_DIFF_CERT_PKG, INSTRUMENT_DIFF_CERT_CLASS, methodName);
         } finally {
             getDevice().uninstallPackage(TARGET_INSTRUMENT_PKG);
             getDevice().uninstallPackage(INSTRUMENT_DIFF_CERT_PKG);
@@ -253,6 +243,7 @@
      * certificate than the app that declared the permission.
      */
     @Test
+    @AppModeFull(reason = "Only the platform can define permissions obtainable by instant applications")
     public void testPermissionDiffCert() throws Exception {
         Log.i(LOG_TAG, "installing app that attempts to use permission of another app");
         try {
@@ -261,24 +252,11 @@
             getDevice().uninstallPackage(DECLARE_PERMISSION_COMPAT_PKG);
             getDevice().uninstallPackage(PERMISSION_DIFF_CERT_PKG);
 
-            String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
-            String installResult = getDevice().installPackage(
-                    getTestAppFile(DECLARE_PERMISSION_APK), false, options);
-            assertNull(String.format("failed to install declare permission app. Reason: %s",
-                    installResult), installResult);
+            new InstallMultiple().addApk(DECLARE_PERMISSION_APK).run();
+            new InstallMultiple().addApk(DECLARE_PERMISSION_COMPAT_APK).run();
 
-            installResult = getDevice().installPackage(
-                    getTestAppFile(DECLARE_PERMISSION_COMPAT_APK), false, options);
-            assertNull(String.format("failed to install declare permission compat app. Reason: %s",
-                    installResult), installResult);
-
-            // the app will install, but will get error at runtime
-            installResult = getDevice().installPackage(getTestAppFile(PERMISSION_DIFF_CERT_APK),
-                    false, options);
-            assertNull(String.format("failed to install permission app with diff cert. Reason: %s",
-                    installResult), installResult);
-            // run PERMISSION_DIFF_CERT_PKG tests which try to access the permission
-            runDeviceTests(PERMISSION_DIFF_CERT_PKG);
+            new InstallMultiple().addApk(PERMISSION_DIFF_CERT_APK).run();
+            runDeviceTests(PERMISSION_DIFF_CERT_PKG, null);
         } finally {
             getDevice().uninstallPackage(DECLARE_PERMISSION_PKG);
             getDevice().uninstallPackage(DECLARE_PERMISSION_COMPAT_PKG);
@@ -290,13 +268,20 @@
      * Tests that an arbitrary file cannot be installed using the 'cmd' command.
      */
     @Test
-    public void testAdbInstallFile() throws Exception {
-        String output = getDevice().executeShellCommand(
-                "cmd package install -S 1024 /data/local/tmp/foo.apk");
-        assertTrue("Error text", output.contains("Error"));
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testAdbInstallFile_full() throws Exception {
+        testAdbInstallFile(false);
     }
-
-    private void runDeviceTests(String packageName) throws DeviceNotAvailableException {
-        runDeviceTests(packageName, null);
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testAdbInstallFile_instant() throws Exception {
+        testAdbInstallFile(true);
+    }
+    private void testAdbInstallFile(boolean instant) throws Exception {
+        String output = getDevice().executeShellCommand(
+                "cmd package install"
+                        + (instant ? " --instant" : " --full")
+                        + " -S 1024 /data/local/tmp/foo.apk");
+        assertTrue("Error text", output.contains("Error"));
     }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java
index 5e2a97b..822edce 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java
@@ -16,7 +16,6 @@
 
 package android.appsecurity.cts;
 
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
@@ -24,6 +23,7 @@
 import org.junit.Before;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 
 /**
  * Base class.
@@ -38,7 +38,7 @@
     private ArrayList<Integer> mFixedUsers;
 
     @Before
-    public void setUp() throws Exception {
+    public void setUpBaseAppSecurityTest() throws Exception {
         Assert.assertNotNull(getBuild()); // ensure build has been set before test is run.
 
         mSupportsMultiUser = getDevice().getMaxNumberOfUsersSupported() > 1;
@@ -52,7 +52,7 @@
         getDevice().switchUser(mPrimaryUserId);
     }
 
-    private boolean checkIfSplitSystemUser() throws DeviceNotAvailableException {
+    private boolean checkIfSplitSystemUser() throws Exception {
         final String commandOuput = getDevice().executeShellCommand(
                 "getprop ro.fw.system_user_split");
         return "y".equals(commandOuput) || "yes".equals(commandOuput)
@@ -61,19 +61,48 @@
     }
 
     protected void installTestAppForUser(String apk, int userId) throws Exception {
+        installTestAppForUser(apk, false, userId);
+    }
+
+    protected void installTestAppForUser(String apk, boolean instant, int userId) throws Exception {
         if (userId < 0) {
             userId = mPrimaryUserId;
         }
-        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
-        Assert.assertNull(getDevice().installPackageForUser(
-                buildHelper.getTestFile(apk), true, false, userId, "-t"));
+        new InstallMultiple(instant)
+                .addApk(apk)
+                .allowTest()
+                .forUser(userId)
+                .run();
+    }
+
+    // TODO: We should be able to set test arguments from the BaseHostJUnit4Test methods
+    protected void runDeviceTests(String packageName, String testClassName,
+            String testMethodName, boolean instant) throws DeviceNotAvailableException {
+        final HashMap<String, String> testArgs;
+        if (instant) {
+            testArgs = new HashMap<>();
+            testArgs.put("is_instant", Boolean.TRUE.toString());
+        } else {
+            testArgs = null;
+        }
+        Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName, testArgs);
     }
 
     protected boolean isAppVisibleForUser(String packageName, int userId,
-            boolean matchUninstalled) throws DeviceNotAvailableException {
+            boolean matchUninstalled) throws Exception {
         String command = "cmd package list packages --user " + userId;
         if (matchUninstalled) command += " -u";
         String output = getDevice().executeShellCommand(command);
         return output.contains(packageName);
     }
+
+    protected class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
+        public InstallMultiple() {
+            this(false);
+        }
+        public InstallMultiple(boolean instant) {
+            super(getDevice(), getBuild(), getAbi());
+            addArg(instant ? "--instant" : "");
+        }
+    }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java
index cc87584..b403f37 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java
@@ -78,6 +78,11 @@
         return (T) this;
     }
 
+    T allowTest() {
+        addArg("-t");
+        return (T) this;
+    }
+
     T locationAuto() {
         addArg("--install-location 0");
         return (T) this;
@@ -98,15 +103,24 @@
         return (T) this;
     }
 
+    T forUser(int userId) {
+        addArg("--user " + userId);
+        return (T) this;
+    }
+
     void run() throws DeviceNotAvailableException {
-        run(true);
+        run(true, null);
     }
 
     void runExpectingFailure() throws DeviceNotAvailableException {
-        run(false);
+        run(false, null);
     }
 
-    private void run(boolean expectingSuccess) throws DeviceNotAvailableException {
+    void runExpectingFailure(String failure) throws DeviceNotAvailableException {
+        run(false, failure);
+    }
+
+    private void run(boolean expectingSuccess, String failure) throws DeviceNotAvailableException {
         final ITestDevice device = mDevice;
 
         // Create an install session
@@ -159,11 +173,15 @@
         cmd.append("pm install-commit");
         cmd.append(' ').append(sessionId);
 
-        result = device.executeShellCommand(cmd.toString());
-        if (expectingSuccess) {
-            TestCase.assertTrue(result, result.startsWith("Success"));
+        result = device.executeShellCommand(cmd.toString()).trim();
+        if (failure == null) {
+            if (expectingSuccess) {
+                TestCase.assertTrue(result, result.startsWith("Success"));
+            } else {
+                TestCase.assertFalse(result, result.startsWith("Success"));
+            }
         } else {
-            TestCase.assertFalse(result, result.startsWith("Success"));
+            TestCase.assertTrue(result, result.contains(failure));
         }
     }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ClassloaderSplitsTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ClassloaderSplitsTest.java
index f4f6d9e..4e54bc4 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ClassloaderSplitsTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ClassloaderSplitsTest.java
@@ -15,9 +15,9 @@
  */
 package android.appsecurity.cts;
 
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AppModeInstant;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.IBuildReceiver;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import org.junit.After;
 import org.junit.Before;
@@ -25,7 +25,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class ClassloaderSplitsTest extends BaseHostJUnit4Test implements IBuildReceiver {
+public class ClassloaderSplitsTest extends BaseAppSecurityTest {
     private static final String PKG = "com.android.cts.classloadersplitapp";
     private static final String TEST_CLASS = PKG + ".SplitAppTest";
 
@@ -57,36 +57,63 @@
     }
 
     @Test
-    public void testBaseClassLoader() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).run();
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testBaseClassLoader_full() throws Exception {
+        testBaseClassLoader(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testBaseClassLoader_instant() throws Exception {
+        testBaseClassLoader(true);
+    }
+    private void testBaseClassLoader(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_BASE).run();
         runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
     }
 
     @Test
-    public void testFeatureAClassLoader() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).run();
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testFeatureAClassLoader_full() throws Exception {
+        testFeatureAClassLoader(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testFeatureAClassLoader_instant() throws Exception {
+        testFeatureAClassLoader(true);
+    }
+    private void testFeatureAClassLoader(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_BASE).addApk(APK_FEATURE_A).run();
         runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
         runDeviceTests(getDevice(), PKG, TEST_CLASS, "testFeatureAClassLoader");
     }
 
     @Test
-    public void testFeatureBClassLoader() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B).run();
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testFeatureBClassLoader_full() throws Exception {
+        testFeatureBClassLoader(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testFeatureBClassLoader_instant() throws Exception {
+        testFeatureBClassLoader(true);
+    }
+    private void testFeatureBClassLoader(boolean instant) throws Exception {
+        new InstallMultiple(instant)
+                .addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B).run();
         runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
         runDeviceTests(getDevice(), PKG, TEST_CLASS, "testFeatureAClassLoader");
         runDeviceTests(getDevice(), PKG, TEST_CLASS, "testFeatureBClassLoader");
     }
 
     @Test
-    public void testReceiverClassLoaders() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B).run();
+    @AppModeFull(reason = "b/109878606; instant applications can't send broadcasts to manifest receivers")
+    public void testReceiverClassLoaders_full() throws Exception {
+        testReceiverClassLoaders(false);
+    }
+    private void testReceiverClassLoaders(boolean instant) throws Exception {
+        new InstallMultiple(instant)
+                .addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B).run();
         runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
         runDeviceTests(getDevice(), PKG, TEST_CLASS, "testAllReceivers");
     }
-
-    private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
-        public InstallMultiple() {
-            super(getDevice(), getBuild(), null);
-        }
-    }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/CorruptApkTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/CorruptApkTests.java
index d09c525..eec0878 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/CorruptApkTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/CorruptApkTests.java
@@ -15,15 +15,11 @@
  */
 package android.appsecurity.cts;
 
-import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 
 import android.platform.test.annotations.AppModeFull;
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.result.InputStreamSource;
-import com.android.tradefed.testtype.DeviceTestCase;
-import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.util.FileUtil;
 
 import org.junit.After;
@@ -35,104 +31,81 @@
  * Set of tests that verify that corrupt APKs are properly rejected by PackageManager and
  * do not cause the system to crash.
  */
-@AppModeFull // TODO: Needs porting to instant
-public class CorruptApkTests extends DeviceTestCase implements IBuildReceiver {
+@AppModeFull(reason = "the corrupt APKs were provided as-is and we cannot modify them to comply with instant mode")
+public class CorruptApkTests extends BaseAppSecurityTest {
     private final String B71360999_PKG = "com.android.appsecurity.b71360999";
     private final String B71361168_PKG = "com.android.appsecurity.b71361168";
     private final String B79488511_PKG = "com.android.appsecurity.b79488511";
 
-    private IBuildInfo mBuildInfo;
-
-    @Override
-    public void setBuild(IBuildInfo buildInfo) {
-        mBuildInfo = buildInfo;
-    }
-
+    private final String B71360999_APK = "CtsCorruptApkTests_b71360999.apk";
+    private final String B71361168_APK = "CtsCorruptApkTests_b71361168.apk";
+    private final String B79488511_APK = "CtsCorruptApkTests_b79488511.apk";
     @Before
-    @Override
     public void setUp() throws Exception {
-        super.setUp();
-        uninstall(B71360999_PKG);
-        uninstall(B71361168_PKG);
-        uninstall(B79488511_PKG);
+        getDevice().uninstallPackage(B71360999_PKG);
+        getDevice().uninstallPackage(B71361168_PKG);
+        getDevice().uninstallPackage(B79488511_PKG);
     }
 
     @After
-    @Override
     public void tearDown() throws Exception {
-        super.tearDown();
-        uninstall(B71360999_PKG);
-        uninstall(B71361168_PKG);
-        uninstall(B79488511_PKG);
-    }
-
-    /** Uninstall the apk if the test failed previously. */
-    public void uninstall(String pkg) throws Exception {
-        ITestDevice device = getDevice();
-        if (device.getInstalledPackageNames().contains(pkg)) {
-            device.uninstallPackage(pkg);
-        }
+        getDevice().uninstallPackage(B71360999_PKG);
+        getDevice().uninstallPackage(B71361168_PKG);
+        getDevice().uninstallPackage(B79488511_PKG);
     }
 
     /**
      * Tests that apks described in b/71360999 do not install successfully.
      */
     public void testFailToInstallCorruptStringPoolHeader_b71360999() throws Exception {
-        final String APK_PATH = "CtsCorruptApkTests_b71360999.apk";
-        assertInstallNoFatalError(APK_PATH, B71360999_PKG);
+        if (getDevice().getApiLevel() < 28) {
+            return;
+        }
+        assertInstallWithoutFatalError(B71360999_APK, B71360999_PKG);
     }
 
     /**
      * Tests that apks described in b/71361168 do not install successfully.
      */
     public void testFailToInstallCorruptStringPoolHeader_b71361168() throws Exception {
-        final String APK_PATH = "CtsCorruptApkTests_b71361168.apk";
-        assertInstallNoFatalError(APK_PATH, B71361168_PKG);
+        if (getDevice().getApiLevel() < 28) {
+            return;
+        }
+        assertInstallWithoutFatalError(B71361168_APK, B71361168_PKG);
     }
 
     /**
      * Tests that apks described in b/79488511 do not install successfully.
      */
     public void testFailToInstallCorruptStringPoolHeader_b79488511() throws Exception {
-        final String APK_PATH = "CtsCorruptApkTests_b79488511.apk";
-        assertInstallNoFatalError(APK_PATH, B79488511_PKG);
+        if (getDevice().getApiLevel() < 28) {
+            return;
+        }
+        assertInstallWithoutFatalError(B79488511_APK, B79488511_PKG);
     }
 
     /**
-     * Assert that installing the app does not cause a native error caused by a buffer overflow
-     * or an out-of-bounds read.
-     **/
-    private void assertInstallNoFatalError(String filename, String pkg) throws Exception {
-        ITestDevice device = getDevice();
-        device.clearLogcat();
+     * Asserts that installing the application does not cause a native error [typically
+     * the result of a buffer overflow or an out-of-bounds read].
+     */
+    private void assertInstallWithoutFatalError(String apk, String pkg) throws Exception {
+        getDevice().clearLogcat();
 
-        final String result = device.installPackage(
-                new CompatibilityBuildHelper(mBuildInfo).getTestFile(filename),
-                true /*reinstall*/);
-
-        // Starting from P, corrupt apks should always fail to install
-        if (device.getApiLevel() >= 28) {
-            assertThat(result).isNotNull();
-            assertThat(result).isNotEmpty();
-            assertThat(device.getInstalledPackageNames()).doesNotContain(pkg);
-        }
+        new InstallMultiple().addApk(apk).runExpectingFailure();
 
         // This catches if the device fails to install the app because a segmentation fault
         // or out of bounds read created by the bug occurs
-        File tmpTxtFile = null;
-        InputStreamSource source = device.getLogcat(200 * 1024);
+        final File tmpTxtFile = FileUtil.createTempFile("logcat", ".txt");
+        final InputStreamSource source = getDevice().getLogcat(200 * 1024);
         try {
             assertNotNull(source);
-            tmpTxtFile = FileUtil.createTempFile("logcat", ".txt");
             FileUtil.writeToFile(source.createInputStream(), tmpTxtFile);
-            String s = FileUtil.readStringFromFile(tmpTxtFile);
+            final String s = FileUtil.readStringFromFile(tmpTxtFile);
             assertFalse(s.contains("SIGSEGV"));
             assertFalse(s.contains("==ERROR"));
         } finally {
             source.close();
-            if (tmpTxtFile != null) {
-                FileUtil.deleteFile(tmpTxtFile);
-            }
+            FileUtil.deleteFile(tmpTxtFile);
         }
     }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
index 56d605e..f98406c 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
@@ -16,6 +16,7 @@
 
 package android.appsecurity.cts;
 
+import android.platform.test.annotations.AppModeFull;
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.DeviceNotAvailableException;
@@ -33,6 +34,7 @@
 /**
  * Tests for ephemeral packages.
  */
+@AppModeFull(reason = "Already handles instant installs when needed")
 public class EphemeralTest extends DeviceTestCase
         implements IAbiReceiver, IBuildReceiver {
 
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantAppUserTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantAppUserTest.java
index 4609e8a..a22ee80 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantAppUserTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantAppUserTest.java
@@ -16,6 +16,7 @@
 
 package android.appsecurity.cts;
 
+import android.platform.test.annotations.AppModeFull;
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.DeviceNotAvailableException;
@@ -29,6 +30,7 @@
 /**
  * Tests for ephemeral packages.
  */
+@AppModeFull(reason = "Already handles instant installs when needed")
 public class InstantAppUserTest extends DeviceTestCase
         implements IAbiReceiver, IBuildReceiver {
 
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantCookieHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantCookieHostTest.java
index 97658eb..81c552b 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantCookieHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantCookieHostTest.java
@@ -16,6 +16,7 @@
 
 package android.appsecurity.cts;
 
+import android.platform.test.annotations.AppModeFull;
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.DeviceNotAvailableException;
@@ -25,6 +26,7 @@
 /**
  * Tests for the instant cookie APIs
  */
+@AppModeFull(reason = "Already handles instant installs when needed")
 public class InstantCookieHostTest extends DeviceTestCase implements IBuildReceiver {
     private static final String INSTANT_COOKIE_APP_APK = "CtsInstantCookieApp.apk";
     private static final String INSTANT_COOKIE_APP_PKG = "test.instant.cookie";
@@ -32,9 +34,6 @@
     private static final String INSTANT_COOKIE_APP_APK_2 = "CtsInstantCookieApp2.apk";
     private static final String INSTANT_COOKIE_APP_PKG_2 = "test.instant.cookie";
 
-    // Wait time in msec to make sure the cookie is stored on disk.
-    private static final int WAIT_COOKIE_STORED = 1000;
-
     private CompatibilityBuildHelper mBuildHelper;
 
     @Override
@@ -69,7 +68,6 @@
                 "testCookiePersistedAcrossInstantInstalls1");
         uninstallPackage(INSTANT_COOKIE_APP_PKG);
         assertNull(installPackage(INSTANT_COOKIE_APP_APK, false, true));
-        Thread.sleep(WAIT_COOKIE_STORED);
         runDeviceTests(INSTANT_COOKIE_APP_PKG, "test.instant.cookie.CookieTest",
                 "testCookiePersistedAcrossInstantInstalls2");
     }
@@ -79,7 +77,6 @@
         runDeviceTests(INSTANT_COOKIE_APP_PKG, "test.instant.cookie.CookieTest",
                 "testCookiePersistedUpgradeFromInstant1");
         assertNull(installPackage(INSTANT_COOKIE_APP_APK, true, false));
-        Thread.sleep(WAIT_COOKIE_STORED);
         runDeviceTests(INSTANT_COOKIE_APP_PKG, "test.instant.cookie.CookieTest",
                 "testCookiePersistedUpgradeFromInstant2");
     }
@@ -90,18 +87,16 @@
                 "testCookieResetOnNonInstantReinstall1");
         uninstallPackage(INSTANT_COOKIE_APP_PKG);
         assertNull(installPackage(INSTANT_COOKIE_APP_APK, true, false));
-        Thread.sleep(WAIT_COOKIE_STORED);
         runDeviceTests(INSTANT_COOKIE_APP_PKG, "test.instant.cookie.CookieTest",
                 "testCookieResetOnNonInstantReinstall2");
     }
 
-    public void testCookieValidWhenSingedWithTwoCerts() throws Exception {
+    public void testCookieValidWhenSignedWithTwoCerts() throws Exception {
         assertNull(installPackage(INSTANT_COOKIE_APP_APK, false, true));
         runDeviceTests(INSTANT_COOKIE_APP_PKG, "test.instant.cookie.CookieTest",
                 "testCookiePersistedAcrossInstantInstalls1");
         uninstallPackage(INSTANT_COOKIE_APP_PKG);
         assertNull(installPackage(INSTANT_COOKIE_APP_APK_2, true, true));
-        Thread.sleep(WAIT_COOKIE_STORED);
         runDeviceTests(INSTANT_COOKIE_APP_PKG_2, "test.instant.cookie.CookieTest",
                 "testCookiePersistedAcrossInstantInstalls2");
     }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/IsolatedSplitsTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/IsolatedSplitsTests.java
index 8795fe8..3bc1654 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/IsolatedSplitsTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/IsolatedSplitsTests.java
@@ -15,11 +15,17 @@
  */
 package android.appsecurity.cts;
 
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.testtype.DeviceTestCase;
-import com.android.tradefed.testtype.IBuildReceiver;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AppModeInstant;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
-public class IsolatedSplitsTests extends DeviceTestCase implements IBuildReceiver {
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class IsolatedSplitsTests extends BaseAppSecurityTest {
     private static final String PKG = "com.android.cts.isolatedsplitapp";
     private static final String TEST_CLASS = PKG + ".SplitAppTest";
 
@@ -38,68 +44,129 @@
     private static final String APK_FEATURE_C = "CtsIsolatedSplitAppFeatureC.apk";
     private static final String APK_FEATURE_C_pl = "CtsIsolatedSplitAppFeatureC_pl.apk";
 
-    private IBuildInfo mBuildInfo;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
+    @Before
+    public void setUp() throws Exception {
         Utils.prepareSingleUser(getDevice());
         getDevice().uninstallPackage(PKG);
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
+    @After
+    public void tearDown() throws Exception {
         getDevice().uninstallPackage(PKG);
     }
 
-    public void testInstallBase() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).run();
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testInstallBase_full() throws Exception {
+        testInstallBase(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testInstallBase_instant() throws Exception {
+        testInstallBase(true);
+    }
+    private void testInstallBase(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_BASE).run();
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadDefault");
     }
 
-    public void testInstallBaseAndConfigSplit() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).addApk(APK_BASE_pl).run();
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testInstallBaseAndConfigSplit_full() throws Exception {
+        testInstallBaseAndConfigSplit(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testInstallBaseAndConfigSplit_instant() throws Exception {
+        testInstallBaseAndConfigSplit(true);
+    }
+    private void testInstallBaseAndConfigSplit(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_BASE).addApk(APK_BASE_pl).run();
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadPolishLocale");
     }
 
-    public void testInstallMissingDependency() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_B).runExpectingFailure();
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testInstallMissingDependency_full() throws Exception {
+        testInstallMissingDependency(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testInstallMissingDependency_instant() throws Exception {
+        testInstallMissingDependency(true);
+    }
+    private void testInstallMissingDependency(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_BASE).addApk(APK_FEATURE_B).runExpectingFailure();
     }
 
-    public void testInstallOneFeatureSplit() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).run();
+    @Test
+    @AppModeFull(reason = "b/109878606; instant applications can't send broadcasts to manifest receivers")
+    public void testInstallOneFeatureSplit_full() throws Exception {
+        testInstallOneFeatureSplit(false);
+    }
+    private void testInstallOneFeatureSplit(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_BASE).addApk(APK_FEATURE_A).run();
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadDefault");
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureADefault");
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureAReceivers");
     }
 
-    public void testInstallOneFeatureSplitAndConfigSplits() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_BASE_pl)
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testInstallOneFeatureSplitAndConfigSplits_full() throws Exception {
+        testInstallOneFeatureSplitAndConfigSplits(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testInstallOneFeatureSplitAndConfigSplits_instant() throws Exception {
+        testInstallOneFeatureSplitAndConfigSplits(true);
+    }
+    private void testInstallOneFeatureSplitAndConfigSplits(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_BASE_pl)
                 .addApk(APK_FEATURE_A_pl).run();
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadPolishLocale");
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureAPolishLocale");
     }
 
-    public void testInstallDependentFeatureSplits() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B).run();
+    @Test
+    @AppModeFull(reason = "b/109878606; instant applications can't send broadcasts to manifest receivers")
+    public void testInstallDependentFeatureSplits_full() throws Exception {
+        testInstallDependentFeatureSplits(false);
+    }
+    private void testInstallDependentFeatureSplits(boolean instant) throws Exception {
+        new InstallMultiple(instant)
+                .addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B).run();
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadDefault");
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureADefault");
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureBDefault");
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureAAndBReceivers");
     }
 
-    public void testInstallDependentFeatureSplitsAndConfigSplits() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B)
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testInstallDependentFeatureSplitsAndConfigSplits_full() throws Exception {
+        testInstallDependentFeatureSplitsAndConfigSplits(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testInstallDependentFeatureSplitsAndConfigSplits_instant() throws Exception {
+        testInstallDependentFeatureSplitsAndConfigSplits(true);
+    }
+    private void testInstallDependentFeatureSplitsAndConfigSplits(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B)
                 .addApk(APK_BASE_pl).addApk(APK_FEATURE_A_pl).addApk(APK_FEATURE_B_pl).run();
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadPolishLocale");
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureAPolishLocale");
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureBPolishLocale");
     }
 
-    public void testInstallAllFeatureSplits() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B)
+    @Test
+    @AppModeFull(reason = "b/109878606; instant applications can't send broadcasts to manifest receivers")
+    public void testInstallAllFeatureSplits_full() throws Exception {
+        testInstallAllFeatureSplits(false);
+    }
+    private void testInstallAllFeatureSplits(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B)
                 .addApk(APK_FEATURE_C).run();
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadDefault");
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureADefault");
@@ -108,8 +175,18 @@
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureAAndBAndCReceivers");
     }
 
-    public void testInstallAllFeatureSplitsAndConfigSplits() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B)
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testInstallAllFeatureSplitsAndConfigSplits_full() throws Exception {
+        testInstallAllFeatureSplitsAndConfigSplits(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testInstallAllFeatureSplitsAndConfigSplits_instant() throws Exception {
+        testInstallAllFeatureSplitsAndConfigSplits(true);
+    }
+    private void testInstallAllFeatureSplitsAndConfigSplits(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B)
                 .addApk(APK_FEATURE_C).addApk(APK_BASE_pl).addApk(APK_FEATURE_A_pl)
                 .addApk(APK_FEATURE_C_pl).run();
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadDefault");
@@ -117,15 +194,4 @@
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureBDefault");
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureCDefault");
     }
-
-    @Override
-    public void setBuild(IBuildInfo buildInfo) {
-        mBuildInfo = buildInfo;
-    }
-
-    private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
-        public InstallMultiple() {
-            super(getDevice(), mBuildInfo, null);
-        }
-    }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/MajorVersionTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/MajorVersionTest.java
index 15c3d3c..ec113cb 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/MajorVersionTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/MajorVersionTest.java
@@ -16,118 +16,125 @@
 
 package android.appsecurity.cts;
 
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
-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 static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AppModeInstant;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Test that install of apps using major version codes is being handled properly.
  */
-public class MajorVersionTest extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class MajorVersionTest extends BaseAppSecurityTest {
     private static final String PKG = "com.android.cts.majorversion";
     private static final String APK_000000000000ffff = "CtsMajorVersion000000000000ffff.apk";
     private static final String APK_00000000ffffffff = "CtsMajorVersion00000000ffffffff.apk";
     private static final String APK_000000ff00000000 = "CtsMajorVersion000000ff00000000.apk";
     private static final String APK_000000ffffffffff = "CtsMajorVersion000000ffffffffff.apk";
 
-    private IAbi mAbi;
-    private CompatibilityBuildHelper mBuildHelper;
-
-    @Override
-    public void setAbi(IAbi abi) {
-        mAbi = abi;
-    }
-
-    @Override
-    public void setBuild(IBuildInfo buildInfo) {
-        mBuildHelper = new CompatibilityBuildHelper(buildInfo);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
+    @Before
+    public void setUp() throws Exception {
         Utils.prepareSingleUser(getDevice());
-        assertNotNull(mAbi);
-        assertNotNull(mBuildHelper);
-
         getDevice().uninstallPackage(PKG);
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-
+    @After
+    public void tearDown() throws Exception {
         getDevice().uninstallPackage(PKG);
     }
 
-    public void testInstallMinorVersion() throws Exception {
-        assertNull(getDevice().installPackage(
-                mBuildHelper.getTestFile(APK_000000000000ffff), false, false));
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testInstallMinorVersion_full() throws Exception {
+        testInstallMinorVersion(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testInstallMinorVersion_instant() throws Exception {
+        testInstallMinorVersion(true);
+    }
+    private void testInstallMinorVersion(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_000000000000ffff).run();
         assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
         runVersionDeviceTests("testCheckVersion");
-        getDevice().uninstallPackage(PKG);
     }
 
-    public void testInstallMajorVersion() throws Exception {
-        assertNull(getDevice().installPackage(
-                mBuildHelper.getTestFile(APK_000000ff00000000), false, false));
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testInstallMajorVersion_full() throws Exception {
+        testInstallMajorVersion(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testInstallMajorVersion_instant() throws Exception {
+        testInstallMajorVersion(true);
+    }
+    private void testInstallMajorVersion(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_000000ff00000000).run();
         assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
         runVersionDeviceTests("testCheckVersion");
-        getDevice().uninstallPackage(PKG);
     }
 
-    public void testInstallUpdateAcrossMinorMajorVersion() throws Exception {
-        assertNull(getDevice().installPackage(
-                mBuildHelper.getTestFile(APK_000000000000ffff), false, false));
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testInstallUpdateAcrossMinorMajorVersion_full() throws Exception {
+        testInstallUpdateAcrossMinorMajorVersion(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testInstallUpdateAcrossMinorMajorVersion_instant() throws Exception {
+        testInstallUpdateAcrossMinorMajorVersion(true);
+    }
+    private void testInstallUpdateAcrossMinorMajorVersion(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_000000000000ffff).run();
         assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
         runVersionDeviceTests("testCheckVersion");
-        assertNull(getDevice().installPackage(
-                mBuildHelper.getTestFile(APK_00000000ffffffff), true, false));
+        new InstallMultiple(instant).addApk(APK_00000000ffffffff).run();
         assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
         runVersionDeviceTests("testCheckVersion");
-        assertNull(getDevice().installPackage(
-                mBuildHelper.getTestFile(APK_000000ff00000000), true, false));
+        new InstallMultiple(instant).addApk(APK_000000ff00000000).run();
         assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
         runVersionDeviceTests("testCheckVersion");
-        assertNull(getDevice().installPackage(
-                mBuildHelper.getTestFile(APK_000000ffffffffff), true, false));
+        new InstallMultiple(instant).addApk(APK_000000ffffffffff).run();
         assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
         runVersionDeviceTests("testCheckVersion");
-        getDevice().uninstallPackage(PKG);
     }
 
-    public void testInstallDowngradeAcrossMajorMinorVersion() throws Exception {
-        assertNull(getDevice().installPackage(
-                mBuildHelper.getTestFile(APK_000000ffffffffff), false, false));
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testInstallDowngradeAcrossMajorMinorVersion_full() throws Exception {
+        testInstallDowngradeAcrossMajorMinorVersion(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testInstallDowngradeAcrossMajorMinorVersion_instant() throws Exception {
+        testInstallDowngradeAcrossMajorMinorVersion(true);
+    }
+    private void testInstallDowngradeAcrossMajorMinorVersion(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_000000ffffffffff).run();
         assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
         runVersionDeviceTests("testCheckVersion");
-        assertEquals("INSTALL_FAILED_VERSION_DOWNGRADE", getDevice().installPackage(
-                mBuildHelper.getTestFile(APK_00000000ffffffff), true, false));
+        new InstallMultiple(instant).addApk(APK_00000000ffffffff)
+                .runExpectingFailure("INSTALL_FAILED_VERSION_DOWNGRADE");
         assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
         runVersionDeviceTests("testCheckVersion");
-        assertEquals("INSTALL_FAILED_VERSION_DOWNGRADE", getDevice().installPackage(
-                mBuildHelper.getTestFile(APK_000000ff00000000), true, false));
+        new InstallMultiple(instant).addApk(APK_000000ff00000000)
+                .runExpectingFailure("INSTALL_FAILED_VERSION_DOWNGRADE");
         assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
         runVersionDeviceTests("testCheckVersion");
-        assertEquals("INSTALL_FAILED_VERSION_DOWNGRADE", getDevice().installPackage(
-                mBuildHelper.getTestFile(APK_000000000000ffff), true, false));
+        new InstallMultiple(instant).addApk(APK_000000000000ffff)
+                .runExpectingFailure("INSTALL_FAILED_VERSION_DOWNGRADE");
         assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
         runVersionDeviceTests("testCheckVersion");
-        getDevice().uninstallPackage(PKG);
     }
 
-    private void runVersionDeviceTests(String testMethodName)
-            throws DeviceNotAvailableException {
+    private void runVersionDeviceTests(String testMethodName) throws Exception {
         runDeviceTests(PKG, PKG + ".VersionTest", testMethodName);
     }
-
-    private void runDeviceTests(String packageName, String testClassName, String testMethodName)
-            throws DeviceNotAvailableException {
-        Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
-    }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/OverlayHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/OverlayHostTest.java
index cf903db..0a2c5ce 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/OverlayHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/OverlayHostTest.java
@@ -15,38 +15,38 @@
  */
 package android.appsecurity.cts;
 
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.testtype.DeviceTestCase;
-import com.android.tradefed.testtype.IBuildReceiver;
+import static org.junit.Assert.assertFalse;
 
-public class OverlayHostTest extends DeviceTestCase implements IBuildReceiver {
+import android.platform.test.annotations.AppModeFull;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+@AppModeFull(reason = "Overlays cannot be instant apps")
+public class OverlayHostTest extends BaseAppSecurityTest {
     private static final String PKG = "com.android.cts.overlayapp";
     private static final String APK = "CtsOverlayApp.apk";
-    private CompatibilityBuildHelper mBuildHelper;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+    @Before
+    public void setUp() throws Exception {
         getDevice().uninstallPackage(PKG);
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void tearDown() throws Exception {
         getDevice().uninstallPackage(PKG);
-        super.tearDown();
     }
 
-    @Override
-    public void setBuild(IBuildInfo buildInfo) {
-        mBuildHelper = new CompatibilityBuildHelper(buildInfo);
-    }
-
+    @Test
     public void testInstallingOverlayHasNoEffect() throws Exception {
         assertFalse(getDevice().getInstalledPackageNames().contains(PKG));
 
         // Try to install the overlay, but expect an error.
-        assertNotNull(getDevice().installPackage(mBuildHelper.getTestFile(APK), false, false));
+        new InstallMultiple().addApk(APK).runExpectingFailure();
 
         // The install should have failed.
         assertFalse(getDevice().getInstalledPackageNames().contains(PKG));
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageResolutionHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageResolutionHostTest.java
index 680798a..e081d62 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageResolutionHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageResolutionHostTest.java
@@ -16,11 +16,8 @@
 
 package android.appsecurity.cts;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.tradefed.device.DeviceNotAvailableException;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AppModeInstant;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
 import org.junit.After;
@@ -38,12 +35,10 @@
     private static final String TINY_PKG = "android.appsecurity.cts.orderedactivity";
 
     private String mOldVerifierValue;
-    private CompatibilityBuildHelper mBuildHelper;
 
     @Before
     public void setUp() throws Exception {
         getDevice().uninstallPackage(TINY_PKG);
-        mBuildHelper = new CompatibilityBuildHelper(getBuild());
     }
 
     @After
@@ -52,26 +47,56 @@
     }
 
     @Test
-    public void testResolveOrderedActivity() throws Exception {
-        getDevice().installPackage(mBuildHelper.getTestFile(TINY_APK), true);
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testResolveOrderedActivity_full() throws Exception {
+        testResolveOrderedActivity(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testResolveOrderedActivity_instant() throws Exception {
+        testResolveOrderedActivity(true);
+    }
+    private void testResolveOrderedActivity(boolean instant) throws Exception {
+        new InstallMultiple(instant)
+                .addApk(TINY_APK)
+                .run();
         Utils.runDeviceTests(getDevice(), TINY_PKG,
                 ".PackageResolutionTest", "queryActivityOrdered");
-        getDevice().uninstallPackage(TINY_PKG);
     }
 
     @Test
-    public void testResolveOrderedService() throws Exception {
-        getDevice().installPackage(mBuildHelper.getTestFile(TINY_APK), true);
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testResolveOrderedService_full() throws Exception {
+        testResolveOrderedService(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testResolveOrderedService_instant() throws Exception {
+        testResolveOrderedService(true);
+    }
+    private void testResolveOrderedService(boolean instant) throws Exception {
+        new InstallMultiple(instant)
+                .addApk(TINY_APK)
+                .run();
         Utils.runDeviceTests(getDevice(), TINY_PKG,
                 ".PackageResolutionTest", "queryServiceOrdered");
-        getDevice().uninstallPackage(TINY_PKG);
     }
 
     @Test
-    public void testResolveOrderedReceiver() throws Exception {
-        getDevice().installPackage(mBuildHelper.getTestFile(TINY_APK), true);
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testResolveOrderedReceiver_full() throws Exception {
+        testResolveOrderedReceiver(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testResolveOrderedReceiver_instant() throws Exception {
+        testResolveOrderedReceiver(true);
+    }
+    private void testResolveOrderedReceiver(boolean instant) throws Exception {
+        new InstallMultiple(instant)
+                .addApk(TINY_APK)
+                .run();
         Utils.runDeviceTests(getDevice(), TINY_PKG,
                 ".PackageResolutionTest", "queryReceiverOrdered");
-        getDevice().uninstallPackage(TINY_PKG);
     }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageVisibilityTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageVisibilityTest.java
index 5bb70d1..c8c1e67 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageVisibilityTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageVisibilityTest.java
@@ -19,7 +19,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tradefed.device.DeviceNotAvailableException;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AppModeInstant;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
 import org.junit.After;
@@ -66,7 +67,16 @@
     }
 
     @Test
-    public void testUninstalledPackageVisibility() throws Exception {
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testUninstalledPackageVisibility_full() throws Exception {
+        testUninstalledPackageVisibility(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testUninstalledPackageVisibility_instant() throws Exception {
+        testUninstalledPackageVisibility(true);
+    }
+    private void testUninstalledPackageVisibility(boolean instant) throws Exception {
         if (!mSupportsMultiUser) {
             return;
         }
@@ -135,8 +145,7 @@
         getDevice().uninstallPackage(TEST_PKG);
     }
 
-    protected void uninstallWithKeepDataForUser(String packageName, int userId)
-            throws DeviceNotAvailableException {
+    private void uninstallWithKeepDataForUser(String packageName, int userId) throws Exception {
         final String command = "pm uninstall -k --user " + userId + " " + packageName;
         getDevice().executeShellCommand(command);
     }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
index e4b0f09..188c064 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
@@ -16,6 +16,7 @@
 
 package android.appsecurity.cts;
 
+import android.platform.test.annotations.AppModeFull;
 import android.platform.test.annotations.Presubmit;
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
@@ -38,6 +39,8 @@
     private static final String APK_23 = "CtsUsePermissionApp23.apk";
     private static final String APK_25 = "CtsUsePermissionApp25.apk";
     private static final String APK_26 = "CtsUsePermissionApp26.apk";
+    private static final String APK_28 = "CtsUsePermissionApp28.apk";
+    private static final String APK_P0 = "CtsUsePermissionAppP0.apk";
     private static final String APK_Latest = "CtsUsePermissionAppLatest.apk";
 
     private static final String APK_PERMISSION_POLICY_25 = "CtsPermissionPolicyTest25.apk";
@@ -69,6 +72,18 @@
         mBuildHelper = new CompatibilityBuildHelper(buildInfo);
     }
 
+    /**
+     * Approve the review permission prompt
+     */
+    private void approveReviewPermissionDialog() throws Exception {
+        assertNull(getDevice().installPackage(
+                mBuildHelper.getTestFile("ReviewPermissionHelper.apk"), true, true));
+
+        runDeviceTests("com.android.cts.reviewpermissionhelper",
+                "com.android.cts.reviewpermissionhelper.ReviewPermissionsTest",
+                "approveReviewPermissions");
+    }
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -135,14 +150,22 @@
         }
     }
 
+    @AppModeFull(reason = "Instant applications must be at least SDK 26")
     public void testCompatDefault22() throws Exception {
         assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_22), false, false));
+
+        approveReviewPermissionDialog();
+
         runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest22",
                 "testCompatDefault");
     }
 
+    @AppModeFull(reason = "Instant applications must be at least SDK 26")
     public void testCompatRevoked22() throws Exception {
         assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_22), false, false));
+
+        approveReviewPermissionDialog();
+
         boolean didThrow = false;
         try {
             runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest22",
@@ -157,8 +180,12 @@
                 "testCompatRevoked_part2");
     }
 
+    @AppModeFull(reason = "Instant applications must be at least SDK 26")
     public void testNoRuntimePrompt22() throws Exception {
         assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_22), false, false));
+
+        approveReviewPermissionDialog();
+
         runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest22",
                 "testNoRuntimePrompt");
     }
@@ -282,6 +309,9 @@
 
     public void testUpgradeKeepsPermissions() throws Exception {
         assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_22), false, false));
+
+        approveReviewPermissionDialog();
+
         runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest22",
                 "testAllPermissionsGrantedByDefault");
         assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_23), true, false));
@@ -314,6 +344,9 @@
 
     public void testRevokePropagatedOnUpgradeOldToNewModel() throws Exception {
         assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_22), false, false));
+
+        approveReviewPermissionDialog();
+
         boolean didThrow = false;
         try {
             runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest22",
@@ -399,6 +432,54 @@
                 "testAccessSerialPermissionNeeded");
     }
 
+    public void testPermissionSplit28() throws Exception {
+        assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_28), false, false));
+        runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest28",
+                "testLocationPermissionWasSplit");
+    }
+
+    public void testPermissionNotSplitP0() throws Exception {
+        assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_P0), false, false));
+        runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTestP0",
+                "locationPermissionIsNotSplit");
+    }
+
+    public void testRequestOnlyBackgroundNotPossible() throws Exception {
+        assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_P0), false, false));
+        runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTestP0",
+                "requestOnlyBackgroundNotPossible");
+    }
+
+    public void testRequestBoth() throws Exception {
+        assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_P0), false, false));
+        runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTestP0",
+                "requestBoth");
+    }
+
+    public void testRequestBothInSequence() throws Exception {
+        assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_P0), false, false));
+        runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTestP0",
+                "requestBothInSequence");
+    }
+
+    public void testRequestBothButGrantInSequence() throws Exception {
+        assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_P0), false, false));
+        runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTestP0",
+                "requestBothButGrantInSequence");
+    }
+
+    public void testDenyBackgroundWithPrejudice() throws Exception {
+        assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_P0), false, false));
+        runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTestP0",
+                "denyBackgroundWithPrejudice");
+    }
+
+    public void testPermissionNotSplitLatest() throws Exception {
+        assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_Latest), false, false));
+        runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTestP0",
+                "locationPermissionIsNotSplit");
+    }
+
     private void runDeviceTests(String packageName, String testClassName, String testMethodName)
             throws DeviceNotAvailableException {
         Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java
index 66cee05..aa28a97 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java
@@ -16,6 +16,7 @@
 
 package android.appsecurity.cts;
 
+import android.platform.test.annotations.AppModeFull;
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.ddmlib.Log;
 import com.android.tradefed.build.IBuildInfo;
@@ -31,6 +32,7 @@
 /**
  * Tests that verify intent filters.
  */
+@AppModeFull(reason="Instant applications can never be system or privileged")
 public class PrivilegedUpdateTests extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
     private static final String TAG = "PrivilegedUpdateTests";
     private static final String SHIM_PKG = "com.android.cts.priv.ctsshim";
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java
index acec1fb..051fcf8 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java
@@ -16,19 +16,24 @@
 
 package android.appsecurity.cts;
 
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
-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 static org.junit.Assert.assertNotNull;
+
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AppModeInstant;
+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.HashMap;
 
 /**
  * Tests that verify installing of various split APKs from host side.
  */
-public class SplitTests extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class SplitTests extends BaseAppSecurityTest {
     static final String PKG_NO_RESTART = "com.android.cts.norestart";
     static final String APK_NO_RESTART_BASE = "CtsNoRestartBase.apk";
     static final String APK_NO_RESTART_FEATURE = "CtsNoRestartFeature.apk";
@@ -79,50 +84,61 @@
         ABI_TO_APK.put("mips", APK_mips);
     }
 
-    private IAbi mAbi;
-    private IBuildInfo mCtsBuild;
-
-    @Override
-    public void setAbi(IAbi abi) {
-        mAbi = abi;
-    }
-
-    @Override
-    public void setBuild(IBuildInfo buildInfo) {
-        mCtsBuild = buildInfo;
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
+    @Before
+    public void setUp() throws Exception {
         Utils.prepareSingleUser(getDevice());
-        assertNotNull(mAbi);
-        assertNotNull(mCtsBuild);
-
-        getDevice().uninstallPackage(PKG);
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-
         getDevice().uninstallPackage(PKG);
         getDevice().uninstallPackage(PKG_NO_RESTART);
     }
 
-    public void testSingleBase() throws Exception {
-        new InstallMultiple().addApk(APK).run();
+    @After
+    public void tearDown() throws Exception {
+        getDevice().uninstallPackage(PKG);
+        getDevice().uninstallPackage(PKG_NO_RESTART);
+    }
+
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testSingleBase_full() throws Exception {
+        testSingleBase(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testSingleBase_instant() throws Exception {
+        testSingleBase(true);
+    }
+    private void testSingleBase(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).run();
         runDeviceTests(PKG, CLASS, "testSingleBase");
     }
 
-    public void testDensitySingle() throws Exception {
-        new InstallMultiple().addApk(APK).addApk(APK_mdpi).run();
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testDensitySingle_full() throws Exception {
+        testDensitySingle(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testDensitySingle_instant() throws Exception {
+        testDensitySingle(true);
+    }
+    private void testDensitySingle(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).addApk(APK_mdpi).run();
         runDeviceTests(PKG, CLASS, "testDensitySingle");
     }
 
-    public void testDensityAll() throws Exception {
-        new InstallMultiple().addApk(APK).addApk(APK_mdpi).addApk(APK_hdpi).addApk(APK_xhdpi)
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testDensityAll_full() throws Exception {
+        testDensityAll(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testDensityAll_instant() throws Exception {
+        testDensityAll(true);
+    }
+    private void testDensityAll(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).addApk(APK_mdpi).addApk(APK_hdpi).addApk(APK_xhdpi)
                 .addApk(APK_xxhdpi).run();
         runDeviceTests(PKG, CLASS, "testDensityAll");
     }
@@ -131,12 +147,22 @@
      * Install first with low-resolution resources, then add a split that offers
      * higher-resolution resources.
      */
-    public void testDensityBest() throws Exception {
-        new InstallMultiple().addApk(APK).addApk(APK_mdpi).run();
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testDensityBest_full() throws Exception {
+        testDensityBest(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testDensityBest_instant() throws Exception {
+        testDensityBest(true);
+    }
+    private void testDensityBest(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).addApk(APK_mdpi).run();
         runDeviceTests(PKG, CLASS, "testDensityBest1");
 
         // Now splice in an additional split which offers better resources
-        new InstallMultiple().inheritFrom(PKG).addApk(APK_xxhdpi).run();
+        new InstallMultiple(instant).inheritFrom(PKG).addApk(APK_xxhdpi).run();
         runDeviceTests(PKG, CLASS, "testDensityBest2");
     }
 
@@ -144,13 +170,33 @@
      * Verify that an API-based split can change enabled/disabled state of
      * manifest elements.
      */
-    public void testApi() throws Exception {
-        new InstallMultiple().addApk(APK).addApk(APK_v7).run();
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testApi_full() throws Exception {
+        testApi(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testApi_instant() throws Exception {
+        testApi(true);
+    }
+    private void testApi(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).addApk(APK_v7).run();
         runDeviceTests(PKG, CLASS, "testApi");
     }
 
-    public void testLocale() throws Exception {
-        new InstallMultiple().addApk(APK).addApk(APK_de).addApk(APK_fr).run();
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testLocale_full() throws Exception {
+        testLocale(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testLocale_instant() throws Exception {
+        testLocale(true);
+    }
+    private void testLocale(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).addApk(APK_de).addApk(APK_fr).run();
         runDeviceTests(PKG, CLASS, "testLocale");
     }
 
@@ -158,12 +204,22 @@
      * Install test app with <em>single</em> split that exactly matches the
      * currently active ABI. This also explicitly forces ABI when installing.
      */
-    public void testNativeSingle() throws Exception {
-        final String abi = mAbi.getName();
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testNativeSingle_full() throws Exception {
+        testNativeSingle(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testNativeSingle_instant() throws Exception {
+        testNativeSingle(true);
+    }
+    private void testNativeSingle(boolean instant) throws Exception {
+        final String abi = getAbi().getName();
         final String apk = ABI_TO_APK.get(abi);
         assertNotNull("Failed to find APK for ABI " + abi, apk);
 
-        new InstallMultiple().addApk(APK).addApk(apk).run();
+        new InstallMultiple(instant).addApk(APK).addApk(apk).run();
         runDeviceTests(PKG, CLASS, "testNative");
     }
 
@@ -173,12 +229,22 @@
      * installing, instead exercising the system's ability to choose the ABI
      * through inspection of the installed app.
      */
-    public void testNativeSingleNatural() throws Exception {
-        final String abi = mAbi.getName();
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testNativeSingleNatural_full() throws Exception {
+        testNativeSingleNatural(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testNativeSingleNatural_instant() throws Exception {
+        testNativeSingleNatural(true);
+    }
+    private void testNativeSingleNatural(boolean instant) throws Exception {
+        final String abi = getAbi().getName();
         final String apk = ABI_TO_APK.get(abi);
         assertNotNull("Failed to find APK for ABI " + abi, apk);
 
-        new InstallMultiple().useNaturalAbi().addApk(APK).addApk(apk).run();
+        new InstallMultiple(instant).useNaturalAbi().addApk(APK).addApk(apk).run();
         runDeviceTests(PKG, CLASS, "testNative");
     }
 
@@ -186,8 +252,18 @@
      * Install test app with <em>all</em> possible ABI splits. This also
      * explicitly forces ABI when installing.
      */
-    public void testNativeAll() throws Exception {
-        final InstallMultiple inst = new InstallMultiple().addApk(APK);
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testNativeAll_full() throws Exception {
+        testNativeAll(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testNativeAll_instant() throws Exception {
+        testNativeAll(true);
+    }
+    private void testNativeAll(boolean instant) throws Exception {
+        final InstallMultiple inst = new InstallMultiple(instant).addApk(APK);
         for (String apk : ABI_TO_APK.values()) {
             inst.addApk(apk);
         }
@@ -201,8 +277,18 @@
      * system's ability to choose the ABI through inspection of the installed
      * app.
      */
-    public void testNativeAllNatural() throws Exception {
-        final InstallMultiple inst = new InstallMultiple().useNaturalAbi().addApk(APK);
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testNativeAllNatural_full() throws Exception {
+        testNativeAllNatural(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testNativeAllNatural_instant() throws Exception {
+        testNativeAllNatural(true);
+    }
+    private void testNativeAllNatural(boolean instant) throws Exception {
+        final InstallMultiple inst = new InstallMultiple(instant).useNaturalAbi().addApk(APK);
         for (String apk : ABI_TO_APK.values()) {
             inst.addApk(apk);
         }
@@ -210,104 +296,241 @@
         runDeviceTests(PKG, CLASS, "testNative");
     }
 
-    public void testDuplicateBase() throws Exception {
-        new InstallMultiple().addApk(APK).addApk(APK).runExpectingFailure();
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testDuplicateBase_full() throws Exception {
+        testDuplicateBase(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testDuplicateBase_instant() throws Exception {
+        testDuplicateBase(true);
+    }
+    private void testDuplicateBase(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).addApk(APK).runExpectingFailure();
     }
 
-    public void testDuplicateSplit() throws Exception {
-        new InstallMultiple().addApk(APK).addApk(APK_v7).addApk(APK_v7).runExpectingFailure();
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testDuplicateSplit_full() throws Exception {
+        testDuplicateSplit(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testDuplicateSplit_instant() throws Exception {
+        testDuplicateSplit(true);
+    }
+    private void testDuplicateSplit(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).addApk(APK_v7).addApk(APK_v7).runExpectingFailure();
     }
 
-    public void testDiffCert() throws Exception {
-        new InstallMultiple().addApk(APK).addApk(APK_DIFF_CERT_v7).runExpectingFailure();
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testDiffCert_full() throws Exception {
+        testDiffCert(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testDiffCert_instant() throws Exception {
+        testDiffCert(true);
+    }
+    private void testDiffCert(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).addApk(APK_DIFF_CERT_v7).runExpectingFailure();
     }
 
-    public void testDiffCertInherit() throws Exception {
-        new InstallMultiple().addApk(APK).run();
-        new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_CERT_v7).runExpectingFailure();
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testDiffCertInherit_full() throws Exception {
+        testDiffCertInherit(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testDiffCertInherit_instant() throws Exception {
+        testDiffCertInherit(true);
+    }
+    private void testDiffCertInherit(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).run();
+        new InstallMultiple(instant).inheritFrom(PKG).addApk(APK_DIFF_CERT_v7).runExpectingFailure();
     }
 
-    public void testDiffVersion() throws Exception {
-        new InstallMultiple().addApk(APK).addApk(APK_DIFF_VERSION_v7).runExpectingFailure();
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testDiffVersion_full() throws Exception {
+        testDiffVersion(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testDiffVersion_instant() throws Exception {
+        testDiffVersion(true);
+    }
+    private void testDiffVersion(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).addApk(APK_DIFF_VERSION_v7).runExpectingFailure();
     }
 
-    public void testDiffVersionInherit() throws Exception {
-        new InstallMultiple().addApk(APK).run();
-        new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_VERSION_v7).runExpectingFailure();
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testDiffVersionInherit_full() throws Exception {
+        testDiffVersionInherit(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testDiffVersionInherit_instant() throws Exception {
+        testDiffVersionInherit(true);
+    }
+    private void testDiffVersionInherit(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).run();
+        new InstallMultiple(instant).inheritFrom(PKG).addApk(APK_DIFF_VERSION_v7).runExpectingFailure();
     }
 
-    public void testDiffRevision() throws Exception {
-        new InstallMultiple().addApk(APK).addApk(APK_DIFF_REVISION_v7).run();
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testDiffRevision_full() throws Exception {
+        testDiffRevision(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testDiffRevision_instant() throws Exception {
+        testDiffRevision(true);
+    }
+    private void testDiffRevision(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).addApk(APK_DIFF_REVISION_v7).run();
         runDeviceTests(PKG, CLASS, "testRevision0_12");
     }
 
-    public void testDiffRevisionInheritBase() throws Exception {
-        new InstallMultiple().addApk(APK).addApk(APK_v7).run();
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testDiffRevisionInheritBase_full() throws Exception {
+        testDiffRevisionInheritBase(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testDiffRevisionInheritBase_instant() throws Exception {
+        testDiffRevisionInheritBase(true);
+    }
+    private void testDiffRevisionInheritBase(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).addApk(APK_v7).run();
         runDeviceTests(PKG, CLASS, "testRevision0_0");
-        new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_REVISION_v7).run();
+        new InstallMultiple(instant).inheritFrom(PKG).addApk(APK_DIFF_REVISION_v7).run();
         runDeviceTests(PKG, CLASS, "testRevision0_12");
     }
 
-    public void testDiffRevisionInheritSplit() throws Exception {
-        new InstallMultiple().addApk(APK).addApk(APK_v7).run();
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testDiffRevisionInheritSplit_full() throws Exception {
+        testDiffRevisionInheritSplit(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testDiffRevisionInheritSplit_instant() throws Exception {
+        testDiffRevisionInheritSplit(true);
+    }
+    private void testDiffRevisionInheritSplit(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).addApk(APK_v7).run();
         runDeviceTests(PKG, CLASS, "testRevision0_0");
-        new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_REVISION).run();
+        new InstallMultiple(instant).inheritFrom(PKG).addApk(APK_DIFF_REVISION).run();
         runDeviceTests(PKG, CLASS, "testRevision12_0");
     }
 
-    public void testDiffRevisionDowngrade() throws Exception {
-        new InstallMultiple().addApk(APK).addApk(APK_DIFF_REVISION_v7).run();
-        new InstallMultiple().inheritFrom(PKG).addApk(APK_v7).runExpectingFailure();
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testDiffRevisionDowngrade_full() throws Exception {
+        testDiffRevisionDowngrade(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testDiffRevisionDowngrade_instant() throws Exception {
+        testDiffRevisionDowngrade(true);
+    }
+    private void testDiffRevisionDowngrade(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).addApk(APK_DIFF_REVISION_v7).run();
+        new InstallMultiple(instant).inheritFrom(PKG).addApk(APK_v7).runExpectingFailure();
     }
 
-    public void testFeatureBase() throws Exception {
-        new InstallMultiple().addApk(APK).addApk(APK_FEATURE).run();
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testFeatureBase_full() throws Exception {
+        testFeatureBase(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testFeatureBase_instant() throws Exception {
+        testFeatureBase(true);
+    }
+    private void testFeatureBase(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).addApk(APK_FEATURE).run();
         runDeviceTests(PKG, CLASS, "testFeatureBase");
     }
 
-    public void testFeatureApi() throws Exception {
-        new InstallMultiple().addApk(APK).addApk(APK_FEATURE).addApk(APK_FEATURE_v7).run();
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testFeatureApi_full() throws Exception {
+        testFeatureApi(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testFeatureApi_instant() throws Exception {
+        testFeatureApi(true);
+    }
+    private void testFeatureApi(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).addApk(APK_FEATURE).addApk(APK_FEATURE_v7).run();
         runDeviceTests(PKG, CLASS, "testFeatureApi");
     }
 
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
     public void testInheritUpdatedBase() throws Exception {
         // TODO: flesh out this test
     }
 
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
     public void testInheritUpdatedSplit() throws Exception {
         // TODO: flesh out this test
     }
 
-    public void testFeatureWithoutRestart() throws Exception {
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testFeatureWithoutRestart_full() throws Exception {
+        testFeatureWithoutRestart(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testFeatureWithoutRestart_instant() throws Exception {
+        testFeatureWithoutRestart(true);
+    }
+    private void testFeatureWithoutRestart(boolean instant) throws Exception {
+        // always install as a full app; we're testing that the instant app can be
+        // updated without restarting and need a broadcast receiver to ensure the
+        // correct behaviour. So, this component must be visible to instant apps.
         new InstallMultiple().addApk(APK).run();
-        new InstallMultiple().addApk(APK_NO_RESTART_BASE).run();
-        runDeviceTests(PKG, CLASS, "testBaseInstalled");
-        new InstallMultiple()
+
+        new InstallMultiple(instant).addApk(APK_NO_RESTART_BASE).run();
+        runDeviceTests(PKG, CLASS, "testBaseInstalled", instant);
+        new InstallMultiple(instant)
                 .addArg("--dont-kill")
                 .inheritFrom(PKG_NO_RESTART)
                 .addApk(APK_NO_RESTART_FEATURE)
                 .run();
-        runDeviceTests(PKG, CLASS, "testFeatureInstalled");
+        runDeviceTests(PKG, CLASS, "testFeatureInstalled", instant);
     }
 
     /**
      * Verify that installing a new version of app wipes code cache.
      */
-    public void testClearCodeCache() throws Exception {
-        new InstallMultiple().addApk(APK).run();
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    public void testClearCodeCache_full() throws Exception {
+        testClearCodeCache(false);
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    public void testClearCodeCache_instant() throws Exception {
+        testClearCodeCache(true);
+    }
+    private void testClearCodeCache(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).run();
         runDeviceTests(PKG, CLASS, "testCodeCacheWrite");
-        new InstallMultiple().addArg("-r").addApk(APK_DIFF_VERSION).run();
+        new InstallMultiple(instant).addArg("-r").addApk(APK_DIFF_VERSION).run();
         runDeviceTests(PKG, CLASS, "testCodeCacheRead");
     }
-
-    private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
-        public InstallMultiple() {
-            super(getDevice(), mCtsBuild, mAbi);
-        }
-    }
-
-    public void runDeviceTests(String packageName, String testClassName, String testMethodName)
-            throws DeviceNotAvailableException {
-        Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
-    }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java
index 4afeb9bf..41ce7a1 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java
@@ -16,72 +16,67 @@
 
 package android.appsecurity.cts;
 
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
-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 android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AppModeInstant;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Set of tests that verify behavior of runtime permissions, including both
  * dynamic granting and behavior of legacy apps.
  */
-public class UsesLibraryHostTest extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
+@AppModeFull(reason = "TODO verify whether or not these should run in instant mode")
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class UsesLibraryHostTest extends BaseAppSecurityTest {
     private static final String PKG = "com.android.cts.useslibrary";
 
     private static final String APK = "CtsUsesLibraryApp.apk";
     private static final String APK_COMPAT = "CtsUsesLibraryAppCompat.apk";
 
-    private IAbi mAbi;
-    private CompatibilityBuildHelper mBuildHelper;
 
-    @Override
-    public void setAbi(IAbi abi) {
-        mAbi = abi;
-    }
-
-    @Override
-    public void setBuild(IBuildInfo buildInfo) {
-        mBuildHelper = new CompatibilityBuildHelper(buildInfo);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
+    @Before
+    public void setUp() throws Exception {
         Utils.prepareSingleUser(getDevice());
-        assertNotNull(mAbi);
-        assertNotNull(mBuildHelper);
-
         getDevice().uninstallPackage(PKG);
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-
+    @After
+    public void tearDown() throws Exception {
         getDevice().uninstallPackage(PKG);
     }
 
-    public void testUsesLibrary() throws Exception {
-        assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK), false, false));
-        runDeviceTests(PKG, ".UsesLibraryTest", "testUsesLibrary");
+    @Test
+    @AppModeFull
+    public void testUsesLibrary_full() throws Exception {
+        testUsesLibrary(false);
+    }
+    private void testUsesLibrary(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).run();
+        Utils.runDeviceTests(getDevice(), PKG, ".UsesLibraryTest", "testUsesLibrary");
     }
 
-    public void testMissingLibrary() throws Exception {
-        assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK), false, false));
-        runDeviceTests(PKG, ".UsesLibraryTest", "testMissingLibrary");
+    @Test
+    @AppModeFull
+    public void testMissingLibrary_full() throws Exception {
+        testMissingLibrary(false);
+    }
+    public void testMissingLibrary(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).run();
+        Utils.runDeviceTests(getDevice(), PKG, ".UsesLibraryTest", "testMissingLibrary");
     }
 
-    public void testDuplicateLibrary() throws Exception {
-        assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK), false, false));
-        runDeviceTests(PKG, ".UsesLibraryTest", "testDuplicateLibrary");
+    @Test
+    @AppModeFull
+    public void testDuplicateLibrary_full() throws Exception {
+        testDuplicateLibrary(false);
     }
-
-    private void runDeviceTests(String packageName, String testClassName, String testMethodName)
-            throws DeviceNotAvailableException {
-        Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
+    public void testDuplicateLibrary(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).run();
+        Utils.runDeviceTests(getDevice(), PKG, ".UsesLibraryTest", "testDuplicateLibrary");
     }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/Utils.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/Utils.java
index c63720f..d5aa6e1 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/Utils.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/Utils.java
@@ -26,6 +26,7 @@
 import com.android.tradefed.result.TestRunResult;
 
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
diff --git a/hostsidetests/appsecurity/test-apps/AccessSerialLegacy/Android.mk b/hostsidetests/appsecurity/test-apps/AccessSerialLegacy/Android.mk
index bfb88f0..a85305d 100644
--- a/hostsidetests/appsecurity/test-apps/AccessSerialLegacy/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/AccessSerialLegacy/Android.mk
@@ -26,6 +26,7 @@
 
 LOCAL_PACKAGE_NAME := CtsAccessSerialLegacy
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 27
 
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
diff --git a/hostsidetests/appsecurity/test-apps/AccessSerialModern/Android.mk b/hostsidetests/appsecurity/test-apps/AccessSerialModern/Android.mk
index 4bdb346..dd24d5b 100644
--- a/hostsidetests/appsecurity/test-apps/AccessSerialModern/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/AccessSerialModern/Android.mk
@@ -27,6 +27,7 @@
 
 LOCAL_PACKAGE_NAME := CtsAccessSerialModern
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 27
 
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
diff --git a/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml
index 6a846fc..6fb5ce5 100644
--- a/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-       package="com.android.cts.appaccessdata">
+       package="com.android.cts.appaccessdata"
+       android:targetSandboxVersion="2">
 
     <!--
     A simple app to test that other apps cannot access another app's private data.
diff --git a/hostsidetests/appsecurity/test-apps/AppAccessData/src/com/android/cts/appaccessdata/AccessPrivateDataTest.java b/hostsidetests/appsecurity/test-apps/AppAccessData/src/com/android/cts/appaccessdata/AccessPrivateDataTest.java
index 7a64583..54e3946 100644
--- a/hostsidetests/appsecurity/test-apps/AppAccessData/src/com/android/cts/appaccessdata/AccessPrivateDataTest.java
+++ b/hostsidetests/appsecurity/test-apps/AppAccessData/src/com/android/cts/appaccessdata/AccessPrivateDataTest.java
@@ -16,6 +16,10 @@
 
 package com.android.cts.appaccessdata;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -24,7 +28,11 @@
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.SystemClock;
-import android.test.AndroidTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -41,7 +49,8 @@
  *
  * Assumes that {@link #APP_WITH_DATA_PKG} has already created the private and public data.
  */
-public class AccessPrivateDataTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessPrivateDataTest {
 
     /**
      * The Android package name of the application that owns the data
@@ -68,6 +77,7 @@
      * and detailed traffic stats.
      * @throws IOException
      */
+    @Test
     public void testAccessPrivateData() throws IOException {
         try {
             // construct the absolute file path to the app's private file
@@ -84,7 +94,7 @@
 
     private ApplicationInfo getApplicationInfo(String packageName) {
         try {
-            return mContext.getPackageManager().getApplicationInfo(packageName, 0);
+            return InstrumentationRegistry.getContext().getPackageManager().getApplicationInfo(packageName, 0);
         } catch (PackageManager.NameNotFoundException e) {
             throw new IllegalStateException("Expected package not found: " + e);
         }
@@ -94,6 +104,7 @@
      * Tests that another app's public file can be accessed
      * @throws IOException
      */
+    @Test
     public void testAccessPublicData() throws IOException {
         try {
             // construct the absolute file path to the other app's public file
@@ -108,6 +119,7 @@
         }
     }
 
+    @Test
     public void testAccessProcQtaguidTrafficStatsFailed() {
         // For untrusted app with SDK P or above, proc/net/xt_qtaguid files are no long readable.
         // They can only read their own stats from TrafficStats API. The test for TrafficStats API
@@ -116,10 +128,11 @@
             new File(QTAGUID_STATS_FILE).canRead());
     }
 
+    @Test
     public void testAccessPrivateTrafficStats() {
         int otherAppUid = -1;
         try {
-            otherAppUid = getContext()
+            otherAppUid = InstrumentationRegistry.getContext()
                     .createPackageContext(APP_WITH_DATA_PKG, 0 /*flags*/)
                     .getApplicationInfo().uid;
         } catch (NameNotFoundException e) {
@@ -133,7 +146,14 @@
         assertEquals(UNSUPPORTED, TrafficStats.getUidTxPackets(otherAppUid));
     }
 
+    @Test
     public void testTrafficStatsStatsUidSelf() throws Exception {
+        final boolean isInstant = Boolean.parseBoolean(
+                InstrumentationRegistry.getArguments().getString("is_instant"));
+        // test not applicable for instant applications; they cannot shift blame
+        if (isInstant) {
+            return;
+        }
         final int uid = android.os.Process.myUid();
         final long rxb = TrafficStats.getUidRxBytes(uid);
         final long rxp = TrafficStats.getUidRxPackets(uid);
@@ -141,7 +161,7 @@
         final long txp = TrafficStats.getUidTxPackets(uid);
 
         // Start remote server
-        final int port = getContext().getContentResolver().call(PRIVATE_TARGET, "start", null, null)
+        final int port = InstrumentationRegistry.getContext().getContentResolver().call(PRIVATE_TARGET, "start", null, null)
                 .getInt("port");
 
         // Try talking to them, but shift blame
@@ -151,7 +171,7 @@
 
             Bundle extras = new Bundle();
             extras.putParcelable("fd", ParcelFileDescriptor.fromSocket(socket));
-            getContext().getContentResolver().call(PRIVATE_TARGET, "tag", null, extras);
+            InstrumentationRegistry.getContext().getContentResolver().call(PRIVATE_TARGET, "tag", null, extras);
 
             socket.connect(new InetSocketAddress("localhost", port));
 
@@ -163,7 +183,7 @@
         } catch (IOException e) {
             throw new RuntimeException(e);
         } finally {
-            getContext().getContentResolver().call(PRIVATE_TARGET, "stop", null, null);
+            InstrumentationRegistry.getContext().getContentResolver().call(PRIVATE_TARGET, "stop", null, null);
         }
 
         SystemClock.sleep(1000);
diff --git a/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml
index 2accec1..435e636 100644
--- a/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-       package="com.android.cts.appwithdata">
+       package="com.android.cts.appwithdata"
+       android:targetSandboxVersion="2">
 
     <!--
     A simple app to test that other apps cannot access another app's private data.
@@ -30,7 +31,8 @@
         <provider
             android:name="com.android.cts.appwithdata.MyProvider"
             android:authorities="com.android.cts.appwithdata"
-            android:exported="true" />
+            android:exported="true"
+            android:visibleToInstantApps="true" />
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/AndroidManifest.xml
index 05f4573..cdaa469 100644
--- a/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/AndroidManifest.xml
@@ -16,7 +16,8 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.cts.classloadersplitapp"
-    android:isolatedSplits="true">
+    android:isolatedSplits="true"
+    android:targetSandboxVersion="2">
 
     <application android:label="ClassloaderSplitApp"
                  android:classLoader="dalvik.system.PathClassLoader">
diff --git a/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/feature_a/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/feature_a/AndroidManifest.xml
index a334acf..96807d6 100644
--- a/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/feature_a/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/feature_a/AndroidManifest.xml
@@ -16,7 +16,8 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.classloadersplitapp"
-        featureSplit="feature_a">
+        featureSplit="feature_a"
+        android:targetSandboxVersion="2">
 
     <application android:classLoader="dalvik.system.DelegateLastClassLoader">
         <activity android:name=".feature_a.FeatureAActivity">
diff --git a/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/feature_b/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/feature_b/AndroidManifest.xml
index 8d9ac52..fa975ad 100644
--- a/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/feature_b/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/feature_b/AndroidManifest.xml
@@ -16,7 +16,8 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.classloadersplitapp"
-        featureSplit="feature_b">
+        featureSplit="feature_b"
+        android:targetSandboxVersion="2">
 
     <uses-split android:name="feature_a" />
 
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTestCase.java b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTestCase.java
index 7038685..d2eea34 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTestCase.java
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTestCase.java
@@ -152,6 +152,11 @@
         return true;
     }
 
+    protected boolean isTelevision() {
+        final PackageManager pm = getInstrumentation().getContext().getPackageManager();
+        return pm.hasSystemFeature("android.hardware.type.television");
+    }
+
     protected void assertActivityFailed() {
         final Result result = mActivity.getResult();
         assertEquals(REQUEST_CODE, result.requestCode);
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java
index 466dabf..dc44bafb 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java
@@ -32,6 +32,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.storage.StorageManager;
 import android.os.storage.StorageVolume;
@@ -361,8 +362,10 @@
 
         // Close app screen.
         mDevice.pressBack();
-        // Close main screen.
-        mDevice.pressBack();
+        if (!isTelevision()) {
+            // Close main screen.
+            mDevice.pressBack();
+        }
 
         // Finally, make sure it's reset by requesting it again.
         sendOpenExternalDirectoryIntent(volume, dir);
@@ -401,8 +404,10 @@
 
         // Close app screen.
         mDevice.pressBack();
-        // Close main screen.
-        mDevice.pressBack();
+        if (!isTelevision()) {
+            // Close main screen.
+            mDevice.pressBack();
+        }
 
         // Then tries again - should be denied.
         sendOpenExternalDirectoryIntent(volume, dir);
diff --git a/hostsidetests/appsecurity/test-apps/EncryptionApp/Android.mk b/hostsidetests/appsecurity/test-apps/EncryptionApp/Android.mk
index 9734112..e6fb2af 100644
--- a/hostsidetests/appsecurity/test-apps/EncryptionApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/EncryptionApp/Android.mk
@@ -19,8 +19,13 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test compatibility-device-util ctstestrunner ub-uiautomator
+LOCAL_SDK_VERSION := test_current
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    compatibility-device-util \
+    ctstestrunner \
+    ub-uiautomator \
+    truth-prebuilt \
 
 LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
diff --git a/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java b/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java
index 1d0f83e..4673bb3 100644
--- a/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java
+++ b/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java
@@ -19,6 +19,9 @@
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -28,8 +31,13 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.Environment;
+import android.os.StrictMode;
+import android.os.StrictMode.ViolationInfo;
 import android.os.SystemClock;
 import android.os.UserManager;
+import android.os.strictmode.CredentialProtectedWhileLockedViolation;
+import android.os.strictmode.ImplicitDirectBootViolation;
+import android.os.strictmode.Violation;
 import android.provider.Settings;
 import android.support.test.uiautomator.UiDevice;
 import android.test.InstrumentationTestCase;
@@ -39,7 +47,9 @@
 
 import java.io.File;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
 
 public class EncryptionAppTest extends InstrumentationTestCase {
     private static final String TAG = "EncryptionAppTest";
@@ -217,6 +227,22 @@
             assertEquals(expected, mCe.getExternalCacheDir());
             assertEquals(expected, mDe.getExternalCacheDir());
         }
+
+        assertViolation(
+                new StrictMode.VmPolicy.Builder().detectImplicitDirectBoot()
+                        .penaltyLog().build(),
+                ImplicitDirectBootViolation.class,
+                () -> {
+                    final Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
+                    mCe.getPackageManager().queryBroadcastReceivers(intent, 0);
+                });
+
+        final File ceFile = getTestFile(mCe);
+        assertViolation(
+                new StrictMode.VmPolicy.Builder().detectCredentialProtectedWhileLocked()
+                        .penaltyLog().build(),
+                CredentialProtectedWhileLockedViolation.class,
+                ceFile::exists);
     }
 
     public void assertUnlocked() throws Exception {
@@ -260,6 +286,20 @@
             assertCanonicalEquals(expected, mCe.getExternalCacheDir());
             assertCanonicalEquals(expected, mDe.getExternalCacheDir());
         }
+
+        assertNoViolation(
+                new StrictMode.VmPolicy.Builder().detectImplicitDirectBoot()
+                        .penaltyLog().build(),
+                () -> {
+                    final Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
+                    mCe.getPackageManager().queryBroadcastReceivers(intent, 0);
+                });
+
+        final File ceFile = getTestFile(mCe);
+        assertNoViolation(
+                new StrictMode.VmPolicy.Builder().detectCredentialProtectedWhileLocked()
+                        .penaltyLog().build(),
+                ceFile::exists);
     }
 
     private void assertQuery(int count, int flags) throws Exception {
@@ -351,4 +391,36 @@
         }
         throw new AssertionError("Failed to find " + probe);
     }
+
+    public interface ThrowingRunnable {
+        void run() throws Exception;
+    }
+
+    private static void assertViolation(StrictMode.VmPolicy policy,
+            Class<? extends Violation> expected, ThrowingRunnable r) throws Exception {
+        inspectViolation(policy, r,
+                info -> assertThat(info.getViolationClass()).isAssignableTo(expected));
+    }
+
+    private static void assertNoViolation(StrictMode.VmPolicy policy, ThrowingRunnable r)
+            throws Exception {
+        inspectViolation(policy, r,
+                info -> assertWithMessage("Unexpected violation").that(info).isNull());
+    }
+
+    private static void inspectViolation(StrictMode.VmPolicy policy, ThrowingRunnable violating,
+            Consumer<ViolationInfo> consume) throws Exception {
+        final LinkedBlockingQueue<ViolationInfo> violations = new LinkedBlockingQueue<>();
+        StrictMode.setViolationLogger(violations::add);
+
+        final StrictMode.VmPolicy original = StrictMode.getVmPolicy();
+        try {
+            StrictMode.setVmPolicy(policy);
+            violating.run();
+            consume.accept(violations.poll(5, TimeUnit.SECONDS));
+        } finally {
+            StrictMode.setVmPolicy(original);
+            StrictMode.setViolationLogger(null);
+        }
+    }
 }
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/Android.mk b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/Android.mk
index 90b7866..eb8a172 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/Android.mk
@@ -37,4 +37,6 @@
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_DEX_PREOPT := false
 
+LOCAL_MIN_SDK_VERSION := 25
+
 include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/AndroidManifest.xml
index 2dc1b02..93bf970 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/AndroidManifest.xml
@@ -15,10 +15,9 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.cts.ephemeralapp1"
-    android:targetSandboxVersion="2">
+  package="com.android.cts.ephemeralapp1" >
     <uses-sdk
-        android:minSdkVersion="25" />
+        android:minSdkVersion="24" android:targetSdkVersion="26" />
 
     <uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp2/Android.mk b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp2/Android.mk
index 78c7970..d13d8a1 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp2/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp2/Android.mk
@@ -33,6 +33,7 @@
 
 LOCAL_PACKAGE_NAME := CtsEphemeralTestsEphemeralApp2
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 24
 
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_DEX_PREOPT := false
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp2/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp2/AndroidManifest.xml
index 0350cc6..c0fe68f 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp2/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp2/AndroidManifest.xml
@@ -15,10 +15,9 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.cts.ephemeralapp2"
-    android:targetSandboxVersion="2">
+  package="com.android.cts.ephemeralapp2" >
     <uses-sdk
-        android:minSdkVersion="24" />
+        android:minSdkVersion="24" android:targetSdkVersion="26" />
 
     <!-- TEST: exists only for testing ephemeral app1 can't see this app -->
     <application
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/ImplicitlyExposedApp/Android.mk b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/ImplicitlyExposedApp/Android.mk
index 8b04f9b..b9089f0 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/ImplicitlyExposedApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/ImplicitlyExposedApp/Android.mk
@@ -30,6 +30,7 @@
 
 LOCAL_PACKAGE_NAME := CtsEphemeralTestsImplicitApp
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 24
 
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_DEX_PREOPT := false
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/Android.mk b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/Android.mk
index d2c3667..0246b88 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/Android.mk
@@ -32,6 +32,7 @@
 
 LOCAL_PACKAGE_NAME := CtsEphemeralTestsNormalApp
 LOCAL_SDK_VERSION := system_current
+LOCAL_MIN_SDK_VERSION := 24
 
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_DEX_PREOPT := false
diff --git a/hostsidetests/appsecurity/test-apps/InstantUpgradeApp/Android.mk b/hostsidetests/appsecurity/test-apps/InstantUpgradeApp/Android.mk
index e0bc1cf..f4c51c9 100644
--- a/hostsidetests/appsecurity/test-apps/InstantUpgradeApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/InstantUpgradeApp/Android.mk
@@ -36,4 +36,6 @@
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_DEX_PREOPT := false
 
+LOCAL_MIN_SDK_VERSION := 25
+
 include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/AndroidManifest.xml
index a958c4c..6cb2f57 100644
--- a/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/AndroidManifest.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-       package="com.android.cts.instrumentationdiffcertapp">
+       package="com.android.cts.instrumentationdiffcertapp"
+       android:targetSandboxVersion="2">
 
     <!--
     A simple app to test that an instrumentation cannot target an app signed with a different
diff --git a/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/src/com/android/cts/instrumentationdiffcertapp/InstrumentationFailToRunTest.java b/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/src/com/android/cts/instrumentationdiffcertapp/InstrumentationFailToRunTest.java
index 9c320d9..4cdaf55 100644
--- a/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/src/com/android/cts/instrumentationdiffcertapp/InstrumentationFailToRunTest.java
+++ b/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/src/com/android/cts/instrumentationdiffcertapp/InstrumentationFailToRunTest.java
@@ -16,6 +16,8 @@
 
 package com.android.cts.instrumentationdiffcertapp;
 
+import static org.junit.Assert.assertFalse;
+
 import android.app.Instrumentation;
 import android.content.ComponentName;
 import android.content.Context;
@@ -27,18 +29,27 @@
  */
 public class InstrumentationFailToRunTest extends InstrumentationTestCase {
 
-    public void testInstrumentationNotAllowed() {
-        Context myContext = getInstrumentation().getContext();
+    public void testInstrumentationNotAllowed_exception() {
+        final Context myContext = getInstrumentation().getContext();
         // assumes android.app.Instrumentation has been defined in this app's AndroidManifest.xml
         // as targeting an app with a different cert
-        ComponentName appDiffCertInstrumentation = new ComponentName(myContext,
-                Instrumentation.class);
+        final ComponentName appDiffCertInstrumentation =
+                new ComponentName(myContext, Instrumentation.class);
         try {
-            getInstrumentation().getContext().startInstrumentation(appDiffCertInstrumentation,
-                null, new Bundle());
-            fail("could launch instrumentation");
-        } catch (SecurityException e) {
-            // expected
+            assertFalse("instrumentation started",
+                    myContext.startInstrumentation(appDiffCertInstrumentation, null, new Bundle()));
+            fail("SecurityException not thrown");
+        } catch (SecurityException expected) {
         }
     }
+
+    public void testInstrumentationNotAllowed_fail() {
+        final Context myContext = getInstrumentation().getContext();
+        // assumes android.app.Instrumentation has been defined in this app's AndroidManifest.xml
+        // as targeting an app with a different cert
+        final ComponentName appDiffCertInstrumentation =
+                new ComponentName(myContext, Instrumentation.class);
+        assertFalse("instrumentation started",
+                myContext.startInstrumentation(appDiffCertInstrumentation, null, new Bundle()));
+    }
 }
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/AndroidManifest.xml
index 2f3a374..a903c37 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/AndroidManifest.xml
@@ -16,7 +16,8 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.cts.isolatedsplitapp"
-    android:isolatedSplits="true">
+    android:isolatedSplits="true"
+    android:targetSandboxVersion="2">
 
     <application android:label="IsolatedSplitApp">
 
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/AndroidManifest.xml
index 958b8d0..d9ca271 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/AndroidManifest.xml
@@ -16,7 +16,8 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.isolatedsplitapp.feature_a"
-        featureSplit="feature_a">
+        featureSplit="feature_a"
+        android:targetSandboxVersion="2">
 
     <application>
         <activity android:name=".FeatureAActivity">
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/AndroidManifest.xml
index d89a1f2..8b4f16d 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/AndroidManifest.xml
@@ -16,7 +16,8 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.isolatedsplitapp.feature_b"
-        featureSplit="feature_b">
+        featureSplit="feature_b"
+        android:targetSandboxVersion="2">
 
     <uses-split android:name="feature_a" />
 
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/AndroidManifest.xml
index 64b087c..012543b 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/AndroidManifest.xml
@@ -16,7 +16,8 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.isolatedsplitapp.feature_c"
-        featureSplit="feature_c">
+        featureSplit="feature_c"
+        android:targetSandboxVersion="2">
 
     <application>
         <activity android:name=".FeatureCActivity">
diff --git a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000000000ffff/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000000000ffff/AndroidManifest.xml
index 2a63cd7..190c5d0 100644
--- a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000000000ffff/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000000000ffff/AndroidManifest.xml
@@ -15,8 +15,9 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.majorversion"
-        android:versionCodeMajor="0x00000000" android:versionCode="0x0000ffff">
-
+        android:versionCodeMajor="0x00000000" android:versionCode="0x0000ffff"
+        android:targetSandboxVersion="2">
+  
     <application/>
 
     <instrumentation
diff --git a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version00000000ffffffff/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version00000000ffffffff/AndroidManifest.xml
index 934deec..a2a90fc 100644
--- a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version00000000ffffffff/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version00000000ffffffff/AndroidManifest.xml
@@ -15,8 +15,9 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.majorversion"
-        android:versionCodeMajor="0x00000000" android:versionCode="0xffffffff">
-
+        android:versionCodeMajor="0x00000000" android:versionCode="0xffffffff"
+        android:targetSandboxVersion="2">
+  
     <application/>
 
     <instrumentation
diff --git a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ff00000000/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ff00000000/AndroidManifest.xml
index c7b9dd0..33a1dc2 100644
--- a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ff00000000/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ff00000000/AndroidManifest.xml
@@ -15,8 +15,9 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.majorversion"
-        android:versionCodeMajor="0x000000ff" android:versionCode="0x00000000">
-
+        android:versionCodeMajor="0x000000ff" android:versionCode="0x00000000"
+        android:targetSandboxVersion="2">
+  
     <application/>
 
     <instrumentation
diff --git a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ffffffffff/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ffffffffff/AndroidManifest.xml
index 91d4e39..bb0a3e6 100644
--- a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ffffffffff/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ffffffffff/AndroidManifest.xml
@@ -15,7 +15,8 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.majorversion"
-        android:versionCodeMajor="0x000000ff" android:versionCode="0xffffffff">
+        android:versionCodeMajor="0x000000ff" android:versionCode="0xffffffff"
+        android:targetSandboxVersion="2">
 
     <application/>
 
diff --git a/hostsidetests/appsecurity/test-apps/NoRestartApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/NoRestartApp/AndroidManifest.xml
index 7140333..c7550e0 100644
--- a/hostsidetests/appsecurity/test-apps/NoRestartApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/NoRestartApp/AndroidManifest.xml
@@ -16,12 +16,9 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     package="com.android.cts.norestart"
+    android:targetSandboxVersion="2"
     tools:ignore="MissingVersion" >
 
-    <uses-sdk
-        android:minSdkVersion="8"
-        android:targetSdkVersion="23" />
-
     <application
         tools:ignore="AllowBackup,MissingApplicationIcon" >
         <activity
@@ -32,6 +29,14 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
             <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:scheme="https" />
+                <data android:host="cts.android.com" />
+                <data android:path="/norestart" />
+            </intent-filter>
+            <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
diff --git a/hostsidetests/appsecurity/test-apps/NoRestartApp/feature/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/NoRestartApp/feature/AndroidManifest.xml
index b2fa3e8..9c71363 100644
--- a/hostsidetests/appsecurity/test-apps/NoRestartApp/feature/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/NoRestartApp/feature/AndroidManifest.xml
@@ -17,12 +17,9 @@
     xmlns:tools="http://schemas.android.com/tools"
     package="com.android.cts.norestart"
     split="feature"
+    android:targetSandboxVersion="2"
     tools:ignore="MissingVersion" >
 
-    <uses-sdk
-        android:minSdkVersion="8"
-        android:targetSdkVersion="23" />
-
     <application
         android:allowBackup="false"
         tools:ignore="MissingApplicationIcon" >
diff --git a/hostsidetests/appsecurity/test-apps/OrderedActivityApp/Android.mk b/hostsidetests/appsecurity/test-apps/OrderedActivityApp/Android.mk
index 67fe9f4..97e63d0 100644
--- a/hostsidetests/appsecurity/test-apps/OrderedActivityApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/OrderedActivityApp/Android.mk
@@ -22,7 +22,7 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := 23
+LOCAL_SDK_VERSION := current
 LOCAL_PACKAGE_NAME := CtsOrderedActivityApp
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_DEX_PREOPT := false
diff --git a/hostsidetests/appsecurity/test-apps/OrderedActivityApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/OrderedActivityApp/AndroidManifest.xml
index 383f000..6d08f9e 100644
--- a/hostsidetests/appsecurity/test-apps/OrderedActivityApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/OrderedActivityApp/AndroidManifest.xml
@@ -16,7 +16,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="android.appsecurity.cts.orderedactivity"
         android:versionCode="10"
-        android:versionName="1.0">
+        android:versionName="1.0"
+        android:targetSandboxVersion="2">
     <application android:label="@string/app_name">
         <!-- Activities used for queries -->
         <activity android:name=".OrderActivity2">
diff --git a/hostsidetests/appsecurity/test-apps/PermissionPolicy25/Android.mk b/hostsidetests/appsecurity/test-apps/PermissionPolicy25/Android.mk
index a8d659b..c696530 100644
--- a/hostsidetests/appsecurity/test-apps/PermissionPolicy25/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/PermissionPolicy25/Android.mk
@@ -29,6 +29,7 @@
 
 LOCAL_PACKAGE_NAME := CtsPermissionPolicyTest25
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 25
 
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
diff --git a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/Android.bp b/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/Android.bp
new file mode 100644
index 0000000..3ce27fad3
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/Android.bp
@@ -0,0 +1,31 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+    name: "ReviewPermissionHelper",
+
+    srcs: ["src/**/*.kt"],
+
+    static_libs: [
+        "ub-uiautomator",
+        "android-support-test",
+        "compatibility-device-util",
+    ],
+
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+}
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/AndroidManifest.xml
new file mode 100644
index 0000000..24ccc55
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.reviewpermissionhelper">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+            android:name="android.support.test.runner.AndroidJUnitRunner"
+            android:targetPackage="com.android.cts.reviewpermissionhelper" />
+
+</manifest>
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
new file mode 100644
index 0000000..63b9d82
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/src/com/android/cts/reviewpermissionhelper/ReviewPermissionsTest.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.content.ComponentName
+import android.content.Intent
+import android.support.test.InstrumentationRegistry
+import android.support.test.runner.AndroidJUnit4
+import android.support.test.uiautomator.By
+import android.support.test.uiautomator.UiDevice
+import android.support.test.uiautomator.Until
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val UI_TIMEOUT = 5000L
+
+@RunWith(AndroidJUnit4::class)
+class ReviewPermissionsTest {
+    @Test
+    fun approveReviewPermissions() {
+        val instrumentation = InstrumentationRegistry.getInstrumentation()
+        val startAutoClosingActivity = Intent()
+        startAutoClosingActivity.component = ComponentName("com.android.cts.usepermission",
+                "com.android.cts.usepermission.AutoClosingActivity")
+        startAutoClosingActivity.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+        instrumentation.targetContext.startActivity(startAutoClosingActivity)
+
+        UiDevice.getInstance(instrumentation).wait(Until.findObject(
+                By.res("com.android.permissioncontroller:id/continue_button")), UI_TIMEOUT).click()
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/SimpleAppInstall/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SimpleAppInstall/AndroidManifest.xml
index 247617e..942cae0 100644
--- a/hostsidetests/appsecurity/test-apps/SimpleAppInstall/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/SimpleAppInstall/AndroidManifest.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-       package="com.android.cts.simpleappinstall">
+       package="com.android.cts.simpleappinstall"
+       android:targetSandboxVersion="2">
 
     <!--
     A simple app to test that apps cannot be installed over existing app with
diff --git a/hostsidetests/appsecurity/test-apps/SimpleAppInstallDiffCert/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SimpleAppInstallDiffCert/AndroidManifest.xml
index da48194..1491d9c 100644
--- a/hostsidetests/appsecurity/test-apps/SimpleAppInstallDiffCert/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/SimpleAppInstallDiffCert/AndroidManifest.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-       package="com.android.cts.simpleappinstall">
+       package="com.android.cts.simpleappinstall"
+       android:targetSandboxVersion="2">
 
     <!--
     A simple second app to test that apps cannot be installed over existing app
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SplitApp/AndroidManifest.xml
index abb7f32..a1ce506 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/AndroidManifest.xml
@@ -15,7 +15,8 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.cts.splitapp">
+    package="com.android.cts.splitapp"
+    android:targetSandboxVersion="2">
     <!-- TODO(b/73365611) Remove targetSdkVersion once EncryptionApp tests
          are fixed to no longer access SplitApp's data by path. -->
     <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="27" />
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SplitApp/feature/AndroidManifest.xml
index 8ba3c2f..04a6c80 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/feature/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature/AndroidManifest.xml
@@ -16,7 +16,7 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.splitapp"
-        featureName="feature">
+        split="feature">
 
     <!-- New permission should be ignored -->
     <uses-permission android:name="android.permission.INTERNET" />
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/revision/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SplitApp/revision/AndroidManifest.xml
index 8e053ba..f2617fa 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/revision/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/revision/AndroidManifest.xml
@@ -16,6 +16,7 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.splitapp"
+        android:targetSandboxVersion="2"
         android:revisionCode="12">
 
     <uses-permission android:name="android.permission.CAMERA" />
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
index 4cfd7d0..8d48c16 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
@@ -35,10 +35,12 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
+import android.net.Uri;
 import android.os.ConditionVariable;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.os.StatFs;
+import android.support.test.InstrumentationRegistry;
 import android.system.Os;
 import android.system.OsConstants;
 import android.system.StructStat;
@@ -236,8 +238,10 @@
         assertEquals("base", getXmlTestValue(r.getXml(R.xml.my_activity_meta)));
 
         // And that we can access resources from feature
-        assertEquals("red", r.getString(r.getIdentifier("feature_string", "string", PKG)));
-        assertEquals(123, r.getInteger(r.getIdentifier("feature_integer", "integer", PKG)));
+        assertEquals("red", r.getString(r.getIdentifier(
+                "com.android.cts.splitapp.feature:feature_string", "string", PKG)));
+        assertEquals(123, r.getInteger(r.getIdentifier(
+                "com.android.cts.splitapp.feature:feature_integer", "integer", PKG)));
 
         final Class<?> featR = Class.forName("com.android.cts.splitapp.FeatureR");
         final int boolId = (int) featR.getDeclaredField("feature_receiver_enabled").get(null);
@@ -308,6 +312,23 @@
         }
     }
 
+    private Intent createLaunchIntent() {
+        final boolean isInstant = Boolean.parseBoolean(
+                InstrumentationRegistry.getArguments().getString("is_instant", "false"));
+        if (isInstant) {
+            final Intent i = new Intent(Intent.ACTION_VIEW);
+            i.addCategory(Intent.CATEGORY_BROWSABLE);
+            i.setData(Uri.parse("https://cts.android.com/norestart"));
+            i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            return i;
+        } else {
+            final Intent i = new Intent("com.android.cts.norestart.START");
+            i.addCategory(Intent.CATEGORY_DEFAULT);
+            i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            return i;
+        }
+    }
+
     public void testBaseInstalled() throws Exception {
         final ConditionVariable cv = new ConditionVariable();
         final BroadcastReceiver r = new BroadcastReceiver() {
@@ -319,10 +340,8 @@
             }
         };
         final IntentFilter filter = new IntentFilter("com.android.cts.norestart.BROADCAST");
-        getContext().registerReceiver(r, filter);
-        final Intent i = new Intent("com.android.cts.norestart.START");
-        i.addCategory(Intent.CATEGORY_DEFAULT);
-        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        getContext().registerReceiver(r, filter, Context.RECEIVER_VISIBLE_TO_INSTANT_APPS);
+        final Intent i = createLaunchIntent();
         getContext().startActivity(i);
         assertTrue(cv.block(2000L));
         getContext().unregisterReceiver(r);
@@ -345,10 +364,8 @@
             }
         };
         final IntentFilter filter = new IntentFilter("com.android.cts.norestart.BROADCAST");
-        getContext().registerReceiver(r, filter);
-        final Intent i = new Intent("com.android.cts.norestart.START");
-        i.addCategory(Intent.CATEGORY_DEFAULT);
-        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        getContext().registerReceiver(r, filter, Context.RECEIVER_VISIBLE_TO_INSTANT_APPS);
+        final Intent i = createLaunchIntent();
         getContext().startActivity(i);
         assertTrue(cv.block(2000L));
         getContext().unregisterReceiver(r);
@@ -362,7 +379,8 @@
         assertEquals(false, r.getBoolean(R.bool.my_receiver_enabled));
 
         // And that we can access resources from feature
-        assertEquals(321, r.getInteger(r.getIdentifier("feature_integer", "integer", PKG)));
+        assertEquals(321, r.getInteger(r.getIdentifier(
+                "com.android.cts.splitapp.feature:feature_integer", "integer", PKG)));
 
         final Class<?> featR = Class.forName("com.android.cts.splitapp.FeatureR");
         final int boolId = (int) featR.getDeclaredField("feature_receiver_enabled").get(null);
diff --git a/hostsidetests/appsecurity/test-apps/TargetInstrumentationApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/TargetInstrumentationApp/AndroidManifest.xml
index 8b15f1f..0410e1a 100644
--- a/hostsidetests/appsecurity/test-apps/TargetInstrumentationApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/TargetInstrumentationApp/AndroidManifest.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-       package="com.android.cts.targetinstrumentationapp">
+       package="com.android.cts.targetinstrumentationapp"
+       android:targetSandboxVersion="2">
 
     <!--
     A simple app used to test that instrumentation cannot target an app signed with a different
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/Android.mk
index 7a62f09..36d7a13 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/Android.mk
@@ -33,6 +33,8 @@
     ../UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
 
 LOCAL_PACKAGE_NAME := CtsUsePermissionApp22
+
+# For ACCESS_BACKGROUND_LOCATION
 LOCAL_PRIVATE_PLATFORM_APIS := true
 
 # tag this module as a cts test artifact
@@ -43,4 +45,6 @@
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_DEX_PREOPT := false
 
+LOCAL_MIN_SDK_VERSION := 22
+
 include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/AndroidManifest.xml
index ebb0cbf..3bbd85c 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/AndroidManifest.xml
@@ -68,6 +68,7 @@
     <application>
         <uses-library android:name="android.test.runner" />
         <activity android:name=".BasePermissionActivity" />
+        <activity android:name=".AutoClosingActivity" android:exported="true" />
     </application>
 
     <instrumentation
diff --git a/tests/tests/content/src/android/content/cts/ContextWrapperCtsActivity.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
similarity index 64%
copy from tests/tests/content/src/android/content/cts/ContextWrapperCtsActivity.java
copy to hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
index 7ea2ab7..5ee3aeb 100644
--- a/tests/tests/content/src/android/content/cts/ContextWrapperCtsActivity.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/AutoClosingActivity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 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,21 +14,16 @@
  * limitations under the License.
  */
 
-package android.content.cts;
+package com.android.cts.usepermission;
 
-// Need the following import to get access to the app resources, since this
-// class is in a sub-package.
 import android.app.Activity;
 import android.os.Bundle;
 
-import android.content.cts.R;
-
-public class ContextWrapperCtsActivity extends Activity {
+public class AutoClosingActivity extends Activity {
     @Override
-    public void onCreate(Bundle savedInstanceState) {
+    protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        setContentView(R.layout.local_sample);
+        finish();
     }
 }
-
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 35ac7ec..4a07a92 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
@@ -145,6 +145,10 @@
                 .checkSelfPermission(Manifest.permission.CAMERA));
         assertEquals(PackageManager.PERMISSION_GRANTED, getInstrumentation().getContext()
                 .checkSelfPermission(Manifest.permission.BODY_SENSORS));
+
+        // Split permissions
+        assertEquals(PackageManager.PERMISSION_GRANTED, getInstrumentation().getContext()
+                .checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION));
     }
 
     @Test
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/Android.mk
index 58ee1c7..cfbcbea 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/Android.mk
@@ -31,6 +31,8 @@
     ../ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
 
 LOCAL_PACKAGE_NAME := CtsUsePermissionApp23
+
+# For ACCESS_BACKGROUND_LOCATION
 LOCAL_PRIVATE_PLATFORM_APIS := true
 
 # tag this module as a cts test artifact
@@ -41,4 +43,6 @@
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_DEX_PREOPT := false
 
+LOCAL_MIN_SDK_VERSION := 23
+
 include $(BUILD_CTS_SUPPORT_PACKAGE)
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 d219c6c..3e237a0 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
@@ -253,7 +253,7 @@
 
         PackageManager packageManager = mContext.getPackageManager();
         mWatch = packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH);
-        initPermissionToLabelMap(packageManager.isPermissionReviewModeEnabled());
+        initPermissionToLabelMap(packageManager.arePermissionsIndividuallyControlled());
 
         UiObject2 button = getUiDevice().findObject(By.text("Close"));
         if (button != null) {
@@ -287,27 +287,43 @@
         return result;
     }
 
+    protected void selectForegroundOnlyOption() {
+        getUiDevice().findObject(By.res(
+                "com.android.permissioncontroller:id/foreground_only_radio_button")).click();
+    }
+
+    protected void selectAlwaysOption() {
+        getUiDevice().findObject(By.res(
+                "com.android.permissioncontroller:id/always_radio_button")).click();
+    }
+
+    protected void selectDenyAndDontAskAgainOption() {
+        getUiDevice().findObject(By.res(
+                "com.android.permissioncontroller:id/deny_dont_ask_again_radio_button")).click();
+    }
+
     protected void clickAllowButton() throws Exception {
         scrollToBottomIfWatch();
         getUiDevice().findObject(new UiSelector().resourceId(
-                "com.android.packageinstaller:id/permission_allow_button")).click();
+                "com.android.permissioncontroller:id/permission_allow_button")).click();
     }
 
     protected void clickDenyButton() throws Exception {
         scrollToBottomIfWatch();
         getUiDevice().findObject(new UiSelector().resourceId(
-                "com.android.packageinstaller:id/permission_deny_button")).click();
+                "com.android.permissioncontroller:id/permission_deny_button")).click();
     }
 
     protected void clickDontAskAgainCheckbox() throws Exception {
         getUiDevice().findObject(new UiSelector().resourceId(
-                "com.android.packageinstaller:id/do_not_ask_checkbox")).click();
+                "com.android.permissioncontroller:id/do_not_ask_checkbox")).click();
     }
 
     protected void clickDontAskAgainButton() throws Exception {
         scrollToBottomIfWatch();
         getUiDevice().findObject(new UiSelector().resourceId(
-                "com.android.packageinstaller:id/permission_deny_dont_ask_again_button")).click();
+                "com.android.permissioncontroller:id/permission_deny_dont_ask_again_button"))
+                .click();
     }
 
     protected void grantPermission(String permission) throws Exception {
@@ -401,7 +417,7 @@
                     scrollToBottomIfWatch();
                     String packageName = getInstrumentation().getContext().getPackageManager()
                             .getPermissionControllerPackageName();
-                    String resIdName = "com.android.packageinstaller"
+                    String resIdName = "com.android.permissioncontroller"
                             + ":string/grant_dialog_button_deny_anyway";
                     Resources resources = getInstrumentation().getContext()
                             .createPackageContext(packageName, 0).getResources();
@@ -478,7 +494,9 @@
                     return result;
                 }
                 try {
-                    while (child.getActionList().contains(AccessibilityAction.ACTION_SCROLL_FORWARD)) {
+                    while (child.getActionList().contains(
+                            AccessibilityAction.ACTION_SCROLL_FORWARD) || child.getActionList()
+                            .contains(AccessibilityAction.ACTION_SCROLL_DOWN)) {
                         scrollForward(child);
                         result = getNodeTimed(() -> findByText(child, text), false);
                         if (result != null) {
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 79c03fb..ebee589 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
@@ -29,6 +29,7 @@
 import android.Manifest;
 import android.content.pm.PackageManager;
 import android.os.Environment;
+import android.support.test.InstrumentationRegistry;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -497,9 +498,17 @@
                 Manifest.permission.CALL_PHONE,
                 Manifest.permission.RECORD_AUDIO,
                 Manifest.permission.BODY_SENSORS,
-                Manifest.permission.ACCESS_COARSE_LOCATION,
                 Manifest.permission.CAMERA
         });
+
+        // Don't use UI for granting location permission as this shows another dialog
+        String packageName = InstrumentationRegistry.getTargetContext().getPackageName();
+        getInstrumentation().getUiAutomation().grantRuntimePermission(packageName,
+                Manifest.permission.ACCESS_FINE_LOCATION);
+        getInstrumentation().getUiAutomation().grantRuntimePermission(packageName,
+                Manifest.permission.ACCESS_COARSE_LOCATION);
+        getInstrumentation().getUiAutomation().grantRuntimePermission(packageName,
+                Manifest.permission.ACCESS_BACKGROUND_LOCATION);
     }
 
     @Test
@@ -650,7 +659,10 @@
                 Manifest.permission.ACCESS_COARSE_LOCATION,
                 Manifest.permission.CAMERA,
                 Manifest.permission.BODY_SENSORS,
-                "android.permission.READ_CELL_BROADCASTS"
+                Manifest.permission.READ_CELL_BROADCASTS,
+
+                // Split permissions
+                Manifest.permission.ACCESS_BACKGROUND_LOCATION
         }, grantState);
     }
 
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp25/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionApp25/Android.mk
index 8528752..acacc1e 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp25/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp25/Android.mk
@@ -32,6 +32,8 @@
 LOCAL_RESOURCE_DIR := cts/hostsidetests/appsecurity/test-apps/UsePermissionApp23/res
 
 LOCAL_PACKAGE_NAME := CtsUsePermissionApp25
+
+# For ACCESS_BACKGROUND_LOCATION
 LOCAL_PRIVATE_PLATFORM_APIS := true
 
 # tag this module as a cts test artifact
@@ -42,4 +44,6 @@
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_DEX_PREOPT := false
 
+LOCAL_MIN_SDK_VERSION := 25
+
 include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp26/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionApp26/Android.mk
index 52c8ba4..a7f7d2c 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp26/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp26/Android.mk
@@ -31,7 +31,8 @@
 LOCAL_RESOURCE_DIR := cts/hostsidetests/appsecurity/test-apps/UsePermissionApp23/res
 
 LOCAL_PACKAGE_NAME := CtsUsePermissionApp26
-LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_SDK_VERSION := test_current
+LOCAL_MIN_SDK_VERSION := 26
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp28/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionApp28/Android.mk
new file mode 100644
index 0000000..07b19ab
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp28/Android.mk
@@ -0,0 +1,48 @@
+#
+# 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 := tests
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    compatibility-device-util \
+    ctstestrunner \
+    ub-uiautomator
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    ../UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionActivity.java \
+    ../UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
+LOCAL_RESOURCE_DIR := cts/hostsidetests/appsecurity/test-apps/UsePermissionApp23/res
+
+LOCAL_PACKAGE_NAME := CtsUsePermissionApp28
+
+# For ACCESS_BACKGROUND_LOCATION
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey2
+
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_DEX_PREOPT := false
+
+LOCAL_MIN_SDK_VERSION := 28
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp28/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/UsePermissionApp28/AndroidManifest.xml
new file mode 100644
index 0000000..9346444
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp28/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.usepermission">
+    <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
+
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name="com.android.cts.usepermission.BasePermissionActivity" />
+    </application>
+
+    <instrumentation
+            android:name="android.support.test.runner.AndroidJUnitRunner"
+            android:targetPackage="com.android.cts.usepermission" />
+
+</manifest>
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
new file mode 100644
index 0000000..f568989
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp28/src/com/android/cts/usepermission/UsePermissionTest28.java
@@ -0,0 +1,67 @@
+/*
+ * 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.usepermission;
+
+import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import org.junit.Test;
+
+/**
+ * Runtime permission behavior tests for apps targeting API 28
+ */
+public class UsePermissionTest28 extends BasePermissionsTest {
+    private static final int REQUEST_CODE_PERMISSIONS = 42;
+
+    @Test
+    public void testLocationPermissionWasSplit() throws Exception {
+        Context context = getInstrumentation().getTargetContext();
+
+        assertEquals(PERMISSION_DENIED, context.checkSelfPermission(ACCESS_FINE_LOCATION));
+        assertEquals(PERMISSION_DENIED, context.checkSelfPermission(ACCESS_BACKGROUND_LOCATION));
+
+        String[] permissions = {ACCESS_FINE_LOCATION};
+
+        // request only foreground permission. This should automatically also add the background
+        // permission
+        BasePermissionActivity.Result result = requestPermissions(permissions,
+                REQUEST_CODE_PERMISSIONS,
+                BasePermissionActivity.class,
+                () -> {
+                    try {
+                        selectAlwaysOption();
+                        clickAllowButton();
+                    } catch (Exception e) {
+                        throw new RuntimeException(e);
+                    }
+                });
+
+        assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS, 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/UsePermissionAppLatest/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/Android.mk
index 62a15e4..7882936 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/Android.mk
@@ -26,13 +26,16 @@
     ub-uiautomator
 
 LOCAL_SRC_FILES := $(call all-java-files-under, ../UsePermissionApp26/src)  \
+    $(call all-java-files-under, ../UsePermissionAppP0/src)  \
     ../UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionActivity.java \
     ../UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
 LOCAL_RESOURCE_DIR := cts/hostsidetests/appsecurity/test-apps/UsePermissionApp23/res
-LOCAL_SDK_VERSION := test_current
 
 LOCAL_PACKAGE_NAME := CtsUsePermissionAppLatest
 
+# For ACCESS_BACKGROUND_LOCATION
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/AndroidManifest.xml
index 57a58ab..9d397d7 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/AndroidManifest.xml
@@ -22,6 +22,8 @@
     <uses-permission android:name="android.permission.SEND_SMS" />
     <uses-permission android:name="android.permission.RECEIVE_SMS" />
 
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+
     <application>
         <uses-library android:name="android.test.runner" />
 
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionAppP0/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionAppP0/Android.mk
new file mode 100644
index 0000000..3eae70c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionAppP0/Android.mk
@@ -0,0 +1,46 @@
+#
+# 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 := tests
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    compatibility-device-util \
+    ctstestrunner \
+    ub-uiautomator
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    ../UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionActivity.java \
+    ../UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
+LOCAL_RESOURCE_DIR := cts/hostsidetests/appsecurity/test-apps/UsePermissionApp23/res
+
+LOCAL_PACKAGE_NAME := CtsUsePermissionAppP0
+
+# For ACCESS_BACKGROUND_LOCATION
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey2
+
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionAppP0/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/UsePermissionAppP0/AndroidManifest.xml
new file mode 100644
index 0000000..9d89608
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionAppP0/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.usepermission">
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name="com.android.cts.usepermission.BasePermissionActivity" />
+    </application>
+
+    <instrumentation
+            android:name="android.support.test.runner.AndroidJUnitRunner"
+            android:targetPackage="com.android.cts.usepermission" />
+
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionAppP0/src/com/android/cts/usepermission/UsePermissionTestP0.java b/hostsidetests/appsecurity/test-apps/UsePermissionAppP0/src/com/android/cts/usepermission/UsePermissionTestP0.java
new file mode 100644
index 0000000..fdfcfa2
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionAppP0/src/com/android/cts/usepermission/UsePermissionTestP0.java
@@ -0,0 +1,182 @@
+/*
+ * 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.usepermission;
+
+import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import static junit.framework.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Runtime permission behavior tests for apps targeting {@link android.os.Build.VERSION_CODES#P0}.
+ */
+public class UsePermissionTestP0 extends BasePermissionsTest {
+    private static final int REQUEST_CODE_PERMISSIONS = 42;
+
+    public interface UiInteraction {
+        void run() throws Exception;
+    }
+
+    private static void assertGranted(String permission) {
+        assertEquals(PERMISSION_GRANTED, getInstrumentation().getContext()
+                .checkSelfPermission(permission));
+    }
+
+    private static void assertDenied(String permission) {
+        assertEquals(PERMISSION_DENIED, getInstrumentation().getContext()
+                .checkSelfPermission(permission));
+    }
+
+    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);
+                    }
+                });
+    }
+
+    private static void assertPermissionRequestResult(BasePermissionActivity.Result result,
+            String[] permissions, boolean... granted) {
+        BasePermissionsTest.assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
+                permissions, granted);
+    }
+
+    @Before
+    public void assertPermissionsNotGranted() {
+        assertDenied(ACCESS_FINE_LOCATION);
+        assertDenied(ACCESS_BACKGROUND_LOCATION);
+    }
+
+    @Test
+    public void locationPermissionIsNotSplit() throws Exception {
+        String[] permissions = {ACCESS_FINE_LOCATION};
+
+        BasePermissionActivity.Result result = requestPermissions(permissions,
+                this::clickAllowButton);
+        assertPermissionRequestResult(result, permissions, true);
+
+        assertGranted(ACCESS_FINE_LOCATION);
+        assertDenied(ACCESS_BACKGROUND_LOCATION);
+    }
+
+    @Test
+    public void requestOnlyBackgroundNotPossible() throws Exception {
+        String[] permissions = {ACCESS_BACKGROUND_LOCATION};
+
+        BasePermissionActivity.Result result = requestPermissions(permissions);
+        assertPermissionRequestResult(result, permissions, false);
+
+        assertDenied(ACCESS_FINE_LOCATION);
+        assertDenied(ACCESS_BACKGROUND_LOCATION);
+    }
+
+    @Test
+    public void requestBoth() throws Exception {
+        String[] permissions = {ACCESS_FINE_LOCATION, ACCESS_BACKGROUND_LOCATION};
+
+        BasePermissionActivity.Result result = requestPermissions(permissions,
+                this::selectAlwaysOption, this::clickAllowButton);
+        assertPermissionRequestResult(result, permissions, true, true);
+
+        assertGranted(ACCESS_FINE_LOCATION);
+        assertGranted(ACCESS_BACKGROUND_LOCATION);
+    }
+
+    @Test
+    public void requestBothInSequence() throws Exception {
+        // Step 1: request foreground only
+        String[] permissions = {ACCESS_FINE_LOCATION};
+
+        BasePermissionActivity.Result result = requestPermissions(permissions,
+                this::clickAllowButton);
+        assertPermissionRequestResult(result, permissions, true);
+
+        assertGranted(ACCESS_FINE_LOCATION);
+        assertDenied(ACCESS_BACKGROUND_LOCATION);
+
+        // Step 2: request background only
+        permissions = new String[]{ACCESS_BACKGROUND_LOCATION};
+
+        result = requestPermissions(permissions, this::clickAllowButton);
+        assertPermissionRequestResult(result, permissions, true);
+
+        assertGranted(ACCESS_FINE_LOCATION);
+        assertGranted(ACCESS_BACKGROUND_LOCATION);
+    }
+
+    @Test
+    public void requestBothButGrantInSequence() throws Exception {
+        // Step 1: grant foreground only
+        String[] permissions = {ACCESS_FINE_LOCATION, ACCESS_BACKGROUND_LOCATION};
+
+        BasePermissionActivity.Result result = requestPermissions(permissions,
+                this::selectForegroundOnlyOption, this::clickAllowButton);
+        assertPermissionRequestResult(result, permissions, true, false);
+
+        assertGranted(ACCESS_FINE_LOCATION);
+        assertDenied(ACCESS_BACKGROUND_LOCATION);
+
+        // Step 2: grant background
+        result = requestPermissions(permissions, this::clickAllowButton);
+        assertPermissionRequestResult(result, permissions, true, true);
+
+        assertGranted(ACCESS_FINE_LOCATION);
+        assertGranted(ACCESS_BACKGROUND_LOCATION);
+    }
+
+    @Test
+    public void denyBackgroundWithPrejudice() throws Exception {
+        // Step 1: deny the first time
+        String[] permissions = {ACCESS_FINE_LOCATION, ACCESS_BACKGROUND_LOCATION};
+
+        BasePermissionActivity.Result result = requestPermissions(permissions,
+                this::clickDenyButton);
+        assertPermissionRequestResult(result, permissions, false, false);
+
+        assertDenied(ACCESS_FINE_LOCATION);
+        assertDenied(ACCESS_BACKGROUND_LOCATION);
+
+        // Step 2: deny with prejudice
+        result = requestPermissions(permissions, this::selectDenyAndDontAskAgainOption,
+                this::clickDenyButton);
+        assertPermissionRequestResult(result, permissions, false, false);
+
+        assertDenied(ACCESS_FINE_LOCATION);
+        assertDenied(ACCESS_BACKGROUND_LOCATION);
+
+        // Step 3: All further requests should be denied automatically
+        result = requestPermissions(permissions);
+        assertPermissionRequestResult(result, permissions, false, false);
+
+        assertDenied(ACCESS_FINE_LOCATION);
+        assertDenied(ACCESS_BACKGROUND_LOCATION);
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/tinyapp/Android.mk b/hostsidetests/appsecurity/test-apps/tinyapp/Android.mk
index 4822a11..da5eba3 100644
--- a/hostsidetests/appsecurity/test-apps/tinyapp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/tinyapp/Android.mk
@@ -21,7 +21,7 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := 23
+LOCAL_SDK_VERSION := current
 LOCAL_PACKAGE_NAME := CtsPkgInstallTinyApp
 LOCAL_DEX_PREOPT := false
 
diff --git a/hostsidetests/appsecurity/test-apps/tinyapp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/tinyapp/AndroidManifest.xml
index def2931..1ead3a2 100644
--- a/hostsidetests/appsecurity/test-apps/tinyapp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/tinyapp/AndroidManifest.xml
@@ -16,7 +16,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="android.appsecurity.cts.tinyapp"
         android:versionCode="10"
-        android:versionName="1.0">
+        android:versionName="1.0"
+        android:targetSandboxVersion="2">
     <application android:label="@string/app_name">
         <activity
                 android:name=".MainActivity"
diff --git a/hostsidetests/atrace/AtraceTestApp/src/com/android/cts/atracetestapp/AtraceTestAppActivity.java b/hostsidetests/atrace/AtraceTestApp/src/com/android/cts/atracetestapp/AtraceTestAppActivity.java
index 0269d0d..3e8fa3a 100644
--- a/hostsidetests/atrace/AtraceTestApp/src/com/android/cts/atracetestapp/AtraceTestAppActivity.java
+++ b/hostsidetests/atrace/AtraceTestApp/src/com/android/cts/atracetestapp/AtraceTestAppActivity.java
@@ -23,7 +23,18 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         Trace.beginSection("traceable-app-test-section");
+        Trace.setCounter("mycounter", Trace.isEnabled() ? 1 : 0);
+        Trace.beginAsyncSection("traceable-async-section", 100);
         super.onCreate(savedInstanceState);
         Trace.endSection();
+
+        Thread t = new Thread(() ->
+                Trace.endAsyncSection("traceable-async-section", 100));
+        t.start();
+        try {
+            t.join();
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
     }
 }
diff --git a/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java b/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
index 3c6087c..4c9eb70 100644
--- a/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
+++ b/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
@@ -52,26 +52,12 @@
      * Regexs copied from (and should be kept in sync with) ftrace importer in catapult.
      */
     private static class FtraceParser {
-        // Matches the trace record in 3.2 and later with the print-tgid option:
-        //          <idle>-0    0 [001] d...  1.23: sched_switch
-        private static final Pattern sLineWithTgid = Pattern.compile(
-                "^\\s*(.+)-(\\d+)\\s+\\(\\s*(\\d+|-+)\\)\\s\\[(\\d+)\\]"
-                + "\\s+[dX.][N.][Hhs.][0-9a-f.]"
-                + "\\s+(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)");
+        private static final Pattern sFtraceLine = Pattern.compile(
+                "^ *(.{1,16})-(\\d+) +(?:\\( *(\\d+)?-*\\) )?\\[(\\d+)] (?:[dX.]...)? *([\\d.]*): *"
+                + "([^:]*): *(.*) *$");
 
-        // Matches the default trace record in 3.2 and later (includes irq-info):
-        //          <idle>-0     [001] d...  1.23: sched_switch
-        private static final Pattern sLineWithIrqInfo = Pattern.compile(
-                "^\\s*(.+)-(\\d+)\\s+\\[(\\d+)\\]"
-                + "\\s+[dX.][N.][Hhs.][0-9a-f.]"
-                + "\\s+(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)$");
-
-        // Matches the default trace record pre-3.2:
-        //          <idle>-0     [001]  1.23: sched_switch
-        private static final Pattern sLineLegacy = Pattern.compile(
-                "^\\s*(.+)-(\\d+)\\s+\\[(\\d+)\\]\\s*(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)");
         private static void parseLine(String line, FtraceEntryCallback callback) {
-            Matcher m = sLineWithTgid.matcher(line);
+            Matcher m = sFtraceLine.matcher(line);
             if (m.matches()) {
                 callback.onTraceEntry(
                         /*threadname*/ m.group(1),
@@ -82,27 +68,6 @@
                 return;
             }
 
-            m = sLineWithIrqInfo.matcher(line);
-            if (m.matches()) {
-                callback.onTraceEntry(
-                        /*threadname*/ m.group(1),
-                        /*pid*/ -1,
-                        /*tid*/ Integer.parseInt(m.group(2)),
-                        /*eventName*/ m.group(5),
-                        /*details*/ m.group(6));
-                return;
-            }
-
-            m = sLineLegacy.matcher(line);
-            if (m.matches()) {
-                callback.onTraceEntry(
-                        /*threadname*/ m.group(1),
-                        /*pid*/ -1,
-                        /*tid*/ Integer.parseInt(m.group(2)),
-                        /*eventName*/ m.group(5),
-                        /*details*/ m.group(6));
-                return;
-            }
             CLog.i("line doesn't match: " + line);
         }
 
@@ -234,6 +199,10 @@
             private int nextSectionIndex = -1;
             private int appTid = -1;
 
+            private boolean foundCounter = false;
+            private boolean foundAsyncStart = false;
+            private int asyncEndTid = -1;
+
 
             private final String initialSection = "traceable-app-test-section";
             // list of tags expected to be seen on app launch, in order, after the initial.
@@ -250,7 +219,7 @@
             @Override
             public void onTraceEntry(String truncatedThreadName, int pid, int tid,
                     String eventName, String details) {
-                if (!"tracing_mark_write".equals(eventName)) {
+                if (!"tracing_mark_write".equals(eventName) || details == null) {
                     // not userspace trace, ignore
                     return;
                 }
@@ -259,7 +228,24 @@
                 assertTrue(tid > 0);
                 userSpaceMatches++;
 
-                if (details == null || !details.startsWith("B|")) {
+                if (details.startsWith("S|") && details.endsWith("traceable-async-section|100")) {
+                    assertFalse("Found more async starts than expected", foundAsyncStart);
+                    foundAsyncStart = true;
+                    return;
+                }
+                if (details.startsWith("F|") && details.endsWith("traceable-async-section|100")) {
+                    assertEquals(-1, asyncEndTid);
+                    asyncEndTid = tid;
+                    return;
+                }
+
+                if (details.startsWith("C|") && details.endsWith("mycounter|1")) {
+                    assertFalse("Found too many counter entires", foundCounter);
+                    foundCounter = true;
+                    return;
+                }
+
+                if (!details.startsWith("B|")) {
                     // not a begin event
                     return;
                 }
@@ -292,6 +278,10 @@
                         nextSectionIndex >= 0);
                 assertEquals("Didn't see required list of traced sections, in order",
                         requiredSectionList.length, nextSectionIndex);
+                assertTrue("Didn't find async start", foundAsyncStart);
+                assertTrue("Didn't find counter", foundCounter);
+                assertTrue("Didn't find async end", asyncEndTid != -1);
+                assertTrue("Async end wasn't on a different thread", asyncEndTid != appTid);
             }
         };
 
diff --git a/hostsidetests/backup/AllowBackup/BackupAllowedApp/Android.mk b/hostsidetests/backup/AllowBackup/BackupAllowedApp/Android.mk
index aa9a22c..22cdb87 100644
--- a/hostsidetests/backup/AllowBackup/BackupAllowedApp/Android.mk
+++ b/hostsidetests/backup/AllowBackup/BackupAllowedApp/Android.mk
@@ -25,7 +25,7 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test platform-test-annotations
 
 LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
 
diff --git a/hostsidetests/backup/AllowBackup/BackupNotAllowedApp/Android.mk b/hostsidetests/backup/AllowBackup/BackupNotAllowedApp/Android.mk
index c218a28..4ffff06 100644
--- a/hostsidetests/backup/AllowBackup/BackupNotAllowedApp/Android.mk
+++ b/hostsidetests/backup/AllowBackup/BackupNotAllowedApp/Android.mk
@@ -25,7 +25,7 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test platform-test-annotations
 
 LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
 
diff --git a/hostsidetests/backup/AllowBackup/src/AllowBackupTest.java b/hostsidetests/backup/AllowBackup/src/AllowBackupTest.java
index 643b594..efe52ae 100644
--- a/hostsidetests/backup/AllowBackup/src/AllowBackupTest.java
+++ b/hostsidetests/backup/AllowBackup/src/AllowBackupTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import android.platform.test.annotations.AppModeFull;
 import android.content.Context;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
@@ -41,6 +42,7 @@
  *
  */
 @RunWith(AndroidJUnit4.class)
+@AppModeFull
 public class AllowBackupTest {
     public static final String TAG = "AllowBackupCTSApp";
     private static final int FILE_SIZE_BYTES = 1024 * 1024;
diff --git a/hostsidetests/backup/FullBackupOnly/FullBackupOnlyFalseNoAgentApp/Android.mk b/hostsidetests/backup/FullBackupOnly/FullBackupOnlyFalseNoAgentApp/Android.mk
index 6155ffb..a4c449f 100644
--- a/hostsidetests/backup/FullBackupOnly/FullBackupOnlyFalseNoAgentApp/Android.mk
+++ b/hostsidetests/backup/FullBackupOnly/FullBackupOnlyFalseNoAgentApp/Android.mk
@@ -25,7 +25,7 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test platform-test-annotations
 
 LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
 
diff --git a/hostsidetests/backup/FullBackupOnly/FullBackupOnlyFalseWithAgentApp/Android.mk b/hostsidetests/backup/FullBackupOnly/FullBackupOnlyFalseWithAgentApp/Android.mk
index 62b9860..a27c75b 100644
--- a/hostsidetests/backup/FullBackupOnly/FullBackupOnlyFalseWithAgentApp/Android.mk
+++ b/hostsidetests/backup/FullBackupOnly/FullBackupOnlyFalseWithAgentApp/Android.mk
@@ -25,7 +25,7 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test platform-test-annotations
 
 LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
 
diff --git a/hostsidetests/backup/FullBackupOnly/FullBackupOnlyTrueWithAgentApp/Android.mk b/hostsidetests/backup/FullBackupOnly/FullBackupOnlyTrueWithAgentApp/Android.mk
index 8257ea7..ad892e8 100644
--- a/hostsidetests/backup/FullBackupOnly/FullBackupOnlyTrueWithAgentApp/Android.mk
+++ b/hostsidetests/backup/FullBackupOnly/FullBackupOnlyTrueWithAgentApp/Android.mk
@@ -25,7 +25,7 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test platform-test-annotations
 
 LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
 
diff --git a/hostsidetests/backup/FullBackupOnly/src/FullBackupOnlyTest.java b/hostsidetests/backup/FullBackupOnly/src/FullBackupOnlyTest.java
index 17a7e2c..df33281 100644
--- a/hostsidetests/backup/FullBackupOnly/src/FullBackupOnlyTest.java
+++ b/hostsidetests/backup/FullBackupOnly/src/FullBackupOnlyTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import android.platform.test.annotations.AppModeFull;
 import android.content.Context;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
@@ -41,6 +42,7 @@
  * test.
  */
 @RunWith(AndroidJUnit4.class)
+@AppModeFull
 public class FullBackupOnlyTest {
     private static final String TAG = "FullBackupOnlyTest";
 
diff --git a/hostsidetests/backup/KeyValueApp/Android.mk b/hostsidetests/backup/KeyValueApp/Android.mk
index d03f85f..da13f11 100644
--- a/hostsidetests/backup/KeyValueApp/Android.mk
+++ b/hostsidetests/backup/KeyValueApp/Android.mk
@@ -25,7 +25,7 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test platform-test-annotations
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/hostsidetests/backup/KeyValueApp/src/android/cts/backup/keyvaluerestoreapp/KeyValueBackupRestoreTest.java b/hostsidetests/backup/KeyValueApp/src/android/cts/backup/keyvaluerestoreapp/KeyValueBackupRestoreTest.java
index 807f5ae1..e77b815 100644
--- a/hostsidetests/backup/KeyValueApp/src/android/cts/backup/keyvaluerestoreapp/KeyValueBackupRestoreTest.java
+++ b/hostsidetests/backup/KeyValueApp/src/android/cts/backup/keyvaluerestoreapp/KeyValueBackupRestoreTest.java
@@ -30,6 +30,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
+import android.platform.test.annotations.AppModeFull;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 
@@ -55,6 +56,7 @@
  *
  */
 @RunWith(AndroidJUnit4.class)
+@AppModeFull
 public class KeyValueBackupRestoreTest {
     private static final String TAG = "KeyValueBackupRestore";
 
diff --git a/tests/tests/uirendering/Android.mk b/hostsidetests/backup/OtherSoundsSettingsApp/Android.mk
similarity index 61%
copy from tests/tests/uirendering/Android.mk
copy to hostsidetests/backup/OtherSoundsSettingsApp/Android.mk
index ec98a0e..a74bb1d 100644
--- a/tests/tests/uirendering/Android.mk
+++ b/hostsidetests/backup/OtherSoundsSettingsApp/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2014 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.
@@ -16,28 +16,29 @@
 
 include $(CLEAR_VARS)
 
-# don't include this package in any target
-LOCAL_MODULE_TAGS := optional
-
+# Don't include this package in any target
+LOCAL_MODULE_TAGS := tests
+# When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_DEX_PREOPT := false
 
-LOCAL_JAVA_LIBRARIES := android.test.runner.stubs
+LOCAL_PROGUARD_ENABLED := disabled
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    compatibility-device-util \
-    ctsdeviceutillegacy \
-    ctstestrunner \
-    mockito-target-minus-junit4 \
-    android-support-test
+    android-support-test \
+    platform-test-annotations \
+    compatibility-device-util
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_PACKAGE_NAME := CtsUiRenderingTestCases
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-# Enforce public / test api only
-LOCAL_SDK_VERSION := test_current
+LOCAL_PACKAGE_NAME := CtsBackupOtherSoundsSettingsApp
 
-include $(BUILD_CTS_PACKAGE)
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
+
+
diff --git a/tests/app/app2/AndroidManifest.xml b/hostsidetests/backup/OtherSoundsSettingsApp/AndroidManifest.xml
similarity index 64%
rename from tests/app/app2/AndroidManifest.xml
rename to hostsidetests/backup/OtherSoundsSettingsApp/AndroidManifest.xml
index 0926251..d7f54cc8 100644
--- a/tests/app/app2/AndroidManifest.xml
+++ b/hostsidetests/backup/OtherSoundsSettingsApp/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2017 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,16 +14,17 @@
   ~ 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.app2">
-    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+    package="android.cts.backup.othersoundssettingsapp">
 
-    <application>
+    <application android:label="OtherSoundsSettingsApp">
         <uses-library android:name="android.test.runner" />
-
-        <service android:name="android.app.stubs.LocalService"
-                 android:exported="true"/>
-        <service android:name=".AlertWindowService"
-                 android:exported="true"/>
     </application>
+
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.cts.backup.othersoundssettingsapp" />
 </manifest>
diff --git a/hostsidetests/backup/OtherSoundsSettingsApp/src/android/cts/backup/othersoundssettingsapp/OtherSoundsSettingsTest.java b/hostsidetests/backup/OtherSoundsSettingsApp/src/android/cts/backup/othersoundssettingsapp/OtherSoundsSettingsTest.java
new file mode 100644
index 0000000..27d3f7f
--- /dev/null
+++ b/hostsidetests/backup/OtherSoundsSettingsApp/src/android/cts/backup/othersoundssettingsapp/OtherSoundsSettingsTest.java
@@ -0,0 +1,165 @@
+/*
+ * 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.cts.backup.othersoundssettingsapp;
+
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.compatibility.common.util.BackupUtils.LOCAL_TRANSPORT_TOKEN;
+
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.AppModeFull;
+import android.content.ContentResolver;
+import android.os.ParcelFileDescriptor;
+import android.provider.Settings;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.BackupUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Device side routines to be invoked by the host side OtherSoundsSettingsHostSideTest. 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 OtherSoundsSettingsTest {
+    /** The name of the package for backup */
+    private static final String SETTINGS_PACKAGE_NAME = "com.android.providers.settings";
+
+    private ContentResolver mContentResolver;
+    private BackupUtils mBackupUtils;
+
+    @Before
+    public void setUp() throws Exception {
+        mContentResolver = getInstrumentation().getTargetContext().getContentResolver();
+        mBackupUtils =
+                new BackupUtils() {
+                    @Override
+                    protected InputStream executeShellCommand(String command) throws IOException {
+                        ParcelFileDescriptor pfd =
+                                getInstrumentation().getUiAutomation().executeShellCommand(command);
+                        return new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+                    }
+                };
+    }
+
+    /**
+     * Test backup and restore of Dial pad tones.
+     *
+     * Test logic:
+     * 1. Check Dial pad tones exists.
+     * 2. Backup Settings.
+     * 3. Toggle Dial pad tones.
+     * 4. Restore Settings.
+     * 5. Check restored Dial pad tones is the same with backup value.
+     */
+    @Test
+    public void testOtherSoundsSettings_dialPadTones() throws Exception {
+        int originalValue =
+                Settings.System.getInt(
+                        mContentResolver, Settings.System.DTMF_TONE_WHEN_DIALING, -1);
+        assertTrue("Dial pad tones does not exist.", originalValue != -1);
+
+        mBackupUtils.backupNowAndAssertSuccess(SETTINGS_PACKAGE_NAME);
+
+        boolean ret =
+                Settings.System.putInt(
+                        mContentResolver, Settings.System.DTMF_TONE_WHEN_DIALING,
+                        1 - originalValue);
+        assertTrue("Toggle Dial pad tones fail.", ret);
+
+        mBackupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, SETTINGS_PACKAGE_NAME);
+
+        int restoreValue =
+                Settings.System.getInt(
+                        mContentResolver, Settings.System.DTMF_TONE_WHEN_DIALING, -1);
+        assertTrue("Dial pad tones restore fail.", originalValue == restoreValue);
+    }
+
+    /**
+     * Test backup and restore of Touch sounds.
+     *
+     * Test logic:
+     * 1. Check Touch sounds exists.
+     * 2. Backup Settings.
+     * 3. Toggle Touch sounds.
+     * 4. Restore Settings.
+     * 5. Check restored Touch sounds is the same with backup value.
+     */
+    @Test
+    public void testOtherSoundsSettings_touchSounds() throws Exception {
+        int originalValue =
+                Settings.System.getInt(
+                        mContentResolver, Settings.System.SOUND_EFFECTS_ENABLED, -1);
+        assertTrue("Touch sounds does not exist.", originalValue != -1);
+
+        mBackupUtils.backupNowAndAssertSuccess(SETTINGS_PACKAGE_NAME);
+
+        boolean ret =
+                Settings.System.putInt(
+                        mContentResolver, Settings.System.SOUND_EFFECTS_ENABLED, 1 - originalValue);
+        assertTrue("Toggle Touch sounds fail.", ret);
+
+        mBackupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, SETTINGS_PACKAGE_NAME);
+
+        int restoreValue =
+                Settings.System.getInt(
+                        mContentResolver, Settings.System.SOUND_EFFECTS_ENABLED, -1);
+        assertTrue("Touch sounds restore fail.", originalValue == restoreValue);
+    }
+
+    /**
+     * Test backup and restore of Touch vibration.
+     *
+     * Test logic:
+     * 1. Check Touch vibration exists.
+     * 2. Backup Settings.
+     * 3. Toggle Touch vibration.
+     * 4. Restore Settings.
+     * 5. Check restored Touch vibration is the same with backup value.
+     */
+    @Test
+    public void testOtherSoundsSettings_touchVibration() throws Exception {
+        int originalValue =
+                Settings.System.getInt(
+                        mContentResolver, Settings.System.HAPTIC_FEEDBACK_ENABLED, -1);
+        assertTrue("Touch vibration does not exist.", originalValue != -1);
+
+        mBackupUtils.backupNowAndAssertSuccess(SETTINGS_PACKAGE_NAME);
+
+        boolean ret =
+                Settings.System.putInt(
+                        mContentResolver, Settings.System.HAPTIC_FEEDBACK_ENABLED,
+                        1 - originalValue);
+        assertTrue("Toggle Touch vibration fail.", ret);
+
+        mBackupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, SETTINGS_PACKAGE_NAME);
+
+        int restoreValue =
+                Settings.System.getInt(
+                        mContentResolver, Settings.System.HAPTIC_FEEDBACK_ENABLED, -1);
+        assertTrue("Touch vibration restore fail.", originalValue == restoreValue);
+    }
+}
\ No newline at end of file
diff --git a/hostsidetests/backup/RestoreAnyVersion/NewVersionApp/Android.mk b/hostsidetests/backup/RestoreAnyVersion/NewVersionApp/Android.mk
index 0a0efba..ad32558 100644
--- a/hostsidetests/backup/RestoreAnyVersion/NewVersionApp/Android.mk
+++ b/hostsidetests/backup/RestoreAnyVersion/NewVersionApp/Android.mk
@@ -25,7 +25,7 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test platform-test-annotations
 
 LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
 
diff --git a/hostsidetests/backup/RestoreAnyVersion/NoRestoreAnyVersionApp/Android.mk b/hostsidetests/backup/RestoreAnyVersion/NoRestoreAnyVersionApp/Android.mk
index 94d9376..2958849 100644
--- a/hostsidetests/backup/RestoreAnyVersion/NoRestoreAnyVersionApp/Android.mk
+++ b/hostsidetests/backup/RestoreAnyVersion/NoRestoreAnyVersionApp/Android.mk
@@ -25,7 +25,7 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test platform-test-annotations
 
 LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
 
diff --git a/hostsidetests/backup/RestoreAnyVersion/RestoreAnyVersionApp/Android.mk b/hostsidetests/backup/RestoreAnyVersion/RestoreAnyVersionApp/Android.mk
index 81652a7..431e53b 100644
--- a/hostsidetests/backup/RestoreAnyVersion/RestoreAnyVersionApp/Android.mk
+++ b/hostsidetests/backup/RestoreAnyVersion/RestoreAnyVersionApp/Android.mk
@@ -25,7 +25,7 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test platform-test-annotations
 
 LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
 
diff --git a/hostsidetests/backup/RestoreAnyVersion/src/RestoreAnyVersionTest.java b/hostsidetests/backup/RestoreAnyVersion/src/RestoreAnyVersionTest.java
index 1dbdb05..69f376b 100644
--- a/hostsidetests/backup/RestoreAnyVersion/src/RestoreAnyVersionTest.java
+++ b/hostsidetests/backup/RestoreAnyVersion/src/RestoreAnyVersionTest.java
@@ -22,6 +22,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import android.platform.test.annotations.AppModeFull;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -45,6 +46,7 @@
  * test.
  */
 @RunWith(AndroidJUnit4.class)
+@AppModeFull
 public class RestoreAnyVersionTest {
     private static final String TAG = "BackupTestRestoreAnyVer";
 
diff --git a/hostsidetests/backup/SharedPreferencesRestoreApp/Android.mk b/hostsidetests/backup/SharedPreferencesRestoreApp/Android.mk
index 4e4f0be..6d25d36 100644
--- a/hostsidetests/backup/SharedPreferencesRestoreApp/Android.mk
+++ b/hostsidetests/backup/SharedPreferencesRestoreApp/Android.mk
@@ -25,7 +25,7 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test platform-test-annotations
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/hostsidetests/backup/SuccessNotificationApp/Android.mk b/hostsidetests/backup/SuccessNotificationApp/Android.mk
index 8531225..70e74be 100644
--- a/hostsidetests/backup/SuccessNotificationApp/Android.mk
+++ b/hostsidetests/backup/SuccessNotificationApp/Android.mk
@@ -25,7 +25,7 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test platform-test-annotations
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/hostsidetests/backup/SuccessNotificationApp/src/android/cts/backup/successnotificationapp/SuccessNotificationTest.java b/hostsidetests/backup/SuccessNotificationApp/src/android/cts/backup/successnotificationapp/SuccessNotificationTest.java
index 3fb8ef2..5bf05a2 100644
--- a/hostsidetests/backup/SuccessNotificationApp/src/android/cts/backup/successnotificationapp/SuccessNotificationTest.java
+++ b/hostsidetests/backup/SuccessNotificationApp/src/android/cts/backup/successnotificationapp/SuccessNotificationTest.java
@@ -20,12 +20,14 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
+import android.platform.test.annotations.AppModeFull;
 import android.support.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
+@AppModeFull
 public class SuccessNotificationTest {
     protected static final String PREFS_FILE = "android.cts.backup.successnotificationapp.PREFS";
     private static final String KEY_VALUE_RESTORE_APP_PACKAGE =
diff --git a/tests/tests/uirendering/Android.mk b/hostsidetests/backup/SyncAdapterSettingsApp/Android.mk
similarity index 61%
copy from tests/tests/uirendering/Android.mk
copy to hostsidetests/backup/SyncAdapterSettingsApp/Android.mk
index ec98a0e..c9ea6bf 100644
--- a/tests/tests/uirendering/Android.mk
+++ b/hostsidetests/backup/SyncAdapterSettingsApp/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2014 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.
@@ -16,28 +16,27 @@
 
 include $(CLEAR_VARS)
 
-# don't include this package in any target
-LOCAL_MODULE_TAGS := optional
-
+# Don't include this package in any target
+LOCAL_MODULE_TAGS := tests
+# When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_DEX_PREOPT := false
 
-LOCAL_JAVA_LIBRARIES := android.test.runner.stubs
+LOCAL_PROGUARD_ENABLED := disabled
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    compatibility-device-util \
-    ctsdeviceutillegacy \
-    ctstestrunner \
-    mockito-target-minus-junit4 \
-    android-support-test
+        android-support-test \
+        platform-test-annotations \
+        compatibility-device-util
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_PACKAGE_NAME := CtsUiRenderingTestCases
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-# Enforce public / test api only
-LOCAL_SDK_VERSION := test_current
+LOCAL_PACKAGE_NAME := CtsBackupSyncAdapterSettingsApp
 
-include $(BUILD_CTS_PACKAGE)
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/backup/SyncAdapterSettingsApp/AndroidManifest.xml b/hostsidetests/backup/SyncAdapterSettingsApp/AndroidManifest.xml
new file mode 100644
index 0000000..3cda4e6
--- /dev/null
+++ b/hostsidetests/backup/SyncAdapterSettingsApp/AndroidManifest.xml
@@ -0,0 +1,50 @@
+<?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
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.cts.backup.syncadaptersettingsapp">
+
+    <uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
+    <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
+
+    <application android:label="BackupSyncAdapterSettings">
+        <uses-library android:name="android.test.runner"/>
+        <service android:name=".SyncAdapterSettingsAuthenticator">
+            <intent-filter>
+                <action android:name="android.accounts.AccountAuthenticator"/>
+            </intent-filter>
+            <meta-data
+                android:name="android.accounts.AccountAuthenticator"
+                android:resource="@xml/authenticator"/>
+        </service>
+
+        <service android:name=".SyncAdapterSettingsService"
+                 android:exported="false">
+            <intent-filter>
+                <action android:name="android.content.SyncAdapter"/>
+            </intent-filter>
+            <meta-data android:name="android.content.SyncAdapter"
+                       android:resource="@xml/syncadapter"/>
+        </service>
+
+        <provider android:name=".SyncAdapterSettingsProvider"
+                  android:authorities="android.cts.backup.syncadaptersettingsapp.provider"/>
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.cts.backup.syncadaptersettingsapp"/>
+</manifest>
diff --git a/hostsidetests/backup/SyncAdapterSettingsApp/res/xml/authenticator.xml b/hostsidetests/backup/SyncAdapterSettingsApp/res/xml/authenticator.xml
new file mode 100644
index 0000000..a0ec2f2
--- /dev/null
+++ b/hostsidetests/backup/SyncAdapterSettingsApp/res/xml/authenticator.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
+                       android:accountType="android.cts.backup.syncadaptersettingsapp"
+                       android:label="BackupSyncAdapterSettings"/>
diff --git a/hostsidetests/backup/SyncAdapterSettingsApp/res/xml/syncadapter.xml b/hostsidetests/backup/SyncAdapterSettingsApp/res/xml/syncadapter.xml
new file mode 100644
index 0000000..bbe551c
--- /dev/null
+++ b/hostsidetests/backup/SyncAdapterSettingsApp/res/xml/syncadapter.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
+              android:contentAuthority="android.cts.backup.syncadaptersettingsapp.provider"
+              android:accountType="android.cts.backup.syncadaptersettingsapp"
+/>
diff --git a/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsAdapter.java b/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsAdapter.java
new file mode 100644
index 0000000..9bb0dca
--- /dev/null
+++ b/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsAdapter.java
@@ -0,0 +1,38 @@
+/*
+ * 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.cts.backup.syncadaptersettingsapp;
+
+import android.accounts.Account;
+import android.content.AbstractThreadedSyncAdapter;
+import android.content.ContentProviderClient;
+import android.content.Context;
+import android.content.SyncResult;
+import android.os.Bundle;
+
+/**
+ * Sync adapter for the sync adapter settings test.
+ */
+public class SyncAdapterSettingsAdapter extends AbstractThreadedSyncAdapter {
+
+    public SyncAdapterSettingsAdapter(Context context) {
+        super(context, false);
+    }
+
+    @Override
+    public void onPerformSync(Account account, Bundle extras, String authority,
+            ContentProviderClient provider, SyncResult syncResult) {
+    }
+}
diff --git a/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsAuthenticator.java b/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsAuthenticator.java
new file mode 100644
index 0000000..2eb4209
--- /dev/null
+++ b/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsAuthenticator.java
@@ -0,0 +1,94 @@
+/*
+ * 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.cts.backup.syncadaptersettingsapp;
+
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.NetworkErrorException;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+
+/**
+ * Authenticator for the sync adapter settings test.
+ */
+public class SyncAdapterSettingsAuthenticator extends Service {
+
+    private Authenticator mAuthenticator;
+
+    @Override
+    public void onCreate() {
+        mAuthenticator = new Authenticator(this);
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mAuthenticator.getIBinder();
+    }
+
+    public static class Authenticator extends AbstractAccountAuthenticator {
+
+        public Authenticator(Context context) {
+            super(context);
+        }
+
+        @Override
+        public Bundle addAccount(AccountAuthenticatorResponse response, String accountType,
+                String authTokenType, String[] requiredFeatures, Bundle options)
+                throws NetworkErrorException {
+            return null;
+        }
+
+        @Override
+        public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account,
+                String authTokenType, Bundle options) throws NetworkErrorException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account,
+                Bundle options) throws NetworkErrorException {
+            return null;
+        }
+
+        @Override
+        public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account,
+                String authTokenType, Bundle options) throws NetworkErrorException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String getAuthTokenLabel(String authTokenType) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account,
+                String[] features) throws NetworkErrorException {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
+
diff --git a/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsProvider.java b/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsProvider.java
new file mode 100644
index 0000000..1f6839a
--- /dev/null
+++ b/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsProvider.java
@@ -0,0 +1,61 @@
+/*
+ * 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.cts.backup.syncadaptersettingsapp;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+/**
+ * Provider for the sync adapter settings test.
+ */
+public class SyncAdapterSettingsProvider extends ContentProvider {
+    public static final String AUTHORITY = "android.cts.backup.syncadaptersettingsapp.provider";
+
+    @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) {
+        return null;
+    }
+
+    @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/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsService.java b/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsService.java
new file mode 100644
index 0000000..12ccb8c
--- /dev/null
+++ b/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsService.java
@@ -0,0 +1,39 @@
+/*
+ * 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.cts.backup.syncadaptersettingsapp;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * Service for the sync adapter settings test.
+ */
+public class SyncAdapterSettingsService extends Service {
+
+    private static SyncAdapterSettingsAdapter sAdapter;
+    private static final Object LOCK = new Object();
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        synchronized (LOCK) {
+            if (sAdapter == null) {
+                sAdapter = new SyncAdapterSettingsAdapter(getApplicationContext());
+            }
+        }
+        return sAdapter.getSyncAdapterBinder();
+    }
+}
diff --git a/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsTest.java b/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsTest.java
new file mode 100644
index 0000000..cb4482a
--- /dev/null
+++ b/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsTest.java
@@ -0,0 +1,251 @@
+/*
+ * 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.cts.backup.syncadaptersettingsapp;
+
+import static com.android.compatibility.common.util.BackupUtils.LOCAL_TRANSPORT_TOKEN;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.Instrumentation;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.platform.test.annotations.AppModeFull;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.BackupUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.InputStream;
+import java.util.Arrays;
+
+/**
+ * Device side routines to be invoked by the host side SyncAdapterSettingsHostSideTest. 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 SyncAdapterSettingsTest {
+
+    /** The name of the package for backup */
+    private static final String ANDROID_PACKAGE = "android";
+
+    private static final String ACCOUNT_NAME = "SyncAdapterSettings";
+    private static final String ACCOUNT_TYPE = "android.cts.backup.syncadaptersettingsapp";
+
+    private Context mContext;
+    private Account mAccount;
+    private BackupUtils mBackupUtils =
+            new BackupUtils() {
+                @Override
+                protected InputStream executeShellCommand(String command) {
+                    Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+                    ParcelFileDescriptor pfd =
+                            instrumentation.getUiAutomation().executeShellCommand(command);
+                    return new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+                }
+            };
+
+    @Before
+    public void setUp() {
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        mContext = instrumentation.getTargetContext();
+        mAccount = getAccount();
+    }
+
+    /**
+     * Test backup and restore of MasterSyncAutomatically=true.
+     *
+     * Test logic:
+     * 1. Enable MasterSyncAutomatically.
+     * 2. Backup android package, disable MasterSyncAutomatically and restore android package.
+     * 3. Check restored MasterSyncAutomatically=true is the same with backup value.
+     */
+    @Test
+    public void testMasterSyncAutomatically_whenOn_isRestored() throws Exception {
+        ContentResolver.setMasterSyncAutomatically(true);
+
+        mBackupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE);
+        ContentResolver.setMasterSyncAutomatically(false);
+        mBackupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE);
+
+        assertTrue(ContentResolver.getMasterSyncAutomatically());
+    }
+
+    /**
+     * Test backup and restore of MasterSyncAutomatically=false.
+     *
+     * Test logic:
+     * 1. Disable MasterSyncAutomatically.
+     * 2. Backup android package, enable MasterSyncAutomatically and restore android package.
+     * 3. Check restored MasterSyncAutomatically=false is the same with backup value.
+     */
+    @Test
+    public void testMasterSyncAutomatically_whenOff_isRestored() throws Exception {
+        ContentResolver.setMasterSyncAutomatically(false);
+
+        mBackupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE);
+        ContentResolver.setMasterSyncAutomatically(true);
+        mBackupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE);
+
+        assertFalse(ContentResolver.getMasterSyncAutomatically());
+    }
+
+    /**
+     * Test that if syncEnabled=true and isSyncable=1 in restore data, syncEnabled will be true
+     * and isSyncable will be left as the current value in the device.
+     *
+     * Test logic:
+     * 1. Add an account if account does not exist and set syncEnabled=true, isSyncable=1.
+     * 2. Backup android package, set syncEnabled=false and set isSyncable=0. Then restore
+     * android package.
+     * 3. Check restored syncEnabled=true and isSyncable=0. Then remove account.
+     *
+     * @see AccountSyncSettingsBackupHelper#restoreExistingAccountSyncSettingsFromJSON(JSONObject)
+     */
+    @Test
+    public void testIsSyncableChanged_ifTurnOnSyncEnabled() throws Exception {
+        addAccount(mContext);
+        initSettings(/* masterSyncAutomatically= */true, /* syncAutomatically= */
+                true, /* isSyncable= */1);
+
+        mBackupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE);
+        setSyncAutomaticallyAndIsSyncable(false, 0);
+        mBackupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE);
+
+        assertSettings(/* syncAutomatically= */true, /* isSyncable= */0);
+        removeAccount(mContext);
+    }
+
+    /**
+     * Test that if syncEnabled=false and isSyncable=0 in restore data, syncEnabled will be false
+     * and isSyncable will be set to 0.
+     *
+     * Test logic:
+     * 1. Add an account if account does not exist and set syncEnabled=false, isSyncable=0.
+     * 2. Backup android package, set syncEnabled=true and set isSyncable=1. Then restore android
+     * package.
+     * 3. Check restored syncEnabled=false and isSyncable=0. Then remove account.
+     *
+     * @see AccountSyncSettingsBackupHelper#restoreExistingAccountSyncSettingsFromJSON(JSONObject)
+     */
+    @Test
+    public void testIsSyncableIsZero_ifTurnOffSyncEnabled() throws Exception {
+        addAccount(mContext);
+        initSettings(/* masterSyncAutomatically= */true, /* syncAutomatically= */
+                false, /* isSyncable= */0);
+
+        mBackupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE);
+        setSyncAutomaticallyAndIsSyncable(true, 1);
+        mBackupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE);
+
+        assertSettings(/* syncAutomatically= */false, /* isSyncable= */0);
+        removeAccount(mContext);
+    }
+
+    /**
+     * Test that if syncEnabled=false and isSyncable=1 in restore data, syncEnabled will be false
+     * and isSyncable will be set to 1.
+     * According to
+     * {@link AccountSyncSettingsBackupHelper#restoreExistingAccountSyncSettingsFromJSON(JSONObject)}
+     * isSyncable will be set to 2, but function
+     * {@link ContentService#setIsSyncable(Account, String, int)} would call
+     * {@link ContentService#normalizeSyncable(int)} and set isSyncable to 1.
+     *
+     * Test logic:
+     * 1. Add an account if account does not exist and set syncEnabled=false, isSyncable=1.
+     * 2. Backup android package, set syncEnabled=true and set isSyncable=0. Then restore android
+     * package.
+     * 3. Check restored syncEnabled=false and isSyncable=1. Then remove account.
+     */
+    @Test
+    public void testIsSyncableIsOne_ifTurnOffSyncEnabled() throws Exception {
+        addAccount(mContext);
+        initSettings(/* masterSyncAutomatically= */true, /* syncAutomatically= */
+                false, /* isSyncable= */1);
+
+        mBackupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE);
+        setSyncAutomaticallyAndIsSyncable(true, 0);
+        mBackupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE);
+
+        assertSettings(/* syncAutomatically= */false, /* isSyncable= */1);
+        removeAccount(mContext);
+    }
+
+    private void initSettings(boolean masterSyncAutomatically, boolean syncAutomatically,
+            int isSyncable) throws Exception {
+        ContentResolver.setMasterSyncAutomatically(masterSyncAutomatically);
+        setSyncAutomaticallyAndIsSyncable(syncAutomatically, isSyncable);
+    }
+
+    private void setSyncAutomaticallyAndIsSyncable(boolean syncAutomatically, int isSyncable)
+            throws Exception {
+        ContentResolver.setSyncAutomatically(mAccount, SyncAdapterSettingsProvider.AUTHORITY,
+                syncAutomatically);
+
+        ContentResolver.setIsSyncable(mAccount, SyncAdapterSettingsProvider.AUTHORITY, isSyncable);
+    }
+
+    private void assertSettings(boolean syncAutomatically, int isSyncable) throws Exception {
+        assertEquals(syncAutomatically, ContentResolver.getSyncAutomatically(mAccount,
+                SyncAdapterSettingsProvider.AUTHORITY));
+        assertEquals(isSyncable,
+                ContentResolver.getIsSyncable(mAccount, SyncAdapterSettingsProvider.AUTHORITY));
+    }
+
+    /**
+     * Get the account.
+     */
+    private Account getAccount() {
+        return new Account(ACCOUNT_NAME, ACCOUNT_TYPE);
+    }
+
+    /**
+     * Add an account, if it doesn't exist yet.
+     */
+    private void addAccount(Context context) {
+        final String password = "password";
+
+        final AccountManager am = context.getSystemService(AccountManager.class);
+
+        if (!Arrays.asList(am.getAccountsByType(mAccount.type)).contains(mAccount)) {
+            am.addAccountExplicitly(mAccount, password, new Bundle());
+        }
+    }
+
+    /**
+     * Remove the account.
+     */
+    private void removeAccount(Context context) {
+        final AccountManager am = context.getSystemService(AccountManager.class);
+        final Account[] accounts = am.getAccountsByType(ACCOUNT_TYPE);
+
+        for (int i = 0, size = accounts.length; i < size; i++) {
+            Account account = accounts[i];
+            am.removeAccountExplicitly(account);
+        }
+    }
+}
diff --git a/hostsidetests/backup/fullbackupapp/Android.mk b/hostsidetests/backup/fullbackupapp/Android.mk
index 40a90d6..6edd939 100644
--- a/hostsidetests/backup/fullbackupapp/Android.mk
+++ b/hostsidetests/backup/fullbackupapp/Android.mk
@@ -25,7 +25,7 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test platform-test-annotations
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/hostsidetests/backup/fullbackupapp/src/android/cts/backup/fullbackupapp/FullbackupTest.java b/hostsidetests/backup/fullbackupapp/src/android/cts/backup/fullbackupapp/FullbackupTest.java
index ee49b87..61f868b 100644
--- a/hostsidetests/backup/fullbackupapp/src/android/cts/backup/fullbackupapp/FullbackupTest.java
+++ b/hostsidetests/backup/fullbackupapp/src/android/cts/backup/fullbackupapp/FullbackupTest.java
@@ -22,6 +22,7 @@
 import static org.junit.Assert.assertFalse;
 
 import android.content.Context;
+import android.platform.test.annotations.AppModeFull;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 
@@ -40,6 +41,7 @@
  * 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 FullbackupTest {
     public static final String TAG = "FullbackupCTSApp";
     private static final int FILE_SIZE_BYTES = 1024 * 1024;
diff --git a/hostsidetests/backup/includeexcludeapp/Android.mk b/hostsidetests/backup/includeexcludeapp/Android.mk
index 2043a14..0232d34 100644
--- a/hostsidetests/backup/includeexcludeapp/Android.mk
+++ b/hostsidetests/backup/includeexcludeapp/Android.mk
@@ -25,7 +25,7 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test platform-test-annotations
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/hostsidetests/backup/includeexcludeapp/src/android/cts/backup/includeexcludeapp/IncludeExcludeTest.java b/hostsidetests/backup/includeexcludeapp/src/android/cts/backup/includeexcludeapp/IncludeExcludeTest.java
index 286d7a3..80e6b5b 100644
--- a/hostsidetests/backup/includeexcludeapp/src/android/cts/backup/includeexcludeapp/IncludeExcludeTest.java
+++ b/hostsidetests/backup/includeexcludeapp/src/android/cts/backup/includeexcludeapp/IncludeExcludeTest.java
@@ -25,6 +25,7 @@
 
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.platform.test.annotations.AppModeFull;
 import android.support.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
@@ -44,6 +45,7 @@
  * 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 IncludeExcludeTest {
     public static final String TAG = "IncludeExcludeCTSApp";
     private static final int FILE_SIZE_BYTES = 1024 * 1024;
diff --git a/hostsidetests/backup/src/android/cts/backup/AllowBackupHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/AllowBackupHostSideTest.java
index 73373d3..1a8bb22 100644
--- a/hostsidetests/backup/src/android/cts/backup/AllowBackupHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/AllowBackupHostSideTest.java
@@ -18,6 +18,8 @@
 
 import static org.junit.Assert.assertNull;
 
+import android.platform.test.annotations.AppModeFull;
+
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -48,6 +50,7 @@
  * android.cts.backup.backupnotallowedapp.AllowBackupTest.
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
+@AppModeFull
 public class AllowBackupHostSideTest extends BaseBackupHostSideTest {
 
     private static final String ALLOWBACKUP_APP_NAME = "android.cts.backup.backupnotallowedapp";
@@ -87,10 +90,7 @@
         // Generate the files that are going to be backed up.
         checkAllowBackupDeviceTest("createFiles");
 
-        // Do a backup
-        String backupnowOutput = backupNow(ALLOWBACKUP_APP_NAME);
-
-        assertBackupIsNotAllowed(ALLOWBACKUP_APP_NAME, backupnowOutput);
+        getBackupUtils().backupNowAndAssertBackupNotAllowed(ALLOWBACKUP_APP_NAME);
 
         assertNull(uninstallPackage(ALLOWBACKUP_APP_NAME));
 
@@ -112,9 +112,7 @@
         checkAllowBackupDeviceTest("createFiles");
 
         // Do a backup
-        String backupnowOutput = backupNow(ALLOWBACKUP_APP_NAME);
-
-        assertBackupIsSuccessful(ALLOWBACKUP_APP_NAME, backupnowOutput);
+        getBackupUtils().backupNowAndAssertSuccess(ALLOWBACKUP_APP_NAME);
 
         assertNull(uninstallPackage(ALLOWBACKUP_APP_NAME));
 
diff --git a/hostsidetests/backup/src/android/cts/backup/BaseBackupHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/BaseBackupHostSideTest.java
index 9c931da..698a697 100644
--- a/hostsidetests/backup/src/android/cts/backup/BaseBackupHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/BaseBackupHostSideTest.java
@@ -19,7 +19,11 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import android.platform.test.annotations.AppModeFull;
+
+import com.android.compatibility.common.util.BackupUtils;
 import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -28,7 +32,10 @@
 import org.junit.Before;
 import org.junit.runner.RunWith;
 
-import java.util.Scanner;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -36,6 +43,7 @@
  * Base class for CTS backup/restore hostside tests
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
+@AppModeFull
 public abstract class BaseBackupHostSideTest extends BaseHostJUnit4Test {
     protected boolean mIsBackupSupported;
 
@@ -45,8 +53,15 @@
     protected static final String LOCAL_TRANSPORT =
             "android/com.android.internal.backup.LocalTransport";
 
+    private BackupUtils mBackupUtils = new BackupUtils() {
+        @Override
+        protected InputStream executeShellCommand(String command) throws IOException {
+            return executeDeviceShellCommand(getDevice(), command);
+        }
+    };
+
     @Before
-    public void setUp() throws DeviceNotAvailableException, Exception {
+    public void setUp() throws Exception {
         mIsBackupSupported = getDevice().hasFeature("feature:" + FEATURE_BACKUP);
         if (!mIsBackupSupported) {
             CLog.i("android.software.backup feature is not supported on this device");
@@ -55,7 +70,7 @@
 
         // Check that the backup wasn't disabled and the transport wasn't switched unexpectedly.
         assertTrue("Backup was unexpectedly disabled during the module test run",
-                isBackupEnabled());
+                getBackupUtils().isBackupEnabled());
         assertEquals("LocalTransport should be selected at this point", LOCAL_TRANSPORT,
                 getCurrentTransport());
     }
@@ -65,36 +80,8 @@
         // Not deleting to avoid breaking the tests calling super.tearDown()
     }
 
-    /**
-     * Execute shell command "bmgr backupnow <packageName>" and return output from this command.
-     */
-    protected String backupNow(String packageName) throws DeviceNotAvailableException {
-        return getDevice().executeShellCommand("bmgr backupnow " + packageName);
-    }
-
-    /**
-     * Execute shell command "bmgr backupnow <packageName>" and assert success.
-     */
-    protected void backupNowAndAssertSuccess(String packageName)
-            throws DeviceNotAvailableException {
-        String backupnowOutput = backupNow(packageName);
-
-        assertBackupIsSuccessful(packageName, backupnowOutput);
-    }
-
-    /**
-     * Execute shell command "bmgr restore <packageName>" and assert success.
-     */
-    protected void restoreAndAssertSuccess(String packageName) throws DeviceNotAvailableException {
-        String restoreOutput = restore(packageName);
-        assertRestoreIsSuccessful(restoreOutput);
-    }
-
-    /**
-     * Execute shell command "bmgr restore <packageName>" and return output from this command.
-     */
-    protected String restore(String packageName) throws DeviceNotAvailableException {
-        return getDevice().executeShellCommand("bmgr restore " + packageName);
+    protected BackupUtils getBackupUtils() {
+        return mBackupUtils;
     }
 
     /**
@@ -114,62 +101,6 @@
         assertTrue("Device test failed: " + testName, result);
     }
 
-    /**
-     * Parsing the output of "bmgr backupnow" command and checking that the package under test
-     * was backed up successfully.
-     *
-     * Expected format: "Package <packageName> with result: Success"
-     */
-    protected void assertBackupIsSuccessful(String packageName, String backupnowOutput) {
-        Scanner in = new Scanner(backupnowOutput);
-        boolean success = false;
-        while (in.hasNextLine()) {
-            String line = in.nextLine();
-
-            if (line.contains(packageName)) {
-                String result = line.split(":")[1].trim();
-                if ("Success".equals(result)) {
-                    success = true;
-                }
-            }
-        }
-        in.close();
-        assertTrue(success);
-    }
-
-    /**
-     * Parsing the output of "bmgr backupnow" command and checking that the package under test
-     * wasn't backed up because backup is not allowed
-     *
-     * Expected format: "Package <packageName> with result:  Backup is not allowed"
-     */
-    protected void assertBackupIsNotAllowed(String packageName, String backupnowOutput) {
-        Scanner in = new Scanner(backupnowOutput);
-        boolean found = false;
-        while (in.hasNextLine()) {
-            String line = in.nextLine();
-
-            if (line.contains(packageName)) {
-                String result = line.split(":")[1].trim();
-                if ("Backup is not allowed".equals(result)) {
-                    found = true;
-                }
-            }
-        }
-        in.close();
-        assertTrue("Didn't find \'Backup not allowed\' in the output", found);
-    }
-
-    /**
-     * Parsing the output of "bmgr restore" command and checking that the package under test
-     * was restored successfully.
-     *
-     * Expected format: "restoreFinished: 0"
-     */
-    protected void assertRestoreIsSuccessful(String restoreOutput) {
-        assertTrue("Restore not successful", restoreOutput.contains("restoreFinished: 0"));
-    }
-
     protected void startActivityInPackageAndWait(String packageName, String className)
             throws DeviceNotAvailableException {
         getDevice().executeShellCommand(String.format(
@@ -195,19 +126,6 @@
         getDevice().executeShellCommand(String.format("pm clear %s", packageName));
     }
 
-    protected boolean isBackupEnabled() throws DeviceNotAvailableException {
-        boolean isEnabled;
-        String output = getDevice().executeShellCommand("bmgr enabled");
-        Pattern pattern = Pattern.compile("^Backup Manager currently (enabled|disabled)$");
-        Matcher matcher = pattern.matcher(output.trim());
-        if (matcher.find()) {
-            isEnabled = "enabled".equals(matcher.group(1));
-        } else {
-            throw new RuntimeException("non-parsable output setting bmgr enabled: " + output);
-        }
-        return isEnabled;
-    }
-
     protected String getCurrentTransport() throws DeviceNotAvailableException {
         String output = getDevice().executeShellCommand("bmgr list transports");
         Pattern pattern = Pattern.compile("\\* (.*)");
@@ -231,4 +149,14 @@
     protected void disableFakeEncryptionOnTransport() throws Exception {
         setLocalTransportParameters("fake_encryption_flag=false");
     }
+
+    private static InputStream executeDeviceShellCommand(
+            ITestDevice device, String command) throws IOException {
+        try {
+            String result = device.executeShellCommand(command);
+            return new ByteArrayInputStream(result.getBytes(StandardCharsets.UTF_8));
+        } catch (DeviceNotAvailableException e) {
+            throw new IOException(e);
+        }
+    }
 }
diff --git a/hostsidetests/backup/src/android/cts/backup/FullBackupOnlyHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/FullBackupOnlyHostSideTest.java
index 6589993..9ca68a0 100644
--- a/hostsidetests/backup/src/android/cts/backup/FullBackupOnlyHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/FullBackupOnlyHostSideTest.java
@@ -18,6 +18,8 @@
 
 import static org.junit.Assert.assertNull;
 
+import android.platform.test.annotations.AppModeFull;
+
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -47,6 +49,7 @@
  * - Only file in no_backup folder for key/value.
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
+@AppModeFull
 public class FullBackupOnlyHostSideTest extends BaseBackupHostSideTest {
 
     private static final String FULLBACKUPONLY_APP_PACKAGE = "android.cts.backup.fullbackuponlyapp";
@@ -105,7 +108,7 @@
 
         checkFullBackupOnlyDeviceTest("createFiles");
 
-        backupNowAndAssertSuccess(FULLBACKUPONLY_APP_PACKAGE);
+        getBackupUtils().backupNowAndAssertSuccess(FULLBACKUPONLY_APP_PACKAGE);
 
         assertNull(uninstallPackage(FULLBACKUPONLY_APP_PACKAGE));
 
@@ -131,7 +134,7 @@
 
         checkFullBackupOnlyDeviceTest("createFiles");
 
-        backupNowAndAssertSuccess(FULLBACKUPONLY_APP_PACKAGE);
+        getBackupUtils().backupNowAndAssertSuccess(FULLBACKUPONLY_APP_PACKAGE);
 
         assertNull(uninstallPackage(FULLBACKUPONLY_APP_PACKAGE));
 
@@ -157,7 +160,7 @@
 
         checkFullBackupOnlyDeviceTest("createFiles");
 
-        backupNowAndAssertSuccess(FULLBACKUPONLY_APP_PACKAGE);
+        getBackupUtils().backupNowAndAssertSuccess(FULLBACKUPONLY_APP_PACKAGE);
 
         assertNull(uninstallPackage(FULLBACKUPONLY_APP_PACKAGE));
 
diff --git a/hostsidetests/backup/src/android/cts/backup/FullbackupRulesHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/FullbackupRulesHostSideTest.java
index c6538d0..a7cd5a0 100644
--- a/hostsidetests/backup/src/android/cts/backup/FullbackupRulesHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/FullbackupRulesHostSideTest.java
@@ -16,6 +16,11 @@
 
 package android.cts.backup;
 
+import static com.android.compatibility.common.util.BackupUtils.LOCAL_TRANSPORT_TOKEN;
+
+import android.platform.test.annotations.AppModeFull;
+
+import com.android.compatibility.common.util.BackupUtils;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.log.LogUtil.CLog;
@@ -33,6 +38,7 @@
  * android.cts.backup.includeexcludeapp.IncludeExcludeTest.
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
+@AppModeFull
 public class FullbackupRulesHostSideTest extends BaseBackupHostSideTest {
 
     private static final String FULLBACKUP_TESTS_APP_NAME = "android.cts.backup.fullbackupapp";
@@ -61,18 +67,14 @@
                 "createFiles");
 
         // Do a backup
-        String backupnowOutput = backupNow(FULLBACKUP_TESTS_APP_NAME);
-
-        assertBackupIsSuccessful(FULLBACKUP_TESTS_APP_NAME, backupnowOutput);
+        getBackupUtils().backupNowAndAssertSuccess(FULLBACKUP_TESTS_APP_NAME);
 
         // Delete the files
         checkDeviceTest(FULLBACKUP_TESTS_APP_NAME, FULLBACKUP_DEVICE_TEST_CLASS_NAME,
                 "deleteFilesAfterBackup");
 
         // Do a restore
-        String restoreOutput = restore(FULLBACKUP_TESTS_APP_NAME);
-
-        assertRestoreIsSuccessful(restoreOutput);
+        getBackupUtils().restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, FULLBACKUP_TESTS_APP_NAME);
 
         // Check that the right files were restored
         checkDeviceTest(FULLBACKUP_TESTS_APP_NAME, FULLBACKUP_DEVICE_TEST_CLASS_NAME,
@@ -91,16 +93,15 @@
                 "createFiles");
 
         // Do a backup
-        String backupNowOutput = backupNow(INCLUDE_EXCLUDE_TESTS_APP_NAME);
-        assertBackupIsSuccessful(INCLUDE_EXCLUDE_TESTS_APP_NAME, backupNowOutput);
+        getBackupUtils().backupNowAndAssertSuccess(INCLUDE_EXCLUDE_TESTS_APP_NAME);
 
         // Delete the files
         checkDeviceTest(INCLUDE_EXCLUDE_TESTS_APP_NAME, INCLUDE_EXCLUDE_DEVICE_TEST_CLASS_NAME,
                 "deleteFilesAfterBackup");
 
         // Do a restore
-        String restoreOutput = restore(INCLUDE_EXCLUDE_TESTS_APP_NAME);
-        assertRestoreIsSuccessful(restoreOutput);
+        getBackupUtils()
+                .restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, INCLUDE_EXCLUDE_TESTS_APP_NAME);
 
         // Check that the right files were restored
         checkDeviceTest(INCLUDE_EXCLUDE_TESTS_APP_NAME, INCLUDE_EXCLUDE_DEVICE_TEST_CLASS_NAME,
@@ -122,16 +123,15 @@
                 "createFiles");
 
         // Do a backup
-        String backupNowOutput = backupNow(INCLUDE_EXCLUDE_TESTS_APP_NAME);
-        assertBackupIsSuccessful(INCLUDE_EXCLUDE_TESTS_APP_NAME, backupNowOutput);
+        getBackupUtils().backupNowAndAssertSuccess(INCLUDE_EXCLUDE_TESTS_APP_NAME);
 
         // Delete the files
         checkDeviceTest(INCLUDE_EXCLUDE_TESTS_APP_NAME, INCLUDE_EXCLUDE_DEVICE_TEST_CLASS_NAME,
                 "deleteFilesAfterBackup");
 
         // Do a restore
-        String restoreOutput = restore(INCLUDE_EXCLUDE_TESTS_APP_NAME);
-        assertRestoreIsSuccessful(restoreOutput);
+        getBackupUtils()
+                .restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, INCLUDE_EXCLUDE_TESTS_APP_NAME);
 
         // Check that the client-side encryption files were restored
         checkDeviceTest(INCLUDE_EXCLUDE_TESTS_APP_NAME, INCLUDE_EXCLUDE_DEVICE_TEST_CLASS_NAME,
@@ -153,16 +153,15 @@
                 "createFiles");
 
         // Do a backup
-        String backupNowOutput = backupNow(INCLUDE_EXCLUDE_TESTS_APP_NAME);
-        assertBackupIsSuccessful(INCLUDE_EXCLUDE_TESTS_APP_NAME, backupNowOutput);
+        getBackupUtils().backupNowAndAssertSuccess(INCLUDE_EXCLUDE_TESTS_APP_NAME);
 
         // Delete the files
         checkDeviceTest(INCLUDE_EXCLUDE_TESTS_APP_NAME, INCLUDE_EXCLUDE_DEVICE_TEST_CLASS_NAME,
                 "deleteFilesAfterBackup");
 
         // Do a restore
-        String restoreOutput = restore(INCLUDE_EXCLUDE_TESTS_APP_NAME);
-        assertRestoreIsSuccessful(restoreOutput);
+        getBackupUtils()
+                .restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, INCLUDE_EXCLUDE_TESTS_APP_NAME);
 
         // Check that the client-side encryption files were not restored
         checkDeviceTest(INCLUDE_EXCLUDE_TESTS_APP_NAME, INCLUDE_EXCLUDE_DEVICE_TEST_CLASS_NAME,
diff --git a/hostsidetests/backup/src/android/cts/backup/KeyValueBackupRestoreHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/KeyValueBackupRestoreHostSideTest.java
index 5ce86d9..f3944b4 100644
--- a/hostsidetests/backup/src/android/cts/backup/KeyValueBackupRestoreHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/KeyValueBackupRestoreHostSideTest.java
@@ -16,8 +16,12 @@
 
 package android.cts.backup;
 
+import static com.android.compatibility.common.util.BackupUtils.LOCAL_TRANSPORT_TOKEN;
+
 import static org.junit.Assert.assertNull;
 
+import android.platform.test.annotations.AppModeFull;
+
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -35,6 +39,7 @@
  * NB: The tests use "bmgr backupnow" for backup, which works on N+ devices.
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
+@AppModeFull
 public class KeyValueBackupRestoreHostSideTest extends BaseBackupHostSideTest {
 
     /** The name of the package of the app under test */
@@ -103,7 +108,7 @@
 
         checkDeviceTest("saveSharedPreferencesAndNotifyBackupManager");
 
-        backupNowAndAssertSuccess(KEY_VALUE_RESTORE_APP_PACKAGE);
+        getBackupUtils().backupNowAndAssertSuccess(KEY_VALUE_RESTORE_APP_PACKAGE);
 
         assertNull(uninstallPackage(KEY_VALUE_RESTORE_APP_PACKAGE));
 
@@ -145,11 +150,12 @@
 
         checkDeviceTest("launchSharedPrefActivity");
 
-        backupNowAndAssertSuccess(SHARED_PREFERENCES_RESTORE_APP_PACKAGE);
+        getBackupUtils().backupNowAndAssertSuccess(SHARED_PREFERENCES_RESTORE_APP_PACKAGE);
 
         checkDeviceTest("updateSharedPrefActivity");
 
-        restoreAndAssertSuccess(SHARED_PREFERENCES_RESTORE_APP_PACKAGE);
+        getBackupUtils().restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN,
+                SHARED_PREFERENCES_RESTORE_APP_PACKAGE);
 
         checkDeviceTest("checkSharedPrefActivity");
     }
diff --git a/hostsidetests/backup/src/android/cts/backup/OtherSoundsSettingsHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/OtherSoundsSettingsHostSideTest.java
new file mode 100644
index 0000000..6d5b731
--- /dev/null
+++ b/hostsidetests/backup/src/android/cts/backup/OtherSoundsSettingsHostSideTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.cts.backup;
+
+import static org.junit.Assert.assertNull;
+
+import android.platform.test.annotations.AppModeFull;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for checking other sounds settings value backup and restore works correctly.
+ * The tests use "bmgr backupnow com.android.providers.settings" for backup
+ *
+ * Invokes device side tests provided by
+ * {@link android.cts.backup.othersoundssettingsapp.OtherSoundsSettingsTest}.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+@AppModeFull
+public class OtherSoundsSettingsHostSideTest extends BaseBackupHostSideTest {
+
+    /** The name of the APK that a other sounds settings test will be run for */
+    private static final String APP_APK = "CtsBackupOtherSoundsSettingsApp.apk";
+
+    /** The name of the package of the app under test */
+    private static final String APP_PACKAGE = "android.cts.backup.othersoundssettingsapp";
+
+    /** The name of the device side test class */
+    private static final String TEST_CLASS = APP_PACKAGE + ".OtherSoundsSettingsTest";
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        if (!mIsBackupSupported) {
+            return;
+        }
+
+        installPackage(APP_APK);
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+
+        if (!mIsBackupSupported) {
+            return;
+        }
+
+        // Clear backup data and uninstall the package (in that order!)
+        clearBackupDataInLocalTransport(APP_PACKAGE);
+        assertNull(uninstallPackage(APP_PACKAGE));
+    }
+
+    /**
+     * Test backup and restore of Dial pad tones
+     */
+    @Test
+    public void testOtherSoundsSettings_dialPadTones() throws Exception {
+        if (!mIsBackupSupported) {
+            LogUtil.CLog.i("android.software.backup feature is not supported on this device");
+            return;
+        }
+
+        checkDeviceTest("testOtherSoundsSettings_dialPadTones");
+    }
+
+    /**
+     * Test backup and restore of Touch sounds
+     */
+    @Test
+    public void testOtherSoundsSettings_touchSounds() throws Exception {
+        if (!mIsBackupSupported) {
+            LogUtil.CLog.i("android.software.backup feature is not supported on this device");
+            return;
+        }
+
+        checkDeviceTest("testOtherSoundsSettings_touchSounds");
+    }
+
+    /**
+     * Test backup and restore of Touch vibration
+     */
+    @Test
+    public void testOtherSoundsSettings_touchVibration() throws Exception {
+        if (!mIsBackupSupported) {
+            LogUtil.CLog.i("android.software.backup feature is not supported on this device");
+            return;
+        }
+
+        checkDeviceTest("testOtherSoundsSettings_touchVibration");
+    }
+
+    private void checkDeviceTest(String methodName) throws DeviceNotAvailableException {
+        checkDeviceTest(APP_PACKAGE, TEST_CLASS, methodName);
+    }
+}
diff --git a/hostsidetests/backup/src/android/cts/backup/RestoreAnyVersionHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/RestoreAnyVersionHostSideTest.java
index a62eab8..3dca567 100644
--- a/hostsidetests/backup/src/android/cts/backup/RestoreAnyVersionHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/RestoreAnyVersionHostSideTest.java
@@ -18,6 +18,8 @@
 
 import static org.junit.Assert.assertNull;
 
+import android.platform.test.annotations.AppModeFull;
+
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.targetprep.TargetSetupError;
@@ -34,6 +36,7 @@
  * android.cts.backup.restoreanyversionapp.RestoreAnyVersionTest.
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
+@AppModeFull
 public class RestoreAnyVersionHostSideTest extends BaseBackupHostSideTest {
 
     /** The name of the package of the app under test */
@@ -85,7 +88,7 @@
         saveSharedPreferenceValue();
         checkRestoreAnyVersionDeviceTest("checkSharedPrefIsNew");
 
-        backupNowAndAssertSuccess(RESTORE_ANY_VERSION_APP_PACKAGE);
+        getBackupUtils().backupNowAndAssertSuccess(RESTORE_ANY_VERSION_APP_PACKAGE);
 
         assertNull(uninstallPackage(RESTORE_ANY_VERSION_APP_PACKAGE));
 
@@ -111,7 +114,7 @@
         saveSharedPreferenceValue();
         checkRestoreAnyVersionDeviceTest("checkSharedPrefIsNew");
 
-        backupNowAndAssertSuccess(RESTORE_ANY_VERSION_APP_PACKAGE);
+        getBackupUtils().backupNowAndAssertSuccess(RESTORE_ANY_VERSION_APP_PACKAGE);
 
         assertNull(uninstallPackage(RESTORE_ANY_VERSION_APP_PACKAGE));
 
@@ -137,7 +140,7 @@
         saveSharedPreferenceValue();
         checkRestoreAnyVersionDeviceTest("checkSharedPrefIsOld");
 
-        backupNowAndAssertSuccess(RESTORE_ANY_VERSION_APP_PACKAGE);
+        getBackupUtils().backupNowAndAssertSuccess(RESTORE_ANY_VERSION_APP_PACKAGE);
 
         assertNull(uninstallPackage(RESTORE_ANY_VERSION_APP_PACKAGE));
 
diff --git a/hostsidetests/backup/src/android/cts/backup/SuccessNotificationHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/SuccessNotificationHostSideTest.java
index 414e3bc..c833b40 100644
--- a/hostsidetests/backup/src/android/cts/backup/SuccessNotificationHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/SuccessNotificationHostSideTest.java
@@ -18,6 +18,8 @@
 
 import static org.junit.Assert.assertNull;
 
+import android.platform.test.annotations.AppModeFull;
+
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -38,6 +40,7 @@
  * NB: The tests use "bmgr backupnow" for backup, which works on N+ devices.
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
+@AppModeFull
 public class SuccessNotificationHostSideTest extends BaseBackupHostSideTest {
 
     /** The name of the package that a key/value backup will be run for */
@@ -136,7 +139,7 @@
 
         checkDeviceTest(KEY_VALUE_BACKUP_APP_PACKAGE, KEY_VALUE_BACKUP_DEVICE_TEST_NAME,
                 "saveSharedPreferencesAndNotifyBackupManager");
-        backupNowAndAssertSuccess(KEY_VALUE_BACKUP_APP_PACKAGE);
+        getBackupUtils().backupNowAndAssertSuccess(KEY_VALUE_BACKUP_APP_PACKAGE);
 
         checkDeviceTest("verifyBackupSuccessNotificationReceivedForKeyValueApp");
     }
@@ -155,7 +158,7 @@
         }
 
         checkDeviceTest(FULL_BACKUP_APP_PACKAGE, FULL_BACKUP_DEVICE_TEST_CLASS_NAME, "createFiles");
-        backupNowAndAssertSuccess(FULL_BACKUP_APP_PACKAGE);
+        getBackupUtils().backupNowAndAssertSuccess(FULL_BACKUP_APP_PACKAGE);
 
         checkDeviceTest("verifyBackupSuccessNotificationReceivedForFullBackupApp");
     }
diff --git a/hostsidetests/backup/src/android/cts/backup/SyncAdapterSettingsHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/SyncAdapterSettingsHostSideTest.java
new file mode 100644
index 0000000..1d4030d
--- /dev/null
+++ b/hostsidetests/backup/src/android/cts/backup/SyncAdapterSettingsHostSideTest.java
@@ -0,0 +1,154 @@
+/*
+ * 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.cts.backup;
+
+import static org.junit.Assert.assertNull;
+
+import android.platform.test.annotations.AppModeFull;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for checking sync adapter settings value backup and restore works correctly.
+ * The app uses AccountSyncSettingsBackupHelper to do sync adapter settings backup of those values.
+ * The tests use "bmgr backupnow android" for backup
+ *
+ * Invokes device side tests provided by
+ * {@link android.cts.backup.syncadaptersettingsapp.SyncAdapterSettingsTest}.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+@AppModeFull
+public class SyncAdapterSettingsHostSideTest extends BaseBackupHostSideTest {
+
+    /** The name of the APK that a sync adapter settings will be run for */
+    private static final String APP_APK = "CtsBackupSyncAdapterSettingsApp.apk";
+
+    /** The name of the package of the app under test */
+    private static final String APP_PACKAGE = "android.cts.backup.syncadaptersettingsapp";
+
+    /** The name of the device side test class */
+    private static final String TEST_CLASS = APP_PACKAGE + ".SyncAdapterSettingsTest";
+
+    /** The name of the package for backup */
+    private static final String ANDROID_PACKAGE = "android";
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        if (!mIsBackupSupported) {
+            return;
+        }
+
+        installPackage(APP_APK);
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+
+        if (!mIsBackupSupported) {
+            return;
+        }
+
+        // Clear backup data and uninstall the package (in that order!)
+        // BackupManagerService won't let us wipe the data if package is uninstalled
+        clearBackupDataInLocalTransport(ANDROID_PACKAGE);
+        assertNull(uninstallPackage(APP_PACKAGE));
+    }
+
+    /**
+     * Test backup and restore of MasterSyncAutomatically=true.
+     */
+    @Test
+    public void testMasterSyncAutomatically_whenOn_isRestored() throws Exception {
+        if (!mIsBackupSupported) {
+            CLog.i("android.software.backup feature is not supported on this device");
+            return;
+        }
+
+        checkDeviceTest("testMasterSyncAutomatically_whenOn_isRestored");
+    }
+
+    /**
+     * Test backup and restore of MasterSyncAutomatically=false.
+     */
+    @Test
+    public void testMasterSyncAutomatically_whenOff_isRestored() throws Exception {
+        if (!mIsBackupSupported) {
+            CLog.i("android.software.backup feature is not supported on this device");
+            return;
+        }
+
+        checkDeviceTest("testMasterSyncAutomatically_whenOff_isRestored");
+    }
+
+    /**
+     * Test that if syncEnabled=true and isSyncable=1 in restore data, syncEnabled will be true
+     * and isSyncable will be left as the current value in the device.
+     */
+    @Test
+    public void testIsSyncableChanged_ifTurnOnSyncEnabled() throws Exception {
+        if (!mIsBackupSupported) {
+            CLog.i("android.software.backup feature is not supported on this device");
+            return;
+        }
+
+        checkDeviceTest("testIsSyncableChanged_ifTurnOnSyncEnabled");
+    }
+
+    /**
+     * Test that if syncEnabled=false and isSyncable=0 in restore data, syncEnabled will be false
+     * and isSyncable will be set to 0.
+     */
+    @Test
+    public void testIsSyncableIsZero_ifTurnOffSyncEnabled() throws Exception {
+        if (!mIsBackupSupported) {
+            CLog.i("android.software.backup feature is not supported on this device");
+            return;
+        }
+
+        checkDeviceTest("testIsSyncableIsZero_ifTurnOffSyncEnabled");
+    }
+
+    /**
+     * Test that if syncEnabled=false and isSyncable=1 in restore data, syncEnabled will be false
+     * and isSyncable will be set to 1.
+     */
+    @Test
+    public void testIsSyncableIsOne_ifTurnOffSyncEnabled() throws Exception {
+        if (!mIsBackupSupported) {
+            CLog.i("android.software.backup feature is not supported on this device");
+            return;
+        }
+
+        checkDeviceTest("testIsSyncableIsOne_ifTurnOffSyncEnabled");
+    }
+
+    private void checkDeviceTest(String methodName) throws DeviceNotAvailableException {
+        checkDeviceTest(APP_PACKAGE, TEST_CLASS, methodName);
+    }
+}
diff --git a/hostsidetests/compilation/Android.mk b/hostsidetests/compilation/Android.mk
index 09c8507..7684826 100644
--- a/hostsidetests/compilation/Android.mk
+++ b/hostsidetests/compilation/Android.mk
@@ -27,6 +27,8 @@
 
 LOCAL_MODULE := CtsCompilationTestCases
 
+LOCAL_MIN_SDK_VERSION := 23
+
 LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed compatibility-host-util
 
 LOCAL_STATIC_JAVA_LIBRARIES := guavalib
diff --git a/hostsidetests/content/test-apps/CtsSyncInvalidAccountAuthorityTestCases/Android.mk b/hostsidetests/content/test-apps/CtsSyncInvalidAccountAuthorityTestCases/Android.mk
index 3069bac..8ac2416 100644
--- a/hostsidetests/content/test-apps/CtsSyncInvalidAccountAuthorityTestCases/Android.mk
+++ b/hostsidetests/content/test-apps/CtsSyncInvalidAccountAuthorityTestCases/Android.mk
@@ -20,12 +20,13 @@
 
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-annotations android-support-test ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsSyncInvalidAccountAuthorityTestCases
-LOCAL_PRIVATE_PLATFORM_APIS := true
+
+LOCAL_SDK_VERSION := current
 
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
diff --git a/hostsidetests/content/test-apps/CtsSyncInvalidAccountAuthorityTestCases/src/android/content/sync/cts/StubProvider.java b/hostsidetests/content/test-apps/CtsSyncInvalidAccountAuthorityTestCases/src/android/content/sync/cts/StubProvider.java
index f082cc8..c8343d4 100644
--- a/hostsidetests/content/test-apps/CtsSyncInvalidAccountAuthorityTestCases/src/android/content/sync/cts/StubProvider.java
+++ b/hostsidetests/content/test-apps/CtsSyncInvalidAccountAuthorityTestCases/src/android/content/sync/cts/StubProvider.java
@@ -16,11 +16,11 @@
 
 package android.content.sync.cts;
 
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.content.ContentProvider;
 import android.content.ContentValues;
 import android.database.Cursor;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 import android.net.Uri;
 
 public class StubProvider extends ContentProvider {
diff --git a/hostsidetests/devicepolicy/Android.mk b/hostsidetests/devicepolicy/Android.mk
index f9a861c..6ff06d9 100644
--- a/hostsidetests/devicepolicy/Android.mk
+++ b/hostsidetests/devicepolicy/Android.mk
@@ -22,7 +22,17 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := tools-common-prebuilt cts-tradefed tradefed compatibility-host-util
+LOCAL_JAVA_LIBRARIES := \
+    tools-common-prebuilt \
+    cts-tradefed \
+    tradefed \
+    compatibility-host-util
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    truth-host-prebuilt
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    truth-host-prebuilt
 
 LOCAL_CTS_TEST_PACKAGE := android.adminhostside
 
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/Auth/Android.mk b/hostsidetests/devicepolicy/app/AccountCheck/Auth/Android.mk
index 5746089..22f3436 100644
--- a/hostsidetests/devicepolicy/app/AccountCheck/Auth/Android.mk
+++ b/hostsidetests/devicepolicy/app/AccountCheck/Auth/Android.mk
@@ -36,5 +36,6 @@
 LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_SDK_VERSION := test_current
+LOCAL_MIN_SDK_VERSION := 25
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/Tester/Android.mk b/hostsidetests/devicepolicy/app/AccountCheck/Tester/Android.mk
index b3adc70..7706e73 100644
--- a/hostsidetests/devicepolicy/app/AccountCheck/Tester/Android.mk
+++ b/hostsidetests/devicepolicy/app/AccountCheck/Tester/Android.mk
@@ -30,5 +30,6 @@
 LOCAL_STATIC_JAVA_LIBRARIES := androidx.legacy_legacy-support-v4 ctstestrunner ub-uiautomator android-support-test
 
 LOCAL_SDK_VERSION := test_current
+LOCAL_MIN_SDK_VERSION := 25
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/AppRestrictionsTargetApp/Android.mk b/hostsidetests/devicepolicy/app/AppRestrictionsTargetApp/Android.mk
index 78795cf..3aed12d 100644
--- a/hostsidetests/devicepolicy/app/AppRestrictionsTargetApp/Android.mk
+++ b/hostsidetests/devicepolicy/app/AppRestrictionsTargetApp/Android.mk
@@ -25,6 +25,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 24
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := arcts cts vts general-tests
diff --git a/hostsidetests/devicepolicy/app/CertInstaller/Android.mk b/hostsidetests/devicepolicy/app/CertInstaller/Android.mk
index 86440fc..6dc19f8 100644
--- a/hostsidetests/devicepolicy/app/CertInstaller/Android.mk
+++ b/hostsidetests/devicepolicy/app/CertInstaller/Android.mk
@@ -24,9 +24,18 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner.stubs
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs \
+    android.test.base.stubs \
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 22
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    compatibility-device-util \
+    ctstestrunner \
+    truth-prebuilt \
+    testng
+
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/hostsidetests/devicepolicy/app/CertInstaller/AndroidManifest.xml b/hostsidetests/devicepolicy/app/CertInstaller/AndroidManifest.xml
index 6bf1451..b114f96 100644
--- a/hostsidetests/devicepolicy/app/CertInstaller/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/CertInstaller/AndroidManifest.xml
@@ -20,6 +20,8 @@
     <uses-sdk android:minSdkVersion="22"/>
 
     <application>
+        <uses-library android:name="android.test.runner" />
+
         <receiver android:name=".CertInstallerReceiver">
             <intent-filter>
                 <action android:name="com.android.cts.certinstaller.install_cert" />
@@ -31,4 +33,13 @@
         </receiver>
     </application>
 
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:label="Delegated Cert Installer CTS test"
+        android:targetPackage="com.android.cts.certinstaller">
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener"/>
+    </instrumentation>
+
 </manifest>
diff --git a/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/DirectDelegatedCertInstallerTest.java b/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/DirectDelegatedCertInstallerTest.java
new file mode 100644
index 0000000..87d201a
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/DirectDelegatedCertInstallerTest.java
@@ -0,0 +1,206 @@
+/*
+ * 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.certinstaller;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.common.truth.Truth.assertThat;
+import static org.testng.Assert.assertThrows;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.security.KeyChain;
+import android.security.KeyChainException;
+import android.test.InstrumentationTestCase;
+import android.util.Base64;
+import android.util.Base64InputStream;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.List;
+
+/*
+ * Tests the delegated certificate installer functionality.
+ *
+ * This class is configured as DelegatedCertInstaller by the DelegatedCertInstallerHelper and is
+ * invoked directly from the host class,
+ * DeviceAndProfileOwnerTest#testDelegatedCertInstallerDirectly.
+ *
+ * TODO: this class is missing more functionality of the DelegatedCertInstaller tests.
+ * When this class is done then the DelegatedCertInstallerTest can be deleted.
+ */
+public class DirectDelegatedCertInstallerTest extends InstrumentationTestCase {
+    // Content from cacert.pem
+    private static final String TEST_CA =
+            "-----BEGIN CERTIFICATE-----\n" +
+                    "MIIDXTCCAkWgAwIBAgIJAK9Tl/F9V8kSMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\n" +
+                    "BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\n" +
+                    "aWRnaXRzIFB0eSBMdGQwHhcNMTUwMzA2MTczMjExWhcNMjUwMzAzMTczMjExWjBF\n" +
+                    "MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50\n" +
+                    "ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n" +
+                    "CgKCAQEAvItOutsE75WBTgTyNAHt4JXQ3JoseaGqcC3WQij6vhrleWi5KJ0jh1/M\n" +
+                    "Rpry7Fajtwwb4t8VZa0NuM2h2YALv52w1xivql88zce/HU1y7XzbXhxis9o6SCI+\n" +
+                    "oVQSbPeXRgBPppFzBEh3ZqYTVhAqw451XhwdA4Aqs3wts7ddjwlUzyMdU44osCUg\n" +
+                    "kVg7lfPf9sTm5IoHVcfLSCWH5n6Nr9sH3o2ksyTwxuOAvsN11F/a0mmUoPciYPp+\n" +
+                    "q7DzQzdi7akRG601DZ4YVOwo6UITGvDyuAAdxl5isovUXqe6Jmz2/myTSpAKxGFs\n" +
+                    "jk9oRoG6WXWB1kni490GIPjJ1OceyQIDAQABo1AwTjAdBgNVHQ4EFgQUH1QIlPKL\n" +
+                    "p2OQ/AoLOjKvBW4zK3AwHwYDVR0jBBgwFoAUH1QIlPKLp2OQ/AoLOjKvBW4zK3Aw\n" +
+                    "DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAcMi4voMMJHeQLjtq8Oky\n" +
+                    "Azpyk8moDwgCd4llcGj7izOkIIFqq/lyqKdtykVKUWz2bSHO5cLrtaOCiBWVlaCV\n" +
+                    "DYAnnVLM8aqaA6hJDIfaGs4zmwz0dY8hVMFCuCBiLWuPfiYtbEmjHGSmpQTG6Qxn\n" +
+                    "ZJlaK5CZyt5pgh5EdNdvQmDEbKGmu0wpCq9qjZImwdyAul1t/B0DrsWApZMgZpeI\n" +
+                    "d2od0VBrCICB1K4p+C51D93xyQiva7xQcCne+TAnGNy9+gjQ/MyR8MRpwRLv5ikD\n" +
+                    "u0anJCN8pXo6IMglfMAsoton1J6o5/ae5uhC6caQU8bNUsCK570gpNfjkzo6rbP0\n" +
+                    "wQ==\n" +
+                    "-----END CERTIFICATE-----";
+    // Content from userkey.pem without the private key header and footer.
+    private static final String TEST_KEY =
+            "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALCYprGsTU+5L3KM\n" +
+                    "fhkm0gXM2xjGUH+543YLiMPGVr3eVS7biue1/tQlL+fJsw3rqsPKJe71RbVWlpqU\n" +
+                    "mhegxG4s3IvGYVB0KZoRIjDKmnnvlx6nngL2ZJ8O27U42pHsw4z4MKlcQlWkjL3T\n" +
+                    "9sV6zW2Wzri+f5mvzKjhnArbLktHAgMBAAECgYBlfVVPhtZnmuXJzzQpAEZzTugb\n" +
+                    "tN1OimZO0RIocTQoqj4KT+HkiJOLGFQPwbtFpMre+q4SRqNpM/oZnI1yRtKcCmIc\n" +
+                    "mZgkwJ2k6pdSxqO0ofxFFTdT9czJ3rCnqBHy1g6BqUQFXT4olcygkxUpKYUwzlz1\n" +
+                    "oAl487CoPxyr4sVEAQJBANwiUOHcdGd2RoRILDzw5WOXWBoWPOKzX/K9wt0yL+mO\n" +
+                    "wlFNFSymqo9eLheHcEq/VD9qK9rT700dCewJfWj6+bECQQDNXmWNYIxGii5NJilT\n" +
+                    "OBOHiMD/F0NE178j+/kmacbhDJwpkbLYXaP8rW4+Iswrm4ORJ59lvjNuXaZ28+sx\n" +
+                    "fFp3AkA6Z7Bl/IO135+eATgbgx6ZadIqObQ1wbm3Qbmtzl7/7KyJvZXcnuup1icM\n" +
+                    "fxa//jtwB89S4+Ad6ZJ0WaA4dj5BAkEAuG7V9KmIULE388EZy8rIfyepa22Q0/qN\n" +
+                    "hdt8XasRGHsio5Jdc0JlSz7ViqflhCQde/aBh/XQaoVgQeO8jKyI8QJBAJHekZDj\n" +
+                    "WA0w1RsBVVReN1dVXgjm1CykeAT8Qx8TUmBUfiDX6w6+eGQjKtS7f4KC2IdRTV6+\n" +
+                    "bDzDoHBChHNC9ms=\n";
+
+    // Content from usercert.pem without the header and footer.
+    private static final String TEST_CERT =
+            "MIIDEjCCAfqgAwIBAgIBATANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJBVTET\n" +
+                    "MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ\n" +
+                    "dHkgTHRkMB4XDTE1MDUwMTE2NTQwNVoXDTI1MDQyODE2NTQwNVowWzELMAkGA1UE\n" +
+                    "BhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdp\n" +
+                    "ZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLY2xpZW50IGNlcnQwgZ8wDQYJKoZIhvcN\n" +
+                    "AQEBBQADgY0AMIGJAoGBALCYprGsTU+5L3KMfhkm0gXM2xjGUH+543YLiMPGVr3e\n" +
+                    "VS7biue1/tQlL+fJsw3rqsPKJe71RbVWlpqUmhegxG4s3IvGYVB0KZoRIjDKmnnv\n" +
+                    "lx6nngL2ZJ8O27U42pHsw4z4MKlcQlWkjL3T9sV6zW2Wzri+f5mvzKjhnArbLktH\n" +
+                    "AgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2Vu\n" +
+                    "ZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBQ8GL+jKSarvTn9fVNA2AzjY7qq\n" +
+                    "gjAfBgNVHSMEGDAWgBRzBBA5sNWyT/fK8GrhN3tOqO5tgjANBgkqhkiG9w0BAQsF\n" +
+                    "AAOCAQEAgwQEd2bktIDZZi/UOwU1jJUgGq7NiuBDPHcqgzjxhGFLQ8SQAAP3v3PR\n" +
+                    "mLzcfxsxnzGynqN5iHQT4rYXxxaqrp1iIdj9xl9Wl5FxjZgXITxhlRscOd/UOBvG\n" +
+                    "oMrazVczjjdoRIFFnjtU3Jf0Mich68HD1Z0S3o7X6sDYh6FTVR5KbLcxbk6RcoG4\n" +
+                    "VCI5boR5LUXgb5Ed5UxczxvN12S71fyxHYVpuuI0z0HTIbAxKeRw43I6HWOmR1/0\n" +
+                    "G6byGCNL/1Fz7Y+264fGqABSNTKdZwIU2K4ANEH7F+9scnhoO6OBp+gjBe5O+7jb\n" +
+                    "wZmUCAoTka4hmoaOCj7cqt/IkmxozQ==\n";
+
+    private DevicePolicyManager mDpm;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mDpm = getContext().getSystemService(DevicePolicyManager.class);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        mDpm.uninstallCaCert(null, TEST_CA.getBytes());
+        super.tearDown();
+    }
+
+    public void testCaCertsOperations() throws GeneralSecurityException, IOException {
+        final byte[] cert = TEST_CA.getBytes();
+        final Certificate caCert = CertificateFactory.getInstance("X.509")
+                .generateCertificate(new ByteArrayInputStream(cert));
+
+        // Exercise installCaCert()
+        KeyStore keyStore = KeyStore.getInstance("AndroidCAStore");
+        keyStore.load(null, null);
+        assertWithMessage("CA certificate must not be installed in KeyStore at the"
+                + " beginning of the test").that(keyStore.getCertificateAlias(caCert)).isNull();
+        assertWithMessage("CA certificate must not be installed in the DPM at the"
+                + " beginning of the test").that(mDpm.hasCaCertInstalled(null, cert)).isFalse();
+
+
+        assertWithMessage("Expecting CA certificate installation to succeed").that(
+                mDpm.installCaCert(null, cert)).isTrue();
+        assertWithMessage("Expecting CA cert to be installed").that(
+                mDpm.hasCaCertInstalled(null, cert)).isTrue();
+
+        // Exercise getInstalledCaCerts()
+        assertWithMessage("Expecting CA cert to be in the list of installed CA certs").that(
+                containsCertificate(mDpm.getInstalledCaCerts(null), cert)).isTrue();
+
+        // Verify that the CA cert was marked as installed by the Device Owner / Profile Owner.
+        assertWithMessage("CA cert should have a KeyStore alias").that(
+                keyStore.getCertificateAlias(caCert)).isNotNull();
+
+        mDpm.uninstallCaCert(null, cert);
+        assertWithMessage("Expecting CA cert to no longer be installed").that(
+                mDpm.hasCaCertInstalled(null, cert)).isFalse();
+    }
+
+    public void testInstallKeyPair()
+            throws GeneralSecurityException, KeyChainException, InterruptedException {
+        final String alias = "delegated-cert-installer-test-key";
+
+        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(
+                Base64.decode(TEST_KEY, Base64.DEFAULT));
+        PrivateKey privatekey = KeyFactory.getInstance("RSA").generatePrivate(keySpec);
+
+        Certificate certificate = CertificateFactory.getInstance("X.509")
+                .generateCertificate(
+                        new Base64InputStream(new ByteArrayInputStream(TEST_CERT.getBytes()),
+                                Base64.DEFAULT));
+        assertThat(mDpm.installKeyPair(null, privatekey, new Certificate[]{certificate}, alias,
+                true)).isTrue();
+
+        // Test that the installed private key can be obtained.
+        PrivateKey obtainedKey = KeyChain.getPrivateKey(getContext(), alias);
+        assertThat(obtainedKey).isNotNull();
+        assertThat(obtainedKey.getAlgorithm()).isEqualTo("RSA");
+
+        // Test cleaning up the key.
+        assertThat(mDpm.removeKeyPair(null, alias)).isTrue();
+        assertThrows(
+                KeyChainException.class, () -> KeyChain.getPrivateKey(getContext(), alias));
+    }
+
+    private static boolean containsCertificate(List<byte[]> certificates, byte[] toMatch)
+            throws CertificateException {
+        Certificate certificateToMatch = readCertificate(toMatch);
+        for (byte[] certBuffer : certificates) {
+            Certificate cert = readCertificate(certBuffer);
+            if (certificateToMatch.equals(cert)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static Certificate readCertificate(byte[] certBuffer) throws CertificateException {
+        final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+        return certFactory.generateCertificate(new ByteArrayInputStream(certBuffer));
+    }
+
+    private Context getContext() {
+        return getInstrumentation().getContext();
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/AdminReceiver.java b/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/AdminReceiver.java
index bf18ebb..ef07632 100644
--- a/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/AdminReceiver.java
+++ b/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/AdminReceiver.java
@@ -37,7 +37,7 @@
         super.onProfileProvisioningComplete(context, intent);
         Log.i(TAG, "onProfileProvisioningComplete");
         // Enabled profile
-        getManager(context).setProfileEnabled(getComponentName(context));
-        getManager(context).setProfileName(getComponentName(context), "Corp owned Managed Profile");
+        getManager(context).setProfileEnabled(getWho(context));
+        getManager(context).setProfileName(getWho(context), "Corp owned Managed Profile");
     }
 }
diff --git a/hostsidetests/devicepolicy/app/CrossProfileAppsTest/Android.mk b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/Android.mk
index 8ff609d..111b354 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileAppsTest/Android.mk
+++ b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/Android.mk
@@ -34,6 +34,7 @@
 	ub-uiautomator
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 21
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/hostsidetests/devicepolicy/app/CustomizationApp/Android.mk b/hostsidetests/devicepolicy/app/CustomizationApp/Android.mk
index 81d98f1..2cea654 100644
--- a/hostsidetests/devicepolicy/app/CustomizationApp/Android.mk
+++ b/hostsidetests/devicepolicy/app/CustomizationApp/Android.mk
@@ -33,5 +33,6 @@
 LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 24
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/DelegateApp/Android.mk b/hostsidetests/devicepolicy/app/DelegateApp/Android.mk
index 1d0be1d..9e85020 100644
--- a/hostsidetests/devicepolicy/app/DelegateApp/Android.mk
+++ b/hostsidetests/devicepolicy/app/DelegateApp/Android.mk
@@ -29,7 +29,8 @@
 LOCAL_STATIC_JAVA_LIBRARIES = \
     androidx.legacy_legacy-support-v4 \
     ctstestrunner \
-    android-support-test
+    android-support-test \
+    truth-prebuilt
 
 LOCAL_SDK_VERSION := current
 
diff --git a/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/AppRestrictionsIsCallerDelegateHelper.java b/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/AppRestrictionsIsCallerDelegateHelper.java
new file mode 100644
index 0000000..ed88773
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/AppRestrictionsIsCallerDelegateHelper.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.delegate;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.admin.DevicePolicyManager;
+import android.test.InstrumentationTestCase;
+
+/**
+ *  A helper for testing the {@link
+ *  DevicePolicyManager#isCallerApplicationRestrictionsManagingPackage()} method.
+ *  <p>The method names start with "test" to be recognized by {@link InstrumentationTestCase}.
+ */
+public class AppRestrictionsIsCallerDelegateHelper extends InstrumentationTestCase {
+
+    private DevicePolicyManager mDevicePolicyManager;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mDevicePolicyManager =
+            getInstrumentation().getContext().getSystemService(DevicePolicyManager.class);
+    }
+
+    public void testAssertCallerIsApplicationRestrictionsManagingPackage() {
+        assertThat(mDevicePolicyManager.isCallerApplicationRestrictionsManagingPackage()).isTrue();
+    }
+
+    public void testAssertCallerIsNotApplicationRestrictionsManagingPackage() {
+        assertThat(mDevicePolicyManager.isCallerApplicationRestrictionsManagingPackage()).isFalse();
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/api23/Android.mk b/hostsidetests/devicepolicy/app/DeviceAdmin/api23/Android.mk
index 5cf7f05..2d3a367 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdmin/api23/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/api23/Android.mk
@@ -33,6 +33,7 @@
 LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 23
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/api24/Android.mk b/hostsidetests/devicepolicy/app/DeviceAdmin/api24/Android.mk
index a7c7470..c19d0c0 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdmin/api24/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/api24/Android.mk
@@ -33,6 +33,7 @@
 LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 23
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := arcts cts vts general-tests
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/Android.mk b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/Android.mk
index 31e193d..3c2b52b 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/Android.mk
@@ -16,5 +16,7 @@
 
 include $(CLEAR_VARS)
 
+LOCAL_STATIC_JAVA_LIBRARIES := truth-prebuilt
+
 # Build the test APKs using their own makefiles
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api23/Android.mk b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api23/Android.mk
index 1599d60..7956f04 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api23/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api23/Android.mk
@@ -35,7 +35,8 @@
     compatibility-device-util \
     ctstestrunner \
     ub-uiautomator \
-    cts-security-test-support-library
+    cts-security-test-support-library \
+    truth-prebuilt
 
 LOCAL_STATIC_ANDROID_LIBRARIES := \
     androidx.legacy_legacy-support-v4
@@ -44,6 +45,8 @@
 
 LOCAL_ASSET_DIR := $(LOCAL_PATH)/../assets
 
+LOCAL_MIN_SDK_VERSION := 23
+
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api25/Android.mk b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api25/Android.mk
index 481f821..dc7bb51 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api25/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api25/Android.mk
@@ -35,7 +35,8 @@
     compatibility-device-util \
     ctstestrunner \
     ub-uiautomator \
-    cts-security-test-support-library
+    cts-security-test-support-library \
+    truth-prebuilt
 
 LOCAL_STATIC_ANDROID_LIBRARIES := \
     androidx.legacy_legacy-support-v4
@@ -44,6 +45,8 @@
 
 LOCAL_ASSET_DIR := $(LOCAL_PATH)/../assets
 
+LOCAL_MIN_SDK_VERSION := 23
+
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/Android.mk b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/Android.mk
index 8c6d3fd..5f7ccfb 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/Android.mk
@@ -35,7 +35,8 @@
     compatibility-device-util \
     ctstestrunner \
     ub-uiautomator \
-    cts-security-test-support-library
+    cts-security-test-support-library \
+    truth-prebuilt
 
 LOCAL_STATIC_ANDROID_LIBRARIES := \
     androidx.legacy_legacy-support-v4
@@ -44,6 +45,8 @@
 
 LOCAL_ASSET_DIR := $(LOCAL_PATH)/../assets
 
+LOCAL_MIN_SDK_VERSION := 23
+
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := arcts cts vts general-tests
 
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/res/xml/device_admin.xml b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/res/xml/device_admin.xml
index c43856e..c55ba0b 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/res/xml/device_admin.xml
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/res/xml/device_admin.xml
@@ -14,9 +14,12 @@
 -->
 <device-admin xmlns:android="http://schemas.android.com/apk/res/android" android:visible="false">
     <uses-policies>
+         <watch-login />
          <reset-password />
          <limit-password />
+         <expire-password />
          <disable-keyguard-features />
          <disable-camera />
+         <encrypted-storage />
     </uses-policies>
 </device-admin>
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AccessibilityServicesTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AccessibilityServicesTest.java
new file mode 100644
index 0000000..5647f78
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AccessibilityServicesTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.deviceandprofileowner;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ComponentName;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class AccessibilityServicesTest extends BaseDeviceAdminTest {
+    private final ComponentName badAdmin = new ComponentName("com.foo.bar", ".BazQuxReceiver");
+
+    public void testPermittedAccessibilityServices() {
+        // All accessibility services are allowed.
+        mDevicePolicyManager.setPermittedAccessibilityServices(ADMIN_RECEIVER_COMPONENT, null);
+        assertThat(mDevicePolicyManager.
+                getPermittedAccessibilityServices(ADMIN_RECEIVER_COMPONENT))
+                .isNull();
+
+        // Only system accessibility services are allowed.
+        mDevicePolicyManager.setPermittedAccessibilityServices(ADMIN_RECEIVER_COMPONENT,
+                new ArrayList<>());
+        assertThat(mDevicePolicyManager.
+                getPermittedAccessibilityServices(ADMIN_RECEIVER_COMPONENT))
+                .isEmpty();
+
+        // Some random services.
+        final List<String> packages = Arrays.asList("com.google.pkg.one", "com.google.pkg.two");
+        mDevicePolicyManager.setPermittedAccessibilityServices(ADMIN_RECEIVER_COMPONENT, packages);
+        assertThat(mDevicePolicyManager.
+                getPermittedAccessibilityServices(ADMIN_RECEIVER_COMPONENT))
+                .containsExactlyElementsIn(packages);
+    }
+
+    public void testPermittedAccessibilityServicesThrowsIfWrongAdmin() {
+        try {
+            mDevicePolicyManager.setPermittedAccessibilityServices(badAdmin, null);
+            fail("setPermittedAccessibilityServices didn't throw when passed invalid admin");
+        } catch (SecurityException expected) {
+        }
+        try {
+            mDevicePolicyManager.getPermittedAccessibilityServices(badAdmin);
+            fail("getPermittedAccessibilityServices didn't throw when passed invalid admin");
+        } catch (SecurityException expected) {
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ApplicationRestrictionsIsCallerDelegateHelper.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ApplicationRestrictionsIsCallerDelegateHelper.java
new file mode 100644
index 0000000..ca28138
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ApplicationRestrictionsIsCallerDelegateHelper.java
@@ -0,0 +1,41 @@
+/*
+ * 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.deviceandprofileowner;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.test.InstrumentationTestCase;
+
+/**
+ * Helper to set app restriction managing package for
+ * {@link DevicePolicyManager#isCallerApplicationRestrictionsManagingPackage()}.
+ * <p>The method name starts with "test" to be recognized by {@link InstrumentationTestCase}.
+ */
+public class ApplicationRestrictionsIsCallerDelegateHelper extends BaseDeviceAdminTest {
+
+    private static final String APP_RESTRICTIONS_TARGET_PKG = "com.android.cts.delegate";
+
+    public void testSetApplicationRestrictionsManagingPackageToDelegate()
+            throws NameNotFoundException {
+        mDevicePolicyManager.setApplicationRestrictionsManagingPackage(
+            ADMIN_RECEIVER_COMPONENT, APP_RESTRICTIONS_TARGET_PKG);
+        assertThat(APP_RESTRICTIONS_TARGET_PKG)
+            .isEqualTo(mDevicePolicyManager.getApplicationRestrictionsManagingPackage(
+                ADMIN_RECEIVER_COMPONENT));
+    }
+}
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 a88a463..fe6e0b9 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
@@ -24,12 +24,14 @@
 import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Process;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.test.InstrumentationTestCase;
 import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.compatibility.common.util.SystemUtil;
+import java.util.concurrent.CountDownLatch;
 
 /**
  * Base class for profile and device based tests.
@@ -50,6 +52,14 @@
             }
             return uri.getQueryParameter("alias");
         }
+
+        @Override
+        public void onPasswordExpiring(Context context, Intent intent, UserHandle user) {
+            super.onPasswordExpiring(context, intent, user);
+            if (mOnPasswordExpiryTimeoutCalled != null) {
+                mOnPasswordExpiryTimeoutCalled.countDown();
+            }
+        }
     }
 
     public static final String PACKAGE_NAME = BasicAdminReceiver.class.getPackage().getName();
@@ -59,6 +69,7 @@
     protected DevicePolicyManager mDevicePolicyManager;
     protected UserManager mUserManager;
     protected Context mContext;
+    static CountDownLatch mOnPasswordExpiryTimeoutCalled;
 
     private final String mTag = getClass().getSimpleName();
 
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
new file mode 100644
index 0000000..756058c
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerHelper.java
@@ -0,0 +1,60 @@
+/*
+ * 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.deviceandprofileowner;
+
+import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL;
+
+import android.app.admin.DevicePolicyManager;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A helper class for setting the DelegatedCertInstaller to the desired package.
+ * Does not actually contain any tests.
+ */
+public class DelegatedCertInstallerHelper extends BaseDeviceAdminTest {
+    private static final String CERT_INSTALLER_PACKAGE = "com.android.cts.certinstaller";
+
+    private static final List<String> CERT_INSTALL_SCOPES = Arrays.asList(DELEGATION_CERT_INSTALL);
+
+    private DevicePolicyManager mDpm;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mDpm = mContext.getSystemService(DevicePolicyManager.class);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testManualSetCertInstallerDelegate() {
+        mDpm.setDelegatedScopes(ADMIN_RECEIVER_COMPONENT, CERT_INSTALLER_PACKAGE,
+                CERT_INSTALL_SCOPES);
+        assertTrue(mDpm.getDelegatePackages(ADMIN_RECEIVER_COMPONENT,
+                DELEGATION_CERT_INSTALL).contains(CERT_INSTALLER_PACKAGE));
+    }
+
+    public void testManualClearCertInstallerDelegate() {
+        mDpm.setDelegatedScopes(ADMIN_RECEIVER_COMPONENT, CERT_INSTALLER_PACKAGE, Arrays.asList());
+        assertFalse(mDpm.getDelegatePackages(ADMIN_RECEIVER_COMPONENT,
+                DELEGATION_CERT_INSTALL).contains(CERT_INSTALLER_PACKAGE));
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerTest.java
index 99b310e..0fc8f48 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerTest.java
@@ -16,6 +16,10 @@
 
 package com.android.cts.deviceandprofileowner;
 
+import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL;
+
+import static com.google.common.truth.Truth.assertThat;
+
 import android.app.KeyguardManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
@@ -25,7 +29,6 @@
 import android.content.IntentFilter;
 import android.os.Build;
 import android.os.Process;
-import android.os.UserHandle;
 import android.security.KeyChainException;
 import android.test.MoreAsserts;
 
@@ -35,8 +38,8 @@
 import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
+import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
@@ -69,6 +72,8 @@
     private static final ComponentName CERT_INSTALLER_COMPONENT = new ComponentName(
             CERT_INSTALLER_PACKAGE, "com.android.cts.certinstaller.CertInstallerReceiver");
 
+    private static final List<String> CERT_INSTALL_SCOPES = Arrays.asList(DELEGATION_CERT_INSTALL);
+
     /*
      * The CA and keypair below are generated with:
      *
@@ -275,6 +280,41 @@
         }
     }
 
+    public void testSettingDelegatedCertInstallerAPICompatibility_oldSetNewGet() {
+        // Set a delegated cert installer using the deprecated API and verify that the same
+        // package is considered as the delegated cert installer using the new API.
+        mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, CERT_INSTALLER_PACKAGE);
+        assertThat(mDpm.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT)).isEqualTo(
+                CERT_INSTALLER_PACKAGE);
+        assertThat(mDpm.getDelegatePackages(ADMIN_RECEIVER_COMPONENT,
+                DELEGATION_CERT_INSTALL)).containsExactly(CERT_INSTALLER_PACKAGE);
+
+        // Remove a delegate using the old API, make sure no delegates are found using
+        // the new API.
+        mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, null);
+        assertThat(mDpm.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT)).isNull();
+        assertThat(mDpm.getDelegatePackages(ADMIN_RECEIVER_COMPONENT,
+                DELEGATION_CERT_INSTALL)).isEmpty();
+    }
+
+    public void testSettingDelegatedCertInstallerAPICompatibility_newSetOldGet() {
+        // Set a delegate using the new API, verify that the deprecated API returns the same
+        // delegate.
+        mDpm.setDelegatedScopes(ADMIN_RECEIVER_COMPONENT, CERT_INSTALLER_PACKAGE,
+                CERT_INSTALL_SCOPES);
+        assertThat(mDpm.getDelegatePackages(ADMIN_RECEIVER_COMPONENT,
+                DELEGATION_CERT_INSTALL)).containsExactly(CERT_INSTALLER_PACKAGE);
+        assertThat(mDpm.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT)).isEqualTo(
+                CERT_INSTALLER_PACKAGE);
+
+        // Remove the delegate using the new API, verify that the deprecated API returns null
+        // as the current delegated cert installer.
+        mDpm.setDelegatedScopes(ADMIN_RECEIVER_COMPONENT, CERT_INSTALLER_PACKAGE, Arrays.asList());
+        assertThat(mDpm.getDelegatePackages(ADMIN_RECEIVER_COMPONENT,
+                DELEGATION_CERT_INSTALL)).isEmpty();
+        assertThat(mDpm.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT)).isNull();
+    }
+
     /**
      * installKeyPair() requires the system to have a lockscreen password, which should have been
      * set by the host side test.
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/GetCurrentFailedPasswordAttemptsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/GetCurrentFailedPasswordAttemptsTest.java
new file mode 100644
index 0000000..e0b6c21
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/GetCurrentFailedPasswordAttemptsTest.java
@@ -0,0 +1,32 @@
+/*
+ * 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.deviceandprofileowner;
+
+public class GetCurrentFailedPasswordAttemptsTest extends BaseDeviceAdminTest {
+
+    public void testNoFailedPasswordAttempts() {
+        assertEquals(0, mDevicePolicyManager.getCurrentFailedPasswordAttempts());
+    }
+
+    public void testOneFailedPasswordAttempt() {
+        assertEquals(1, mDevicePolicyManager.getCurrentFailedPasswordAttempts());
+    }
+
+    public void testTwoFailedPasswordAttempts() {
+        assertEquals(2, mDevicePolicyManager.getCurrentFailedPasswordAttempts());
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/GetPasswordExpirationTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/GetPasswordExpirationTest.java
new file mode 100644
index 0000000..3ce561a
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/GetPasswordExpirationTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.deviceandprofileowner;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.lang.Math.abs;
+
+public class GetPasswordExpirationTest extends BaseDeviceAdminTest {
+    final long TIMEOUT_RESET_TEST = 86400000L /* 1 day */;
+
+    public void testGetPasswordExpiration() throws Exception {
+        final long[] testTimeouts = new long[] {
+                86400000L /* 1 day */, 864000000L /* 10 days */, 8640000000L /* 100 days */};
+        for (long testTimeout : testTimeouts) {
+            mDevicePolicyManager.setPasswordExpirationTimeout(
+                    ADMIN_RECEIVER_COMPONENT, testTimeout);
+            checkPasswordExpiration("Password expiration time incorrect", testTimeout, 5000);
+        }
+        // Set password expiration timeout to 0 clears the expiration date.
+        mDevicePolicyManager.setPasswordExpirationTimeout(ADMIN_RECEIVER_COMPONENT, 0L);
+        assertThat(mDevicePolicyManager.getPasswordExpiration(ADMIN_RECEIVER_COMPONENT))
+                .isEqualTo(0L);
+    }
+
+    public void testGetPasswordExpirationUpdatedAfterPasswordReset_beforeReset() throws Exception {
+        mDevicePolicyManager.setPasswordExpirationTimeout(
+                ADMIN_RECEIVER_COMPONENT, TIMEOUT_RESET_TEST);
+        checkPasswordExpiration("Password expiration time incorrect", TIMEOUT_RESET_TEST, 5000);
+    }
+
+    public void testGetPasswordExpirationUpdatedAfterPasswordReset_afterReset() throws Exception {
+        checkPasswordExpiration("Password expiration time not refreshed correctly"
+                + " after reseting password", TIMEOUT_RESET_TEST, 10000);
+    }
+
+    private void checkPasswordExpiration(String error, long timeout, long tolerance) {
+        assertTrue(error, abs(System.currentTimeMillis() + timeout
+                - mDevicePolicyManager.getPasswordExpiration(
+                ADMIN_RECEIVER_COMPONENT)) <= tolerance);
+    }
+}
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/InputMethodsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/InputMethodsTest.java
new file mode 100644
index 0000000..ed5047c
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/InputMethodsTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.deviceandprofileowner;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ComponentName;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class InputMethodsTest extends BaseDeviceAdminTest {
+    private final ComponentName badAdmin = new ComponentName("com.foo.bar", ".BazQuxReceiver");
+
+    public void testPermittedInputMethods() {
+        // All input methods are allowed.
+        mDevicePolicyManager.setPermittedInputMethods(ADMIN_RECEIVER_COMPONENT, null);
+        assertThat(
+                mDevicePolicyManager.getPermittedInputMethods(ADMIN_RECEIVER_COMPONENT)).isNull();
+
+        // Only system input methods are allowed.
+        mDevicePolicyManager.setPermittedInputMethods(ADMIN_RECEIVER_COMPONENT, new ArrayList<>());
+        assertThat(
+                mDevicePolicyManager.getPermittedInputMethods(ADMIN_RECEIVER_COMPONENT)).isEmpty();
+
+        // Some random methods.
+        final List<String> packages = Arrays.asList("com.google.pkg.one", "com.google.pkg.two");
+        mDevicePolicyManager.setPermittedInputMethods(ADMIN_RECEIVER_COMPONENT, packages);
+        assertThat(
+                mDevicePolicyManager.getPermittedInputMethods(ADMIN_RECEIVER_COMPONENT))
+                .containsExactlyElementsIn(packages);
+    }
+
+    public void testPermittedInputMethodsThrowsIfWrongAdmin() {
+        try {
+            mDevicePolicyManager.setPermittedInputMethods(badAdmin, null);
+            fail("setPermittedInputMethods didn't throw when passed invalid admin");
+        } catch (SecurityException expected) {
+        }
+        try {
+            mDevicePolicyManager.getPermittedInputMethods(badAdmin);
+            fail("getPermittedInputMethods didn't throw when passed invalid admin");
+        } catch (SecurityException expected) {
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyManagementTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyManagementTest.java
index 214eebb..8c938b8 100755
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyManagementTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/KeyManagementTest.java
@@ -20,6 +20,8 @@
 import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID;
 import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL;
 import static android.keystore.cts.CertificateUtils.createCertificate;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -35,11 +37,10 @@
 import android.security.KeyChainException;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
+import android.security.keystore.StrongBoxUnavailableException;
 import android.support.test.uiautomator.UiDevice;
 import android.telephony.TelephonyManager;
-
 import com.android.compatibility.common.util.FakeKeys.FAKE_RSA_1;
-
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -61,12 +62,10 @@
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.PKCS8EncodedKeySpec;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
-
 import javax.security.auth.x500.X500Principal;
 
 public class KeyManagementTest extends BaseDeviceAdminTest {
@@ -112,22 +111,22 @@
 
     public void testCanInstallAndRemoveValidRsaKeypair() throws Exception {
         final String alias = "com.android.test.valid-rsa-key-1";
-        final PrivateKey privKey = getPrivateKey(FAKE_RSA_1.privateKey , "RSA");
+        final PrivateKey privKey = getPrivateKey(FAKE_RSA_1.privateKey, "RSA");
         final Certificate cert = getCertificate(FAKE_RSA_1.caCertificate);
 
         // Install keypair.
-        assertTrue(mDevicePolicyManager.installKeyPair(getWho(), privKey, cert, alias));
+        assertThat(mDevicePolicyManager.installKeyPair(getWho(), privKey, cert, alias)).isTrue();
         try {
             // Request and retrieve using the alias.
             assertGranted(alias, false);
-            assertEquals(alias, new KeyChainAliasFuture(alias).get());
+            assertThat(new KeyChainAliasFuture(alias).get()).isEqualTo(alias);
             assertGranted(alias, true);
 
             // Verify key is at least something like the one we put in.
-            assertEquals(KeyChain.getPrivateKey(mActivity, alias).getAlgorithm(), "RSA");
+            assertThat(KeyChain.getPrivateKey(mActivity, alias).getAlgorithm()).isEqualTo("RSA");
         } finally {
             // Delete regardless of whether the test succeeded.
-            assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias));
+            assertThat(mDevicePolicyManager.removeKeyPair(getWho(), alias)).isTrue();
         }
         // Verify alias is actually deleted.
         assertGranted(alias, false);
@@ -136,25 +135,29 @@
     public void testCanInstallWithAutomaticAccess() throws Exception {
         final String grant = "com.android.test.autogrant-key-1";
         final String withhold = "com.android.test.nongrant-key-1";
-        final PrivateKey privKey = getPrivateKey(FAKE_RSA_1.privateKey , "RSA");
+        final PrivateKey privKey = getPrivateKey(FAKE_RSA_1.privateKey, "RSA");
         final Certificate cert = getCertificate(FAKE_RSA_1.caCertificate);
 
         // Install keypairs.
-        assertTrue(mDevicePolicyManager.installKeyPair(getWho(), privKey, new Certificate[] {cert},
-                grant, true));
-        assertTrue(mDevicePolicyManager.installKeyPair(getWho(), privKey, new Certificate[] {cert},
-                withhold, false));
+        assertThat(
+                mDevicePolicyManager.installKeyPair(
+                        getWho(), privKey, new Certificate[] {cert}, grant, true))
+                .isTrue();
+        assertThat(
+                mDevicePolicyManager.installKeyPair(
+                        getWho(), privKey, new Certificate[] {cert}, withhold, false))
+                .isTrue();
         try {
             // Verify only the requested key was actually granted.
             assertGranted(grant, true);
             assertGranted(withhold, false);
 
             // Verify the granted key is actually obtainable in PrivateKey form.
-            assertEquals(KeyChain.getPrivateKey(mActivity, grant).getAlgorithm(), "RSA");
+            assertThat(KeyChain.getPrivateKey(mActivity, grant).getAlgorithm()).isEqualTo("RSA");
         } finally {
             // Delete both keypairs.
-            assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), grant));
-            assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), withhold));
+            assertThat(mDevicePolicyManager.removeKeyPair(getWho(), grant)).isTrue();
+            assertThat(mDevicePolicyManager.removeKeyPair(getWho(), withhold)).isTrue();
         }
         // Verify they're actually gone.
         assertGranted(grant, false);
@@ -165,7 +168,7 @@
         final Collection<Certificate> certs = loadCertificatesFromAsset(assetName);
         final ArrayList<Certificate> certChain = new ArrayList(certs);
         // Some sanity check on the cert chain
-        assertTrue(certs.size() > 1);
+        assertThat(certs.size()).isGreaterThan(1);
         for (int i = 1; i < certChain.size(); i++) {
             certChain.get(i - 1).verify(certChain.get(i).getPublicKey());
         }
@@ -180,20 +183,20 @@
         final String alias = "com.android.test.clientkeychain";
 
         // Install keypairs.
-        assertTrue(mDevicePolicyManager.installKeyPair(getWho(), privKey, certChain, alias, true));
+        assertThat(mDevicePolicyManager.installKeyPair(getWho(), privKey, certChain, alias, true))
+                .isTrue();
         try {
             // Verify only the requested key was actually granted.
             assertGranted(alias, true);
 
             // Verify the granted key is actually obtainable in PrivateKey form.
-            assertEquals(KeyChain.getPrivateKey(mActivity, alias).getAlgorithm(), "RSA");
+            assertThat(KeyChain.getPrivateKey(mActivity, alias).getAlgorithm()).isEqualTo("RSA");
 
             // Verify the certificate chain is correct
-            X509Certificate[] returnedCerts = KeyChain.getCertificateChain(mActivity, alias);
-            assertTrue(Arrays.equals(certChain, returnedCerts));
+            assertThat(KeyChain.getCertificateChain(mActivity, alias)).isEqualTo(certChain);
         } finally {
             // Delete both keypairs.
-            assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias));
+            assertThat(mDevicePolicyManager.removeKeyPair(getWho(), alias)).isTrue();
         }
         // Verify they're actually gone.
         assertGranted(alias, false);
@@ -201,28 +204,32 @@
 
     public void testGrantsDoNotPersistBetweenInstallations() throws Exception {
         final String alias = "com.android.test.persistent-key-1";
-        final PrivateKey privKey = getPrivateKey(FAKE_RSA_1.privateKey , "RSA");
+        final PrivateKey privKey = getPrivateKey(FAKE_RSA_1.privateKey, "RSA");
         final Certificate cert = getCertificate(FAKE_RSA_1.caCertificate);
 
         // Install keypair.
-        assertTrue(mDevicePolicyManager.installKeyPair(getWho(), privKey, new Certificate[] {cert},
-                alias, true));
+        assertThat(
+                mDevicePolicyManager.installKeyPair(
+                        getWho(), privKey, new Certificate[] {cert}, alias, true))
+                .isTrue();
         try {
             assertGranted(alias, true);
         } finally {
             // Delete and verify.
-            assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias));
+            assertThat(mDevicePolicyManager.removeKeyPair(getWho(), alias)).isTrue();
         }
         assertGranted(alias, false);
 
         // Install again.
-        assertTrue(mDevicePolicyManager.installKeyPair(getWho(), privKey, new Certificate[] {cert},
-                alias, false));
+        assertThat(
+                mDevicePolicyManager.installKeyPair(
+                        getWho(), privKey, new Certificate[] {cert}, alias, false))
+                .isTrue();
         try {
             assertGranted(alias, false);
         } finally {
             // Delete.
-            assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias));
+            assertThat(mDevicePolicyManager.removeKeyPair(getWho(), alias)).isTrue();
         }
     }
 
@@ -255,20 +262,22 @@
 
     public void testNotUserSelectableAliasCanBeChosenViaPolicy() throws Exception {
         final String alias = "com.android.test.not-selectable-key-1";
-        final PrivateKey privKey = getPrivateKey(FAKE_RSA_1.privateKey , "RSA");
+        final PrivateKey privKey = getPrivateKey(FAKE_RSA_1.privateKey, "RSA");
         final Certificate cert = getCertificate(FAKE_RSA_1.caCertificate);
 
         // Install keypair.
-        assertTrue(mDevicePolicyManager.installKeyPair(
-            getWho(), privKey, new Certificate[] {cert}, alias, 0));
+        assertThat(
+                mDevicePolicyManager.installKeyPair(
+                        getWho(), privKey, new Certificate[] {cert}, alias, 0))
+                .isTrue();
         try {
             // Request and retrieve using the alias.
             assertGranted(alias, false);
-            assertEquals(alias, new KeyChainAliasFuture(alias).get());
+            assertThat(new KeyChainAliasFuture(alias).get()).isEqualTo(alias);
             assertGranted(alias, true);
         } finally {
             // Delete regardless of whether the test succeeded.
-            assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias));
+            assertThat(mDevicePolicyManager.removeKeyPair(getWho(), alias)).isTrue();
         }
     }
 
@@ -286,7 +295,7 @@
         Signature verify = Signature.getInstance(algoIdentifier);
         verify.initVerify(publicKey);
         verify.update(data);
-        assertTrue(verify.verify(signature));
+        assertThat(verify.verify(signature)).isTrue();
     }
 
     void verifySignatureOverData(String algoIdentifier, KeyPair keyPair) throws Exception {
@@ -310,26 +319,24 @@
         final String alias = "com.android.test.generated-rsa-1";
         try {
             AttestedKeyPair generated = mDevicePolicyManager.generateKeyPair(
-                    getWho(), "RSA", buildRsaKeySpec(alias, false /* useStrongBox */),
-                    0 /* idAttestationFlags */);
-            assertNotNull(generated);
+                    getWho(), "RSA", buildRsaKeySpec(alias, false /* useStrongBox */), 0);
+            assertThat(generated).isNotNull();
             verifySignatureOverData("SHA256withRSA", generated.getKeyPair());
         } finally {
-            assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias));
+            assertThat(mDevicePolicyManager.removeKeyPair(getWho(), alias)).isTrue();
         }
     }
 
     public void testCanGenerateRSAKeyPairUsingStrongBox() throws Exception {
         final String alias = "com.android.test.generated-rsa-sb-1";
-        AttestedKeyPair generated = mDevicePolicyManager.generateKeyPair(
-                getWho(), "RSA", buildRsaKeySpec(alias, true /* useStrongBox */),
-                0 /* idAttestationFlags */);
-        if (generated == null) {
-            assertFalse(hasStrongBox());
-            return;
+        try {
+            AttestedKeyPair generated = mDevicePolicyManager.generateKeyPair(
+                    getWho(), "RSA", buildRsaKeySpec(alias, true /* useStrongBox */), 0);
+            verifySignatureOverData("SHA256withRSA", generated.getKeyPair());
+            assertThat(mDevicePolicyManager.removeKeyPair(getWho(), alias)).isTrue();
+        } catch (StrongBoxUnavailableException expected) {
+            assertThat(hasStrongBox()).isFalse();
         }
-        verifySignatureOverData("SHA256withRSA", generated.getKeyPair());
-        assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias));
     }
 
     private KeyGenParameterSpec buildEcKeySpec(String alias, boolean useStrongBox) {
@@ -345,25 +352,24 @@
         final String alias = "com.android.test.generated-ec-1";
         try {
             AttestedKeyPair generated = mDevicePolicyManager.generateKeyPair(
-                    getWho(), "EC", buildEcKeySpec(alias, false /* useStrongBox */),
-                    0 /* idAttestationFlags */);
-            assertNotNull(generated);
+                    getWho(), "EC", buildEcKeySpec(alias, false /* useStrongBox */), 0);
+            assertThat(generated).isNotNull();
             verifySignatureOverData("SHA256withECDSA", generated.getKeyPair());
         } finally {
-            assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias));
+            assertThat(mDevicePolicyManager.removeKeyPair(getWho(), alias)).isTrue();
         }
     }
 
     public void testCanGenerateECKeyPairUsingStrongBox() throws Exception {
         final String alias = "com.android.test.generated-ec-sb-1";
-        AttestedKeyPair generated = mDevicePolicyManager.generateKeyPair(
-                getWho(), "EC", buildEcKeySpec(alias, true /* useStrongBox */), 0);
-        if (generated == null) {
-            assertFalse(hasStrongBox());
-            return;
+        try {
+            AttestedKeyPair generated = mDevicePolicyManager.generateKeyPair(
+                    getWho(), "EC", buildEcKeySpec(alias, true /* useStrongBox */), 0);
+            verifySignatureOverData("SHA256withECDSA", generated.getKeyPair());
+            assertThat(mDevicePolicyManager.removeKeyPair(getWho(), alias)).isTrue();
+        } catch (StrongBoxUnavailableException expected) {
+            assertThat(hasStrongBox()).isFalse();
         }
-        verifySignatureOverData("SHA256withECDSA", generated.getKeyPair());
-        assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias));
     }
 
     private void validateDeviceIdAttestationData(Certificate leaf,
@@ -371,32 +377,31 @@
             throws CertificateParsingException {
         Attestation attestationRecord = new Attestation((X509Certificate) leaf);
         AuthorizationList teeAttestation = attestationRecord.getTeeEnforced();
-        assertNotNull(teeAttestation);
-        assertEquals(Build.BRAND, teeAttestation.getBrand());
-        assertEquals(Build.DEVICE, teeAttestation.getDevice());
-        assertEquals(Build.PRODUCT, teeAttestation.getProduct());
-        assertEquals(Build.MANUFACTURER, teeAttestation.getManufacturer());
-        assertEquals(Build.MODEL, teeAttestation.getModel());
-        assertEquals(expectedSerial, teeAttestation.getSerialNumber());
-        assertEquals(expectedImei, teeAttestation.getImei());
-        assertEquals(expectedMeid, teeAttestation.getMeid());
+        assertThat(teeAttestation).isNotNull();
+        assertThat(teeAttestation.getBrand()).isEqualTo(Build.BRAND);
+        assertThat(teeAttestation.getDevice()).isEqualTo(Build.DEVICE);
+        assertThat(teeAttestation.getProduct()).isEqualTo(Build.PRODUCT);
+        assertThat(teeAttestation.getManufacturer()).isEqualTo(Build.MANUFACTURER);
+        assertThat(teeAttestation.getModel()).isEqualTo(Build.MODEL);
+        assertThat(teeAttestation.getSerialNumber()).isEqualTo(expectedSerial);
+        assertThat(teeAttestation.getImei()).isEqualTo(expectedImei);
+        assertThat(teeAttestation.getMeid()).isEqualTo(expectedMeid);
     }
 
-    private void validateAttestationRecord(List<Certificate> attestation,
-            byte[] providedChallenge) throws CertificateParsingException {
-        assertNotNull(attestation);
-        assertTrue(attestation.size() >= 2);
+    private void validateAttestationRecord(List<Certificate> attestation, byte[] providedChallenge)
+            throws CertificateParsingException {
+        assertThat(attestation).isNotNull();
+        assertThat(attestation.size()).isGreaterThan(1);
         X509Certificate leaf = (X509Certificate) attestation.get(0);
         Attestation attestationRecord = new Attestation(leaf);
-        assertTrue(Arrays.equals(providedChallenge,
-                    attestationRecord.getAttestationChallenge()));
+        assertThat(attestationRecord.getAttestationChallenge()).isEqualTo(providedChallenge);
     }
 
     private void validateSignatureChain(List<Certificate> chain, PublicKey leafKey)
             throws GeneralSecurityException {
         X509Certificate leaf = (X509Certificate) chain.get(0);
         PublicKey keyFromCert = leaf.getPublicKey();
-        assertTrue(Arrays.equals(keyFromCert.getEncoded(), leafKey.getEncoded()));
+        assertThat(keyFromCert.getEncoded()).isEqualTo(leafKey.getEncoded());
         // Check that the certificate chain is valid.
         for (int i = 1; i < chain.size(); i++) {
             X509Certificate intermediate = (X509Certificate) chain.get(i);
@@ -449,21 +454,31 @@
             // attestation is supported.
             if (isDeviceIdAttestationRequested(deviceIdAttestationFlags)) {
                 if (generated == null) {
-                    assertFalse(String.format("Failed getting Device ID attestation for key " +
-                                "algorithm %s, with flags %s, despite device declaring support.",
-                                keyAlgorithm, deviceIdAttestationFlags),
-                            isDeviceIdAttestationSupported());
+                    assertWithMessage(
+                            String.format(
+                                    "Failed getting Device ID attestation for key "
+                                    + "algorithm %s, with flags %s, despite device declaring support.",
+                                    keyAlgorithm, deviceIdAttestationFlags))
+                            .that(isDeviceIdAttestationSupported())
+                            .isFalse();
                     return null;
                 } else {
-                    assertTrue(String.format("Device ID attestation for key " +
-                                "algorithm %s, with flags %d should not have succeeded.",
-                                keyAlgorithm, deviceIdAttestationFlags),
-                            isDeviceIdAttestationSupported());
+                    assertWithMessage(
+                            String.format(
+                                    "Device ID attestation for key "
+                                    + "algorithm %s, with flags %d should not have succeeded.",
+                                    keyAlgorithm, deviceIdAttestationFlags))
+                            .that(isDeviceIdAttestationSupported())
+                            .isTrue();
                 }
             } else {
-                assertNotNull(
-                        String.format("Key generation (of type %s) must succeed when Device ID " +
-                            "attestation was not requested.", keyAlgorithm), generated);
+                assertWithMessage(
+                        String.format(
+                                "Key generation (of type %s) must succeed when Device ID "
+                                + "attestation was not requested.",
+                                keyAlgorithm))
+                        .that(generated)
+                        .isNotNull();
             }
             final KeyPair keyPair = generated.getKeyPair();
             verifySignatureOverData(signatureAlgorithm, keyPair);
@@ -472,13 +487,17 @@
             validateSignatureChain(attestation, keyPair.getPublic());
             return attestation.get(0);
         } catch (UnsupportedOperationException ex) {
-            assertTrue(String.format("Unexpected failure while generating key %s with ID flags %d: %s",
-                        keyAlgorithm, deviceIdAttestationFlags, ex),
-                    isDeviceIdAttestationRequested(deviceIdAttestationFlags) &&
-                    !isDeviceIdAttestationSupported());
+            assertWithMessage(
+                    String.format(
+                            "Unexpected failure while generating key %s with ID flags %d: %s",
+                            keyAlgorithm, deviceIdAttestationFlags, ex))
+                    .that(
+                            isDeviceIdAttestationRequested(deviceIdAttestationFlags)
+                            && !isDeviceIdAttestationSupported())
+                    .isTrue();
             return null;
         } finally {
-            assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias));
+            assertThat(mDevicePolicyManager.removeKeyPair(getWho(), alias)).isTrue();
         }
     }
 
@@ -487,23 +506,32 @@
      * algorithms.
      */
     public void testCanGenerateKeyPairWithKeyAttestation() throws Exception {
-        for (SupportedKeyAlgorithm supportedKey: SUPPORTED_KEY_ALGORITHMS) {
-            assertNotNull(generateKeyAndCheckAttestation(
-                    supportedKey.keyAlgorithm, supportedKey.signatureAlgorithm,
-                    supportedKey.signaturePaddingSchemes, false /* useStrongBox */, 0));
+        for (SupportedKeyAlgorithm supportedKey : SUPPORTED_KEY_ALGORITHMS) {
+            assertThat(
+                    generateKeyAndCheckAttestation(
+                            supportedKey.keyAlgorithm,
+                            supportedKey.signatureAlgorithm,
+                            supportedKey.signaturePaddingSchemes,
+                            false /* useStrongBox */,
+                            0))
+                    .isNotNull();
         }
     }
 
     public void testCanGenerateKeyPairWithKeyAttestationUsingStrongBox() throws Exception {
-        for (SupportedKeyAlgorithm supportedKey: SUPPORTED_KEY_ALGORITHMS) {
-            Certificate attestation = generateKeyAndCheckAttestation(
-                    supportedKey.keyAlgorithm, supportedKey.signatureAlgorithm,
-                    supportedKey.signaturePaddingSchemes, true /* useStrongBox */,
-                    0 /* idAttestationFlags */);
-            if (attestation == null) {
-                assertFalse("StrongBox-backed key attestation must not fail if the device " +
-                        "declares support for StrongBox", hasStrongBox());
+        try {
+            for (SupportedKeyAlgorithm supportedKey : SUPPORTED_KEY_ALGORITHMS) {
+                assertThat(
+                        generateKeyAndCheckAttestation(
+                                supportedKey.keyAlgorithm,
+                                supportedKey.signatureAlgorithm,
+                                supportedKey.signaturePaddingSchemes,
+                                true /* useStrongBox */,
+                                0))
+                        .isNotNull();
             }
+        } catch (StrongBoxUnavailableException expected) {
+            assertThat(hasStrongBox()).isFalse();
         }
     }
 
@@ -518,9 +546,11 @@
         if (isDeviceOwner()) {
             modesToTest.add(ID_TYPE_SERIAL);
             // Get IMEI and MEID of the device.
-            TelephonyManager telephonyService = (TelephonyManager) mActivity.getSystemService(
-                    Context.TELEPHONY_SERVICE);
-            assertNotNull("Need to be able to read device identifiers", telephonyService);
+            TelephonyManager telephonyService =
+                    (TelephonyManager) mActivity.getSystemService(Context.TELEPHONY_SERVICE);
+            assertWithMessage("Need to be able to read device identifiers")
+                    .that(telephonyService)
+                    .isNotNull();
             imei = telephonyService.getImei(0);
             meid = telephonyService.getMeid(0);
             // If the device has a valid IMEI it must support attestation for it.
@@ -553,14 +583,12 @@
                     if (attestation == null && !isDeviceIdAttestationSupported()) {
                         continue;
                     }
-                    // The attestation must only be null if StrongBox attestation was requested,
-                    // but StrongBox is not available on the device.
-                    if (attestation == null && useStrongBox) {
-                        assertFalse(hasStrongBox());
-                    }
-                    assertNotNull(String.format(
-                            "Attestation should be valid for key %s with attestation modes %s",
-                            supportedKey.keyAlgorithm, devIdOpt), attestation);
+                    assertWithMessage(
+                            String.format(
+                                    "Attestation should be valid for key %s with attestation modes %s",
+                                    supportedKey.keyAlgorithm, devIdOpt))
+                            .that(attestation)
+                            .isNotNull();
                     // Set the expected values for serial, IMEI and MEID depending on whether
                     // attestation for them was requested.
                     String expectedSerial = null;
@@ -581,7 +609,10 @@
             } catch (UnsupportedOperationException expected) {
                 // Make sure the test only fails if the device is not meant to support Device
                 // ID attestation.
-                assertFalse(isDeviceIdAttestationSupported());
+                assertThat(isDeviceIdAttestationSupported()).isFalse();
+            } catch (StrongBoxUnavailableException expected) {
+                // This exception must only be thrown if StrongBox attestation was requested.
+                assertThat(useStrongBox && !hasStrongBox()).isTrue();
             }
         }
     }
@@ -601,8 +632,9 @@
         int[] forbiddenModes = new int[] {ID_TYPE_SERIAL, ID_TYPE_IMEI, ID_TYPE_MEID};
         for (int i = 0; i < forbiddenModes.length; i++) {
             try {
-                for (SupportedKeyAlgorithm supportedKey: SUPPORTED_KEY_ALGORITHMS) {
-                    generateKeyAndCheckAttestation(supportedKey.keyAlgorithm,
+                for (SupportedKeyAlgorithm supportedKey : SUPPORTED_KEY_ALGORITHMS) {
+                    generateKeyAndCheckAttestation(
+                            supportedKey.keyAlgorithm,
                             supportedKey.signatureAlgorithm,
                             supportedKey.signaturePaddingSchemes,
                             false /* useStrongBox */,
@@ -611,7 +643,7 @@
                             + "possible from profile owner");
                 }
             } catch (SecurityException e) {
-                assertTrue(e.getMessage().contains("does not own the device"));
+                assertThat(e.getMessage()).contains("does not own the device");
             }
         }
     }
@@ -625,9 +657,9 @@
                     .setDigests(KeyProperties.DIGEST_SHA256)
                     .build();
 
-            AttestedKeyPair generated = mDevicePolicyManager.generateKeyPair(
-                    getWho(), "EC", spec, 0);
-            assertNotNull(generated);
+            AttestedKeyPair generated =
+                    mDevicePolicyManager.generateKeyPair(getWho(), "EC", spec, 0);
+            assertThat(generated).isNotNull();
             // Create a self-signed cert to go with it.
             X500Principal issuer = new X500Principal("CN=SelfSigned, O=Android, C=US");
             X500Principal subject = new X500Principal("CN=Subject, O=Android, C=US");
@@ -637,13 +669,13 @@
             certs.add(cert);
             mDevicePolicyManager.setKeyPairCertificate(getWho(), alias, certs, true);
             // Make sure that the alias can now be obtained.
-            assertEquals(alias, new KeyChainAliasFuture(alias).get());
+            assertThat(new KeyChainAliasFuture(alias).get()).isEqualTo(alias);
             // And can be retrieved from KeyChain
             X509Certificate[] fetchedCerts = KeyChain.getCertificateChain(mActivity, alias);
-            assertEquals(fetchedCerts.length, certs.size());
-            assertTrue(Arrays.equals(fetchedCerts[0].getEncoded(), certs.get(0).getEncoded()));
+            assertThat(fetchedCerts.length).isEqualTo(certs.size());
+            assertThat(fetchedCerts[0].getEncoded()).isEqualTo(certs.get(0).getEncoded());
         } finally {
-            assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias));
+            assertThat(mDevicePolicyManager.removeKeyPair(getWho(), alias)).isTrue();
         }
     }
 
@@ -656,21 +688,21 @@
                     .setDigests(KeyProperties.DIGEST_SHA256)
                     .build();
 
-            AttestedKeyPair generated = mDevicePolicyManager.generateKeyPair(
-                    getWho(), "EC", spec, 0);
-            assertNotNull(generated);
+            AttestedKeyPair generated =
+                    mDevicePolicyManager.generateKeyPair(getWho(), "EC", spec, 0);
+            assertThat(generated).isNotNull();
             List<Certificate> chain = loadCertificateChain("user-cert-chain.crt");
             mDevicePolicyManager.setKeyPairCertificate(getWho(), alias, chain, true);
             // Make sure that the alias can now be obtained.
-            assertEquals(alias, new KeyChainAliasFuture(alias).get());
+            assertThat(new KeyChainAliasFuture(alias).get()).isEqualTo(alias);
             // And can be retrieved from KeyChain
             X509Certificate[] fetchedCerts = KeyChain.getCertificateChain(mActivity, alias);
-            assertEquals(fetchedCerts.length, chain.size());
+            assertThat(fetchedCerts.length).isEqualTo(chain.size());
             for (int i = 0; i < chain.size(); i++) {
-                assertTrue(Arrays.equals(fetchedCerts[i].getEncoded(), chain.get(i).getEncoded()));
+                assertThat(fetchedCerts[i].getEncoded()).isEqualTo(chain.get(i).getEncoded());
             }
         } finally {
-            assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias));
+            assertThat(mDevicePolicyManager.removeKeyPair(getWho(), alias)).isTrue();
         }
     }
 
@@ -683,7 +715,7 @@
                 e.printStackTrace();
             }
         }
-        assertEquals("Grant for alias: \"" + alias + "\"", expected, granted);
+        assertWithMessage("Grant for alias: \"" + alias + "\"").that(granted).isEqualTo(expected);
     }
 
     private static PrivateKey getPrivateKey(final byte[] key, String type)
@@ -748,7 +780,9 @@
         }
 
         public String get() throws InterruptedException {
-            assertTrue("Chooser timeout", mLatch.await(KEYCHAIN_TIMEOUT_MINS, TimeUnit.MINUTES));
+            assertWithMessage("Chooser timeout")
+                    .that(mLatch.await(KEYCHAIN_TIMEOUT_MINS, TimeUnit.MINUTES))
+                    .isTrue();
             return mChosenAlias;
         }
     }
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PasswordExpirationTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PasswordExpirationTest.java
new file mode 100644
index 0000000..73841fa
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PasswordExpirationTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.deviceandprofileowner;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class PasswordExpirationTest extends BaseDeviceAdminTest {
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mOnPasswordExpiryTimeoutCalled = new CountDownLatch(1);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mDevicePolicyManager.setPasswordExpirationTimeout(ADMIN_RECEIVER_COMPONENT, 0);
+        mOnPasswordExpiryTimeoutCalled = null;
+    }
+
+    public void testPasswordExpires() throws Exception {
+        final long testTimeoutMs = 5000;
+        // Set a significantly longer wait time to check that the countDownLatch is called to prevent
+        // tests flakiness due to setups delays.
+        final long testWaitTimeMs = 300000;
+        mDevicePolicyManager.setPasswordExpirationTimeout(ADMIN_RECEIVER_COMPONENT, testTimeoutMs);
+        assertTrue("The password expiry timeout was not triggered.",
+                mOnPasswordExpiryTimeoutCalled.await(testWaitTimeMs, TimeUnit.MILLISECONDS));
+    }
+
+    public void testNoNegativeTimeout() {
+        final long testTimeoutMs = -1;
+        try {
+            mDevicePolicyManager.setPasswordExpirationTimeout(ADMIN_RECEIVER_COMPONENT, testTimeoutMs);
+            fail("Setting a negative value for a timeout is expected to throw an exception.");
+        } catch (IllegalArgumentException e) {}
+    }
+
+    // TODO: investigate this test's failure. b/110976462
+    /* public void testPasswordNotYetExpiredIsInEffect() throws Exception {
+        final long testTimeoutMs = 5000;
+        mDevicePolicyManager.setPasswordExpirationTimeout(ADMIN_RECEIVER_COMPONENT, testTimeoutMs);
+        assertFalse("The password expiry timeout was triggered too early.",
+                mOnPasswordExpiryTimeoutCalled.await(testTimeoutMs, TimeUnit.MILLISECONDS));
+    }
+    */
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/StorageEncryptionTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/StorageEncryptionTest.java
new file mode 100644
index 0000000..3244abc
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/StorageEncryptionTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.deviceandprofileowner;
+
+import static android.app.admin.DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE;
+import static android.app.admin.DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE;
+import static android.app.admin.DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED;
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+
+/**
+ * Test {@link DevicePolicyManager#setStorageEncryption(ComponentName, boolean)} and
+ * {@link DevicePolicyManager#getStorageEncryption(ComponentName)}.
+ * <p>Note that most physical devices are required to have storage encryption, but since emulators
+ * do not support encryption yet, we allow the {@link
+ * DevicePolicyManager#ENCRYPTION_STATUS_UNSUPPORTED} result to pass to reduce noise in our
+ * testing dashboards. If a physical device does not have storage encryption support, it will
+ * be caught in CTSVerifier.
+ */
+public class StorageEncryptionTest extends BaseDeviceAdminTest {
+    private static final ComponentName ADMIN_RECEIVER_COMPONENT =
+        BaseDeviceAdminTest.ADMIN_RECEIVER_COMPONENT;
+
+    public void testSetStorageEncryption_enabled() {
+        if (mDevicePolicyManager.getStorageEncryptionStatus() == ENCRYPTION_STATUS_UNSUPPORTED) {
+            return;
+        }
+        assertThat(mDevicePolicyManager.setStorageEncryption(ADMIN_RECEIVER_COMPONENT, true))
+            .isEqualTo(ENCRYPTION_STATUS_ACTIVE);
+        assertThat(mDevicePolicyManager.getStorageEncryption(ADMIN_RECEIVER_COMPONENT)).isTrue();
+    }
+
+    public void testSetStorageEncryption_disabled() {
+        if (mDevicePolicyManager.getStorageEncryptionStatus() == ENCRYPTION_STATUS_UNSUPPORTED) {
+            return;
+        }
+        assertThat(mDevicePolicyManager.setStorageEncryption(ADMIN_RECEIVER_COMPONENT, false))
+            .isEqualTo(ENCRYPTION_STATUS_INACTIVE);
+        assertThat(mDevicePolicyManager.getStorageEncryption(ADMIN_RECEIVER_COMPONENT)).isFalse();
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk b/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
index c3f6d124..7732437 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
@@ -41,12 +41,20 @@
     compatibility-device-util \
     android-support-test \
     cts-security-test-support-library \
-    testng
+    testng \
+    truth-prebuilt
 
 LOCAL_STATIC_ANDROID_LIBRARIES := \
     androidx.legacy_legacy-support-v4
 
+LOCAL_MIN_SDK_VERSION := 21
+
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := arcts cts vts general-tests
 
+# Code coverage puts us over the dex limit, so enable multi-dex for coverage-enabled builds
+ifeq (true,$(EMMA_INSTRUMENT))
+LOCAL_DX_FLAGS := --multi-dex
+endif # EMMA_INSTRUMENT
+
 include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CreateAndManageUserTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CreateAndManageUserTest.java
index bbe68559..6632c09 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CreateAndManageUserTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CreateAndManageUserTest.java
@@ -16,7 +16,6 @@
 
 package com.android.cts.deviceowner;
 
-import android.app.ActivityManager;
 import android.app.Service;
 import android.app.admin.DeviceAdminReceiver;
 import android.app.admin.DevicePolicyManager;
@@ -29,7 +28,6 @@
 import android.content.pm.PackageManager;
 import android.os.IBinder;
 import android.os.PersistableBundle;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -54,31 +52,16 @@
 public class CreateAndManageUserTest extends BaseDeviceOwnerTest {
     private static final String TAG = "CreateAndManageUserTest";
 
-    private static final String BROADCAST_EXTRA = "broadcastExtra";
-    private static final String ACTION_EXTRA = "actionExtra";
-    private static final String SERIAL_EXTRA = "serialExtra";
-    private static final String PROFILE_OWNER_EXTRA = "profileOwnerExtra";
-    private static final String SETUP_COMPLETE_EXTRA = "setupCompleteExtra";
     private static final int BROADCAST_TIMEOUT = 15_000;
-    private static final int USER_SWITCH_DELAY = 10_000;
 
     private static final String AFFILIATION_ID = "affiliation.id";
     private static final String EXTRA_AFFILIATION_ID = "affiliationIdExtra";
     private static final String EXTRA_METHOD_NAME = "methodName";
     private static final long ON_ENABLED_TIMEOUT_SECONDS = 120;
 
-
-    private PackageManager mPackageManager;
-    private ActivityManager mActivityManager;
-    private volatile boolean mReceived;
-    private volatile boolean mTestProfileOwnerWasUsed;
-    private volatile boolean mSetupComplete;
-
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        mPackageManager = mContext.getPackageManager();
-        mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
     }
 
     @Override
@@ -88,122 +71,7 @@
         super.tearDown();
     }
 
-    // This class is used by createAndManageUserTest as profile owner for the new user. When
-    // enabled, it sends a broadcast to signal success.
-    public static class TestProfileOwner extends DeviceAdminReceiver {
-        @Override
-        public void onEnabled(Context context, Intent intent) {
-            if (intent.getBooleanExtra(BROADCAST_EXTRA, false)) {
-                Intent i = new Intent(intent.getStringExtra(ACTION_EXTRA));
-                UserManager userManager = (UserManager)
-                        context.getSystemService(Context.USER_SERVICE);
-                long serial = intent.getLongExtra(SERIAL_EXTRA, 0);
-                UserHandle handle = userManager.getUserForSerialNumber(serial);
-                i.putExtra(PROFILE_OWNER_EXTRA, true);
-                // find value of user_setup_complete on new user, and send the result back
-                try {
-                    boolean setupComplete = (Settings.Secure.getInt(context.getContentResolver(),
-                            "user_setup_complete") == 1);
-                    i.putExtra(SETUP_COMPLETE_EXTRA, setupComplete);
-                } catch (Settings.SettingNotFoundException e) {
-                    fail("Did not find settings user_setup_complete");
-                }
-
-                context.sendBroadcastAsUser(i, handle);
-            }
-        }
-
-        public static ComponentName getComponentName() {
-            return new ComponentName(CreateAndManageUserTest.class.getPackage().getName(),
-                    TestProfileOwner.class.getName());
-        }
-    }
-
-    private void waitForBroadcastLocked() {
-        // Wait for broadcast. Time is measured in a while loop because of spurious wakeups.
-        final long initTime = System.currentTimeMillis();
-        while (!mReceived) {
-            try {
-                wait(BROADCAST_TIMEOUT - (System.currentTimeMillis() - initTime));
-            } catch (InterruptedException e) {
-                fail("InterruptedException: " + e.getMessage());
-            }
-            if (!mReceived && System.currentTimeMillis() - initTime > BROADCAST_TIMEOUT) {
-                fail("Timeout while waiting for broadcast after createAndManageUser.");
-            }
-        }
-    }
-
-    // This test will create a user that will get passed a bundle that we specify. The bundle will
-    // contain an action and a serial (for user handle) to broadcast to notify the test that the
-    // configuration was triggered.
-    private void createAndManageUserTest(final int flags) {
-        // This test sets a profile owner on the user, which requires the managed_users feature.
-        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS)) {
-            return;
-        }
-
-        final boolean expectedSetupComplete = (flags & DevicePolicyManager.SKIP_SETUP_WIZARD) != 0;
-        UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-
-        UserHandle firstUser = Process.myUserHandle();
-        final String testUserName = "TestUser_" + System.currentTimeMillis();
-        String action = "com.android.cts.TEST_USER_ACTION";
-        PersistableBundle bundle = new PersistableBundle();
-        bundle.putBoolean(BROADCAST_EXTRA, true);
-        bundle.putLong(SERIAL_EXTRA, userManager.getSerialNumberForUser(firstUser));
-        bundle.putString(ACTION_EXTRA, action);
-
-        mReceived = false;
-        mTestProfileOwnerWasUsed = false;
-        mSetupComplete = !expectedSetupComplete;
-        BroadcastReceiver receiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                mReceived = true;
-                if (intent.getBooleanExtra(PROFILE_OWNER_EXTRA, false)) {
-                    mTestProfileOwnerWasUsed = true;
-                }
-                mSetupComplete = intent.getBooleanExtra(SETUP_COMPLETE_EXTRA,
-                        !expectedSetupComplete);
-                synchronized (CreateAndManageUserTest.this) {
-                    CreateAndManageUserTest.this.notify();
-                }
-            }
-        };
-
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(action);
-        mContext.registerReceiver(receiver, filter);
-
-        synchronized (this) {
-            UserHandle userHandle = mDevicePolicyManager.createAndManageUser(getWho(), testUserName,
-                    TestProfileOwner.getComponentName(), bundle, flags);
-            assertNotNull(userHandle);
-
-            mDevicePolicyManager.switchUser(getWho(), userHandle);
-            try {
-                wait(USER_SWITCH_DELAY);
-            } catch (InterruptedException e) {
-                fail("InterruptedException: " + e.getMessage());
-            }
-            mDevicePolicyManager.switchUser(getWho(), firstUser);
-
-            waitForBroadcastLocked();
-
-            assertTrue(mReceived);
-            assertTrue(mTestProfileOwnerWasUsed);
-            assertEquals(expectedSetupComplete, mSetupComplete);
-
-            assertTrue(mDevicePolicyManager.removeUser(getWho(), userHandle));
-
-            userHandle = null;
-        }
-
-        mContext.unregisterReceiver(receiver);
-    }
-
-    public void testCreateAndManageUser() throws Exception {
+    public void testCreateAndManageUser() {
         String testUserName = "TestUser_" + System.currentTimeMillis();
 
         UserHandle userHandle = mDevicePolicyManager.createAndManageUser(
@@ -215,7 +83,7 @@
         Log.d(TAG, "User create: " + userHandle);
     }
 
-    public void testCreateAndManageUser_LowStorage() throws Exception {
+    public void testCreateAndManageUser_LowStorage() {
         String testUserName = "TestUser_" + System.currentTimeMillis();
 
         try {
@@ -231,7 +99,7 @@
         }
     }
 
-    public void testCreateAndManageUser_MaxUsers() throws Exception {
+    public void testCreateAndManageUser_MaxUsers() {
         String testUserName = "TestUser_" + System.currentTimeMillis();
 
         try {
@@ -247,7 +115,20 @@
         }
     }
 
-    public void testCreateAndManageUser_GetSecondaryUsers() throws Exception {
+    @SuppressWarnings("unused")
+    private static void assertSkipSetupWizard(Context context,
+            DevicePolicyManager devicePolicyManager, ComponentName componentName) throws Exception {
+        assertEquals("user setup not completed", 1,
+                Settings.Secure.getInt(context.getContentResolver(),
+                        Settings.Secure.USER_SETUP_COMPLETE));
+    }
+
+    public void testCreateAndManageUser_SkipSetupWizard() throws Exception {
+        runCrossUserVerification(DevicePolicyManager.SKIP_SETUP_WIZARD, "assertSkipSetupWizard");
+        PrimaryUserService.assertCrossUserCallArrived();
+    }
+
+    public void testCreateAndManageUser_GetSecondaryUsers() {
         String testUserName = "TestUser_" + System.currentTimeMillis();
 
         UserHandle userHandle = mDevicePolicyManager.createAndManageUser(
@@ -343,7 +224,7 @@
         }
     }
 
-    public void testCreateAndManageUser_StartInBackground_MaxRunningUsers() throws Exception {
+    public void testCreateAndManageUser_StartInBackground_MaxRunningUsers() {
         String testUserName = "TestUser_" + System.currentTimeMillis();
 
         UserHandle userHandle = mDevicePolicyManager.createAndManageUser(
@@ -496,8 +377,7 @@
         PrimaryUserService.assertCrossUserCallArrived();
     }
 
-    private UserHandle runCrossUserVerification(int createAndManageUserFlags, String methodName)
-            throws Exception {
+    private UserHandle runCrossUserVerification(int createAndManageUserFlags, String methodName) {
         String testUserName = "TestUser_" + System.currentTimeMillis();
 
         // Set affiliation id to allow communication.
@@ -521,18 +401,6 @@
         return userHandle;
     }
 
-    public void testCreateAndManageUser_SkipSetupWizard() {
-        createAndManageUserTest(DevicePolicyManager.SKIP_SETUP_WIZARD);
-    }
-
-    public void testCreateAndManageUser_DontSkipSetupWizard() {
-        if (!mActivityManager.isRunningInTestHarness()) {
-            // In test harness, the setup wizard will be disabled by default, so this test is always
-            // failing.
-            createAndManageUserTest(0);
-        }
-    }
-
     // createAndManageUser should circumvent the DISALLOW_ADD_USER restriction
     public void testCreateAndManageUser_AddRestrictionSet() {
         mDevicePolicyManager.addUserRestriction(getWho(), UserManager.DISALLOW_ADD_USER);
@@ -581,7 +449,7 @@
     }
 
     static class LocalBroadcastReceiver extends BroadcastReceiver {
-        private LinkedBlockingQueue<UserHandle> mQueue = new LinkedBlockingQueue<UserHandle>(1);
+        private LinkedBlockingQueue<UserHandle> mQueue = new LinkedBlockingQueue<>(1);
 
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -592,7 +460,7 @@
 
         }
 
-        public UserHandle waitForBroadcastReceived() throws InterruptedException {
+        UserHandle waitForBroadcastReceived() throws InterruptedException {
             return mQueue.poll(BROADCAST_TIMEOUT, TimeUnit.MILLISECONDS);
         }
     }
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/NetworkLoggingTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/NetworkLoggingTest.java
index e727d56..a4c8f21 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/NetworkLoggingTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/NetworkLoggingTest.java
@@ -15,6 +15,8 @@
  */
 package com.android.cts.deviceowner;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import android.app.admin.ConnectEvent;
 import android.app.admin.DnsEvent;
 import android.app.admin.NetworkEvent;
@@ -22,8 +24,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.os.Parcel;
 import android.support.test.InstrumentationRegistry;
-import androidx.localbroadcastmanager.content.LocalBroadcastManager;
 import android.util.Log;
 
 import java.io.BufferedReader;
@@ -38,10 +40,13 @@
 import java.net.Socket;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+
 public class NetworkLoggingTest extends BaseDeviceOwnerTest {
 
     private static final String TAG = "NetworkLoggingTest";
@@ -193,6 +198,80 @@
         verifyNetworkLogs(mNetworkEvents, eventsExpected);
     }
 
+    private void verifyDnsEvent(DnsEvent dnsEvent) {
+        // Verify that we didn't log a hostname lookup when network logging was disabled.
+        if (dnsEvent.getHostname().contains(NOT_LOGGED_URLS_LIST[0])
+                || dnsEvent.getHostname().contains(NOT_LOGGED_URLS_LIST[1])) {
+            fail("A hostname that was looked-up when network logging was disabled"
+                    + " was logged.");
+        }
+
+        // Verify that as many IP addresses were logged as were reported (max 10).
+        final List<InetAddress> ips = dnsEvent.getInetAddresses();
+        assertThat(ips.size()).isAtMost(MAX_IP_ADDRESSES_LOGGED);
+        final int expectedAddressCount = Math.min(MAX_IP_ADDRESSES_LOGGED,
+                dnsEvent.getTotalResolvedAddressCount());
+        assertThat(expectedAddressCount).isEqualTo(ips.size());
+
+        // Verify the IP addresses are valid IPv4 or IPv6 addresses.
+        for (final InetAddress ipAddress : ips) {
+            assertTrue(isIpv4OrIpv6Address(ipAddress));
+        }
+
+        //Verify writeToParcel.
+        Parcel parcel = Parcel.obtain();
+        try {
+            dnsEvent.writeToParcel(parcel, 0);
+            parcel.setDataPosition(0);
+            final DnsEvent dnsEventOut = DnsEvent.CREATOR.createFromParcel(parcel);
+            assertThat(dnsEventOut).isNotNull();
+            verifyDnsEventsEqual(dnsEvent, dnsEventOut);
+        } finally {
+            parcel.recycle();
+        }
+    }
+
+    private void verifyDnsEventsEqual(DnsEvent event1, DnsEvent event2) {
+        assertThat(event1.getHostname()).isEqualTo(event2.getHostname());
+        assertThat(new HashSet<InetAddress>(event1.getInetAddresses())).isEqualTo(
+                        new HashSet<InetAddress>(event2.getInetAddresses()));
+        assertThat(event1.getTotalResolvedAddressCount()).isEqualTo(
+                event2.getTotalResolvedAddressCount());
+        assertThat(event1.getPackageName()).isEqualTo(event2.getPackageName());
+        assertThat(event1.getTimestamp()).isEqualTo(event2.getTimestamp());
+        assertThat(event1.getId()).isEqualTo(event2.getId());
+    }
+
+    private void verifyConnectEvent(ConnectEvent connectEvent) {
+        // Verify the IP address is a valid IPv4 or IPv6 address.
+        final InetAddress ip = connectEvent.getInetAddress();
+        assertThat(isIpv4OrIpv6Address(ip)).isTrue();
+
+        // Verify that the port is a valid port.
+        assertThat(connectEvent.getPort()).isAtLeast(0);
+        assertThat(connectEvent.getPort()).isAtMost(65535);
+
+        // Verify writeToParcel.
+        Parcel parcel = Parcel.obtain();
+        try {
+            connectEvent.writeToParcel(parcel, 0);
+            parcel.setDataPosition(0);
+            final ConnectEvent connectEventOut = ConnectEvent.CREATOR.createFromParcel(parcel);
+            assertThat(connectEventOut).isNotNull();
+            verifyConnectEventsEqual(connectEvent, connectEventOut);
+        } finally {
+             parcel.recycle();
+        }
+    }
+
+    private void verifyConnectEventsEqual(ConnectEvent event1, ConnectEvent event2) {
+        assertThat(event1.getInetAddress()).isEqualTo(event2.getInetAddress());
+        assertThat(event1.getPort()).isEqualTo(event2.getPort());
+        assertThat(event1.getPackageName()).isEqualTo(event2.getPackageName());
+        assertThat(event1.getTimestamp()).isEqualTo(event2.getTimestamp());
+        assertThat(event1.getId()).isEqualTo(event2.getId());
+    }
+
     private void verifyNetworkLogs(List<NetworkEvent> networkEvents, int eventsExpected) {
         // allow a batch to be slightly smaller or larger.
         assertTrue(Math.abs(eventsExpected - networkEvents.size()) <= 50);
@@ -216,35 +295,18 @@
                 ctsPackageNameCounter++;
                 if (currentEvent instanceof DnsEvent) {
                     final DnsEvent dnsEvent = (DnsEvent) currentEvent;
-                    // verify that we didn't log a hostname lookup when network logging was disabled
-                    if (dnsEvent.getHostname().contains(NOT_LOGGED_URLS_LIST[0])
-                            || dnsEvent.getHostname().contains(NOT_LOGGED_URLS_LIST[1])) {
-                        fail("A hostname that was looked-up when network logging was disabled"
-                                + " was logged.");
-                    }
-                    // count the frequencies of LOGGED_URLS_LIST's hostnames that were looked up
+                    // Mark which addresses from LOGGED_URLS_LIST were visited.
                     for (int j = 0; j < LOGGED_URLS_LIST.length; j++) {
                         if (dnsEvent.getHostname().contains(LOGGED_URLS_LIST[j])) {
                             visited[j] = true;
                             break;
                         }
                     }
-                    // verify that as many IP addresses were logged as were reported (max 10)
-                    final List<InetAddress> ips = dnsEvent.getInetAddresses();
-                    assertTrue(ips.size() <= MAX_IP_ADDRESSES_LOGGED);
-                    final int expectedAddressCount = Math.min(MAX_IP_ADDRESSES_LOGGED,
-                            dnsEvent.getTotalResolvedAddressCount());
-                    assertEquals(expectedAddressCount, ips.size());
-                    // verify the IP addresses are valid IPv4 or IPv6 addresses
-                    for (final InetAddress ipAddress : ips) {
-                        assertTrue(isIpv4OrIpv6Address(ipAddress));
-                    }
+
+                    verifyDnsEvent(dnsEvent);
                 } else if (currentEvent instanceof ConnectEvent) {
                     final ConnectEvent connectEvent = (ConnectEvent) currentEvent;
-                    // verify the IP address is a valid IPv4 or IPv6 address
-                    assertTrue(isIpv4OrIpv6Address(connectEvent.getInetAddress()));
-                    // verify that the port is a valid port
-                    assertTrue(connectEvent.getPort() >= 0 && connectEvent.getPort() <= 65535);
+                    verifyConnectEvent(connectEvent);
                 } else {
                     fail("An unknown NetworkEvent type logged: "
                             + currentEvent.getClass().getName());
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SecurityLoggingTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SecurityLoggingTest.java
index 5f537a3..5118b83 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SecurityLoggingTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SecurityLoggingTest.java
@@ -76,6 +76,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
@@ -399,9 +400,14 @@
     private void verifyOsStartupEventPresent(List<SecurityEvent> events) {
         final SecurityEvent event = findEvent("os startup", events, TAG_OS_STARTUP);
         // Verified boot state, empty if running on emulator
-        assertTrue(ImmutableSet.of("", "green", "yellow", "orange").contains(getString(event, 0)));
+        assertOneOf(ImmutableSet.of("", "green", "yellow", "orange"), getString(event, 0));
         // dm-verity mode, empty if it is disabled
-        assertTrue(ImmutableSet.of("enforcing", "eio", "").contains(getString(event, 1)));
+        assertOneOf(ImmutableSet.of("", "enforcing", "eio", "disabled"), getString(event, 1));
+    }
+
+    private void assertOneOf(Set<String> allowed, String s) {
+        assertTrue(String.format("\"%s\" is not one of [%s]", s, String.join(", ", allowed)),
+                allowed.contains(s));
     }
 
     private void verifyCryptoSelfTestEventPresent(List<SecurityEvent> events) {
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SystemUpdatePolicyTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SystemUpdatePolicyTest.java
index 4fa6235..cc7c29a 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SystemUpdatePolicyTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SystemUpdatePolicyTest.java
@@ -16,6 +16,8 @@
 package com.android.cts.deviceowner;
 
 
+import static com.google.common.truth.Truth.assertThat;
+
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.FreezePeriod;
 import android.app.admin.SystemUpdatePolicy;
@@ -25,14 +27,16 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.icu.util.Calendar;
+import android.os.Parcel;
 import android.provider.Settings;
 import android.provider.Settings.Global;
-import android.util.Pair;
 
+import com.google.common.collect.ImmutableList;
 import java.time.LocalDate;
 import java.time.MonthDay;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
@@ -235,6 +239,109 @@
         }
     }
 
+    public void testWriteSystemUpdatePolicyToParcel() {
+        final Parcel parcel1 = Parcel.obtain();
+        try {
+            final SystemUpdatePolicy policy1 = SystemUpdatePolicy.createAutomaticInstallPolicy();
+            policy1.writeToParcel(parcel1, 0);
+            parcel1.setDataPosition(0);
+            final SystemUpdatePolicy copy1 = SystemUpdatePolicy.CREATOR.createFromParcel(parcel1);
+            assertThat(copy1).isNotNull();
+            assertSystemUpdatePoliciesEqual(policy1, copy1);
+        } finally {
+            parcel1.recycle();
+        }
+
+        final Parcel parcel2 = Parcel.obtain();
+        try {
+            final SystemUpdatePolicy policy2 = SystemUpdatePolicy
+                .createWindowedInstallPolicy(0, 720);
+            policy2.writeToParcel(parcel2, 0);
+            parcel2.setDataPosition(0);
+            final SystemUpdatePolicy copy2 = SystemUpdatePolicy.CREATOR.createFromParcel(parcel2);
+            assertThat(copy2).isNotNull();
+            assertSystemUpdatePoliciesEqual(policy2, copy2);
+        } finally {
+            parcel2.recycle();
+        }
+
+        final Parcel parcel3 = Parcel.obtain();
+        try {
+            final SystemUpdatePolicy policy3 = SystemUpdatePolicy.createPostponeInstallPolicy();
+            policy3.writeToParcel(parcel3, 0);
+            parcel3.setDataPosition(0);
+            final SystemUpdatePolicy copy3 = SystemUpdatePolicy.CREATOR.createFromParcel(parcel3);
+            assertThat(copy3).isNotNull();
+            assertSystemUpdatePoliciesEqual(policy3, copy3);
+        } finally {
+            parcel3.recycle();
+        }
+    }
+
+    public void testWriteValidationFailedExceptionToParcel() {
+        final List<FreezePeriod> freezePeriods =
+            ImmutableList.of(new FreezePeriod(MonthDay.of(1, 10), MonthDay.of(1, 9)));
+        try {
+            SystemUpdatePolicy.createAutomaticInstallPolicy().setFreezePeriods(freezePeriods);
+            fail("ValidationFailedException not thrown for invalid freeze period.");
+        } catch (ValidationFailedException e) {
+            final Parcel parcel = Parcel.obtain();
+            e.writeToParcel(parcel, 0);
+            parcel.setDataPosition(0);
+
+            final ValidationFailedException copy =
+                ValidationFailedException.CREATOR.createFromParcel(parcel);
+
+            assertThat(copy).isNotNull();
+            assertThat(e.getErrorCode()).isEqualTo(copy.getErrorCode());
+            assertThat(e.getMessage()).isEqualTo(copy.getMessage());
+        }
+    }
+
+    private void assertSystemUpdatePoliciesEqual(SystemUpdatePolicy policy,
+            SystemUpdatePolicy copy) {
+        assertThat(policy.getInstallWindowStart()).isEqualTo(copy.getInstallWindowStart());
+        assertThat(policy.getInstallWindowEnd()).isEqualTo(copy.getInstallWindowEnd());
+        assertFreezePeriodListsEqual(policy.getFreezePeriods(), copy.getFreezePeriods());
+        assertThat(policy.getPolicyType()).isEqualTo(copy.getPolicyType());
+    }
+
+    private void assertFreezePeriodListsEqual(List<FreezePeriod> original,
+            List<FreezePeriod> copy) {
+        assertThat(original).isNotNull();
+        assertThat(copy).isNotNull();
+        assertThat(original.size()).isEqualTo(copy.size());
+        for (FreezePeriod period1 : original) {
+            assertThat(period1).isNotNull();
+            assertFreezePeriodListContains(copy, period1);
+        }
+        for (FreezePeriod period1 : copy) {
+            assertThat(period1).isNotNull();
+            assertFreezePeriodListContains(original, period1);
+        }
+    }
+
+    private void assertFreezePeriodListContains(List<FreezePeriod> list, FreezePeriod period) {
+        for (FreezePeriod other : list) {
+            assertThat(other).isNotNull();
+            if (areFreezePeriodsEqual(period, other)) {
+                return;
+            }
+        }
+        final List<String> printablePeriods = new ArrayList<>();
+        for (FreezePeriod printablePeriod : list) {
+            printablePeriods.add(printablePeriod.toString());
+        }
+        fail(String.format("FreezePeriod list [%s] does not contain the specified period %s.",
+            String.join(", ", printablePeriods), period));
+    }
+
+    private boolean areFreezePeriodsEqual(FreezePeriod period1, FreezePeriod period2) {
+        return period1 != null && period2 != null
+            && Objects.equals(period1.getStart(), period2.getStart())
+            && Objects.equals(period1.getEnd(), period2.getEnd());
+    }
+
     private void testPolicy(SystemUpdatePolicy policy) {
         mDevicePolicyManager.setSystemUpdatePolicy(getWho(), policy);
         waitForPolicyChangedBroadcast();
diff --git a/hostsidetests/devicepolicy/app/IntentReceiver/Android.mk b/hostsidetests/devicepolicy/app/IntentReceiver/Android.mk
index 60395c2..bb2393c 100644
--- a/hostsidetests/devicepolicy/app/IntentReceiver/Android.mk
+++ b/hostsidetests/devicepolicy/app/IntentReceiver/Android.mk
@@ -31,6 +31,7 @@
     ctstestrunner
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 19
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := arcts cts vts general-tests
diff --git a/hostsidetests/devicepolicy/app/IntentSender/Android.mk b/hostsidetests/devicepolicy/app/IntentSender/Android.mk
index 5cc3b7a..1551bea 100644
--- a/hostsidetests/devicepolicy/app/IntentSender/Android.mk
+++ b/hostsidetests/devicepolicy/app/IntentSender/Android.mk
@@ -36,6 +36,7 @@
     androidx.legacy_legacy-support-v4
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 19
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := arcts cts vts general-tests
diff --git a/hostsidetests/devicepolicy/app/LauncherTests/Android.mk b/hostsidetests/devicepolicy/app/LauncherTests/Android.mk
index 0475e30..4c0d206 100644
--- a/hostsidetests/devicepolicy/app/LauncherTests/Android.mk
+++ b/hostsidetests/devicepolicy/app/LauncherTests/Android.mk
@@ -35,6 +35,7 @@
 	testng
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 21
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/hostsidetests/devicepolicy/app/LauncherTestsSupport/Android.mk b/hostsidetests/devicepolicy/app/LauncherTestsSupport/Android.mk
index 932f691..51dea69 100644
--- a/hostsidetests/devicepolicy/app/LauncherTestsSupport/Android.mk
+++ b/hostsidetests/devicepolicy/app/LauncherTestsSupport/Android.mk
@@ -27,6 +27,7 @@
 LOCAL_JAVA_LIBRARIES := cts-junit
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 21
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/Android.mk b/hostsidetests/devicepolicy/app/ManagedProfile/Android.mk
index 6a02fd1..463b110 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/Android.mk
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/Android.mk
@@ -33,12 +33,14 @@
 	compatibility-device-util \
 	ub-uiautomator \
 	android-support-test \
-	guava
+	guava \
+	truth-prebuilt
 
 LOCAL_STATIC_ANDROID_LIBRARIES := \
     androidx.legacy_legacy-support-v4
 
 LOCAL_SDK_VERSION := test_current
+LOCAL_MIN_SDK_VERSION := 20
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := arcts cts vts general-tests
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/device_admin.xml b/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/device_admin.xml
index 30538af..b42c966 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/device_admin.xml
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/device_admin.xml
@@ -19,5 +19,7 @@
         <limit-password />
         <disable-keyguard-features/>
         <force-lock />
+        <expire-password />
+        <watch-login />
     </uses-policies>
 </device-admin>
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DevicePolicyManagerParentSupportTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DevicePolicyManagerParentSupportTest.java
new file mode 100644
index 0000000..9bd721f
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DevicePolicyManagerParentSupportTest.java
@@ -0,0 +1,151 @@
+package com.android.cts.managedprofile;
+
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.os.PersistableBundle;
+
+/**
+ * Tests that the {@link DevicePolicyManager} APIs that should work for {@link
+ * DevicePolicyManager#getParentProfileInstance(ComponentName)} are supported.
+ *
+ * <p>Minimum restriction APIs are already tested by {@link PasswordMinimumRestrictionsTest}.
+ */
+public class DevicePolicyManagerParentSupportTest extends BaseManagedProfileTest {
+    private static final ComponentName FAKE_COMPONENT = new ComponentName(
+            FakeComponent.class.getPackage().getName(), FakeComponent.class.getName());
+
+    public void testSetAndGetPasswordQuality_onParent() {
+        mParentDevicePolicyManager.setPasswordQuality(
+                ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_NUMERIC_COMPLEX);
+        final int actualPasswordQuality =
+                mParentDevicePolicyManager.getPasswordQuality(ADMIN_RECEIVER_COMPONENT);
+
+        assertThat(actualPasswordQuality).isEqualTo(PASSWORD_QUALITY_NUMERIC_COMPLEX);
+    }
+
+    public void testSetAndGetPasswordHistoryLength_onParent() {
+        final int passwordHistoryLength = 5;
+
+        mParentDevicePolicyManager.setPasswordHistoryLength(
+                ADMIN_RECEIVER_COMPONENT, passwordHistoryLength);
+        final int actualPasswordHistoryLength =
+                mParentDevicePolicyManager.getPasswordHistoryLength(ADMIN_RECEIVER_COMPONENT);
+
+        assertThat(actualPasswordHistoryLength).isEqualTo(passwordHistoryLength);
+    }
+
+    public void testSetAndGetPasswordExpirationTimeout_onParent() {
+        final int passwordExpirationTimeout = 432000000;
+
+        mParentDevicePolicyManager.setPasswordExpirationTimeout(
+                ADMIN_RECEIVER_COMPONENT, passwordExpirationTimeout);
+        final long actualPasswordExpirationTimeout =
+                mParentDevicePolicyManager.getPasswordExpirationTimeout(ADMIN_RECEIVER_COMPONENT);
+
+        assertThat(actualPasswordExpirationTimeout).isEqualTo(passwordExpirationTimeout);
+    }
+
+    public void testGetPasswordExpiration_onParent() {
+        final long passwordExpirationTimeout = 432000000;
+        final long currentTime = System.currentTimeMillis();
+
+        mParentDevicePolicyManager.setPasswordExpirationTimeout(
+                ADMIN_RECEIVER_COMPONENT, passwordExpirationTimeout);
+        final long actualPasswordExpiration =
+                mParentDevicePolicyManager.getPasswordExpiration(ADMIN_RECEIVER_COMPONENT);
+
+        assertThat(actualPasswordExpiration).isAtLeast(passwordExpirationTimeout + currentTime);
+    }
+
+    public void testGetMaximumPasswordLength_onParent() {
+        final int actualMaximumPasswordLength =
+                mParentDevicePolicyManager.getPasswordMaximumLength(
+                        PASSWORD_QUALITY_NUMERIC_COMPLEX);
+        assertThat(actualMaximumPasswordLength).isGreaterThan(0);
+    }
+
+    public void testIsActivePasswordSufficient_onParent_isSupported() {
+        setPasswordQuality(PASSWORD_QUALITY_NUMERIC_COMPLEX);
+        assertThat(mParentDevicePolicyManager.isActivePasswordSufficient()).isFalse();
+    }
+
+    private void setPasswordQuality(int quality) {
+        mParentDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT, quality);
+    }
+
+    public void testGetCurrentFailedPasswordAttempts_onParent_isSupported() {
+        assertThat(mParentDevicePolicyManager.getCurrentFailedPasswordAttempts()).isEqualTo(0);
+    }
+
+    public void testSetAndGetMaximumFailedPasswordsForWipe_onParent() {
+        final int maximumFailedPasswordsForWipe = 15;
+
+        mParentDevicePolicyManager.setMaximumFailedPasswordsForWipe(
+                ADMIN_RECEIVER_COMPONENT, maximumFailedPasswordsForWipe);
+        final int actualMaximumFailedPasswordsForWipe =
+                mParentDevicePolicyManager.getMaximumFailedPasswordsForWipe(
+                        ADMIN_RECEIVER_COMPONENT);
+
+        assertThat(actualMaximumFailedPasswordsForWipe).isEqualTo(maximumFailedPasswordsForWipe);
+    }
+
+    public void testSetAndGetMaximumTimeToLock_onParent() {
+        final int maximumTimeToLock = 6000;
+
+        mParentDevicePolicyManager.setMaximumTimeToLock(
+                ADMIN_RECEIVER_COMPONENT, maximumTimeToLock);
+        final long actualMaximumTimeToLock =
+                mParentDevicePolicyManager.getMaximumTimeToLock(ADMIN_RECEIVER_COMPONENT);
+
+        assertThat(actualMaximumTimeToLock).isEqualTo(maximumTimeToLock);
+    }
+
+    public void testLockNow_onParent_isSupported() {
+        mParentDevicePolicyManager.lockNow();
+        // Will fail if a SecurityException is thrown.
+    }
+
+    public void testSetAndGetKeyguardDisabledFeatures_onParent() {
+        mParentDevicePolicyManager.setKeyguardDisabledFeatures(
+                ADMIN_RECEIVER_COMPONENT, KEYGUARD_DISABLE_TRUST_AGENTS);
+        long actualKeyguardDisabledFeatures =
+                mParentDevicePolicyManager.getKeyguardDisabledFeatures(ADMIN_RECEIVER_COMPONENT);
+
+        assertThat(actualKeyguardDisabledFeatures).isEqualTo(KEYGUARD_DISABLE_TRUST_AGENTS);
+    }
+
+    public void testSetAndGetTrustAgentConfiguration_onParent() {
+        final PersistableBundle configuration = new PersistableBundle();
+        final String key = "key";
+        final String value = "value";
+        configuration.putString(key, value);
+
+        mParentDevicePolicyManager.setTrustAgentConfiguration(
+                ADMIN_RECEIVER_COMPONENT, FAKE_COMPONENT, configuration);
+        final PersistableBundle actualConfiguration =
+                mParentDevicePolicyManager.getTrustAgentConfiguration(
+                        ADMIN_RECEIVER_COMPONENT, FAKE_COMPONENT).get(0);
+
+        assertThat(actualConfiguration.get(key)).isEqualTo(value);
+    }
+
+    public void testSetAndGetRequiredStrongAuthTimeout_onParent() {
+        final int requiredStrongAuthTimeout = 4600000;
+
+        mParentDevicePolicyManager.setRequiredStrongAuthTimeout(
+                ADMIN_RECEIVER_COMPONENT, requiredStrongAuthTimeout);
+        final long actualRequiredStrongAuthTimeout =
+                mParentDevicePolicyManager.getRequiredStrongAuthTimeout(ADMIN_RECEIVER_COMPONENT);
+
+        assertThat(actualRequiredStrongAuthTimeout).isEqualTo(requiredStrongAuthTimeout);
+    }
+
+    public abstract class FakeComponent extends BroadcastReceiver {}
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/NotificationListenerTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/NotificationListenerTest.java
index 6986c39..c89ef13 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/NotificationListenerTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/NotificationListenerTest.java
@@ -15,13 +15,8 @@
  */
 package com.android.cts.managedprofile;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
 
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.UiAutomation;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -32,15 +27,19 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.uiautomator.UiDevice;
-import androidx.localbroadcastmanager.content.LocalBroadcastManager;
 import android.util.Log;
 
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+
+import com.google.common.collect.ImmutableList;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
 import java.io.IOException;
 import java.util.Collections;
+import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -107,16 +106,16 @@
         toggleNotificationListener(true);
 
         sendProfileNotification();
-        assertTrue(mReceiver.waitForNotificationPostedReceived());
+        assertThat(mReceiver.waitForNotificationPostedReceived()).isTrue();
         cancelProfileNotification();
-        assertTrue(mReceiver.waitForNotificationRemovedReceived());
+        assertThat(mReceiver.waitForNotificationRemovedReceived()).isTrue();
 
         mReceiver.reset();
 
         sendPersonalNotification();
-        assertTrue(mReceiver.waitForNotificationPostedReceived());
+        assertThat(mReceiver.waitForNotificationPostedReceived()).isTrue();
         cancelPersonalNotification();
-        assertTrue(mReceiver.waitForNotificationRemovedReceived());
+        assertThat(mReceiver.waitForNotificationRemovedReceived()).isTrue();
     }
 
     @Test
@@ -125,17 +124,29 @@
 
         sendProfileNotification();
         // Don't see notification or cancellation from work profile.
-        assertFalse(mReceiver.waitForNotificationPostedReceived());
+        assertThat(mReceiver.waitForNotificationPostedReceived()).isFalse();
         cancelProfileNotification();
-        assertFalse(mReceiver.waitForNotificationRemovedReceived());
+        assertThat(mReceiver.waitForNotificationRemovedReceived()).isFalse();
 
         mReceiver.reset();
 
         // Do see the one from the personal side.
         sendPersonalNotification();
-        assertTrue(mReceiver.waitForNotificationPostedReceived());
+        assertThat(mReceiver.waitForNotificationPostedReceived()).isTrue();
         cancelPersonalNotification();
-        assertTrue(mReceiver.waitForNotificationRemovedReceived());
+        assertThat(mReceiver.waitForNotificationRemovedReceived()).isTrue();
+    }
+
+    @Test
+    public void testSetAndGetPermittedCrossProfileNotificationListeners() {
+        List<String> packageList = ImmutableList.of("package1", "package2");
+
+        mDpm.setPermittedCrossProfileNotificationListeners(
+                BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT, packageList);
+        List<String> actualPackageList = mDpm.getPermittedCrossProfileNotificationListeners(
+                BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT);
+
+        assertThat(actualPackageList).isEqualTo(packageList);
     }
 
     private void cancelProfileNotification() throws IOException {
@@ -170,7 +181,7 @@
                 + testListener);
         Log.i(TAG, "Toggled notification listener state" + testListener + " to state " + enable);
         if (enable) {
-            assertTrue(mReceiver.waitForListenerConnected());
+            assertThat(mReceiver.waitForListenerConnected()).isTrue();
         }
     }
 
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PasswordMinimumRestrictionsTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PasswordMinimumRestrictionsTest.java
index 4f94674..8b7d16b 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PasswordMinimumRestrictionsTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PasswordMinimumRestrictionsTest.java
@@ -25,6 +25,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
+/** Tests minimum password restriction APIs, including on parent profile instances. */
 public class PasswordMinimumRestrictionsTest extends BaseManagedProfileTest {
 
     private static final int TEST_PASSWORD_LENGTH = 5;
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ProfileTimeoutTestHelper.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ProfileTimeoutTestHelper.java
index a386baa..7af838a 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ProfileTimeoutTestHelper.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ProfileTimeoutTestHelper.java
@@ -29,7 +29,8 @@
  */
 public class ProfileTimeoutTestHelper extends InstrumentationTestCase {
     // This should be sufficiently smaller than ManagedProfileTest.PROFILE_TIMEOUT_DELAY_SEC.
-    private static final int TIMEOUT_MS = 5_000;
+    // This should also be sufficiently larger than time required to run "input tap" on emulator.
+    private static final int TIMEOUT_MS = 30_000;
     private static final ComponentName ADMIN_COMPONENT = new ComponentName(
             BasicAdminReceiver.class.getPackage().getName(), BasicAdminReceiver.class.getName());
 
diff --git a/hostsidetests/devicepolicy/app/PackageInstaller/Android.mk b/hostsidetests/devicepolicy/app/PackageInstaller/Android.mk
index a1514ff..f60242c 100644
--- a/hostsidetests/devicepolicy/app/PackageInstaller/Android.mk
+++ b/hostsidetests/devicepolicy/app/PackageInstaller/Android.mk
@@ -32,6 +32,7 @@
     ub-uiautomator
 
 LOCAL_SDK_VERSION := test_current
+LOCAL_MIN_SDK_VERSION := 21
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := arcts cts vts general-tests
diff --git a/hostsidetests/devicepolicy/app/ProfileOwner/Android.mk b/hostsidetests/devicepolicy/app/ProfileOwner/Android.mk
index dd76558..1937da8 100644
--- a/hostsidetests/devicepolicy/app/ProfileOwner/Android.mk
+++ b/hostsidetests/devicepolicy/app/ProfileOwner/Android.mk
@@ -19,6 +19,8 @@
 LOCAL_PACKAGE_NAME := CtsProfileOwnerApp
 LOCAL_PRIVATE_PLATFORM_APIS := true
 
+LOCAL_MIN_SDK_VERSION := 24
+
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/Android.mk b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/Android.mk
index 8f39155..96e95ce 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/Android.mk
+++ b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/Android.mk
@@ -35,6 +35,7 @@
     testng
 
 LOCAL_SDK_VERSION := test_current
+LOCAL_MIN_SDK_VERSION := 24
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/AndroidManifest.xml b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/AndroidManifest.xml
index b721292..06ebcbc 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/AndroidManifest.xml
@@ -43,6 +43,15 @@
                 <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
             </intent-filter>
         </receiver>
+
+        <service
+            android:name="com.android.cts.transferowner.DeviceAndProfileOwnerTransferIncomingTest$BasicAdminService"
+            android:exported="true"
+            android:permission="android.permission.BIND_DEVICE_ADMIN">
+            <intent-filter>
+                <action android:name="android.app.action.DEVICE_ADMIN_SERVICE" />
+            </intent-filter>
+        </service>
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferIncomingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferIncomingTest.java
index 7a598d9..5d255e0e 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferIncomingTest.java
+++ b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferIncomingTest.java
@@ -19,11 +19,14 @@
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 
+import android.app.Service;
 import android.app.admin.DeviceAdminReceiver;
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.SharedPreferences;
+import android.os.IBinder;
 import android.os.PersistableBundle;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
@@ -44,12 +47,27 @@
         }
     }
 
+    public static class BasicAdminService extends Service {
+        @Override
+        public void onCreate() {
+            super.onCreate();
+            putBooleanPref(getApplicationContext(), KEY_TRANSFER_ADMIN_SERVICE_BOUND, true);
+        }
+
+        @Override
+        public IBinder onBind(Intent intent) {
+            return null;
+        }
+    }
+
     public static class BasicAdminReceiverNoMetadata extends DeviceAdminReceiver {
         public BasicAdminReceiverNoMetadata() {}
     }
 
     private final static String SHARED_PREFERENCE_NAME = "shared-preference-name";
     private final static String KEY_TRANSFER_COMPLETED_CALLED = "key-transfer-completed-called";
+    private final static String KEY_TRANSFER_ADMIN_SERVICE_BOUND =
+        "key-transfer-admin-service-bound";
     private final static String ARE_PARAMETERS_SAVED = "ARE_PARAMETERS_SAVED";
 
     protected Context mContext;
@@ -97,6 +115,11 @@
         assertTrue(bundle.isEmpty());
     }
 
+    @Test
+    public void testAdminServiceIsBound() {
+        assertTrue(getBooleanPref(mContext, KEY_TRANSFER_ADMIN_SERVICE_BOUND));
+    }
+
     private static SharedPreferences getPrefs(Context context) {
         return context.getSharedPreferences(SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE);
     }
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/Android.mk b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/Android.mk
index aeacb40..2ac7c0e 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/Android.mk
+++ b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/Android.mk
@@ -35,6 +35,7 @@
     testng
 
 LOCAL_SDK_VERSION := test_current
+LOCAL_MIN_SDK_VERSION := 24
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index ef51156..65d4517 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -94,6 +94,14 @@
     protected static final int FLAG_EPHEMERAL = 0x00000100;
     protected static final int FLAG_MANAGED_PROFILE = 0x00000020;
 
+    /**
+     * The {@link android.os.BatteryManager} flags value representing all charging types; {@link
+     * android.os.BatteryManager#BATTERY_PLUGGED_AC}, {@link
+     * android.os.BatteryManager#BATTERY_PLUGGED_USB}, and {@link
+     * android.os.BatteryManager#BATTERY_PLUGGED_WIRELESS}.
+     */
+    private static final int STAY_ON_WHILE_PLUGGED_IN_FLAGS = 7;
+
     protected static interface Settings {
         public static final String GLOBAL_NAMESPACE = "global";
         public static interface Global {
@@ -122,6 +130,8 @@
     /** Users we shouldn't delete in the tests */
     private ArrayList<Integer> mFixedUsers;
 
+    private static final String VERIFY_CREDENTIAL_CONFIRMATION = "Lock credential verified";
+
     @Override
     public void setBuild(IBuildInfo buildInfo) {
         mCtsBuild = buildInfo;
@@ -155,6 +165,7 @@
         removeTestUsers();
         // Unlock keyguard before test
         wakeupAndDismissKeyguard();
+        stayAwake();
         // Go to home.
         executeShellCommand("input keyevent KEYCODE_HOME");
     }
@@ -437,7 +448,7 @@
 
     /** Reboots the device and block until the boot complete flag is set. */
     protected void rebootAndWaitUntilReady() throws DeviceNotAvailableException {
-        getDevice().executeShellCommand("reboot");
+        getDevice().rebootUntilOnline();
         assertTrue("Device failed to boot", getDevice().waitForBootComplete(120000));
     }
 
@@ -866,20 +877,47 @@
     }
 
     /**
-     * Verifies the lock credential for the given user, which unlocks the user.
+     * Verifies the lock credential for the given user.
      *
      * @param credential The credential to verify.
      * @param userId The id of the user.
      */
     protected void verifyUserCredential(String credential, int userId)
             throws DeviceNotAvailableException {
+        String commandOutput = verifyUserCredentialCommandOutput(credential, userId);
+        if (!commandOutput.startsWith(VERIFY_CREDENTIAL_CONFIRMATION)) {
+            fail("Failed to verify user credential: " + commandOutput);
+        }
+     }
+
+    /**
+     * Verifies the lock credential for the given user, which unlocks the user, and returns
+     * whether it was successful or not.
+     *
+     * @param credential The credential to verify.
+     * @param userId The id of the user.
+     */
+    protected boolean verifyUserCredentialIsCorrect(String credential, int userId)
+            throws DeviceNotAvailableException {
+        String commandOutput = verifyUserCredentialCommandOutput(credential, userId);
+        return commandOutput.startsWith(VERIFY_CREDENTIAL_CONFIRMATION);
+    }
+
+    /**
+     * Verifies the lock credential for the given user, which unlocks the user. Returns the
+     * commandline output, which includes whether the verification was successful.
+     *
+     * @param credential The credential to verify.
+     * @param userId The id of the user.
+     * @return The command line output.
+     */
+    protected String verifyUserCredentialCommandOutput(String credential, int userId)
+            throws DeviceNotAvailableException {
         final String credentialArgument = (credential == null || credential.isEmpty())
                 ? "" : ("--old " + credential);
         String commandOutput = getDevice().executeShellCommand(String.format(
                 "cmd lock_settings verify --user %d %s", userId, credentialArgument));
-        if (!commandOutput.startsWith("Lock credential verified")) {
-            fail("Failed to verify user credential: " + commandOutput);
-        }
+        return commandOutput;
     }
 
     protected void wakeupAndDismissKeyguard() throws Exception {
@@ -887,6 +925,11 @@
         executeShellCommand("wm dismiss-keyguard");
     }
 
+    private void stayAwake() throws Exception {
+        executeShellCommand(
+                "settings put global stay_on_while_plugged_in " + STAY_ON_WHILE_PLUGGED_IN_FLAGS);
+    }
+
     protected void startActivityAsUser(int userId, String packageName, String activityName)
         throws Exception {
         wakeupAndDismissKeyguard();
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerHostSideTransferTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerHostSideTransferTest.java
index a18c772..997b831 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerHostSideTransferTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerHostSideTransferTest.java
@@ -3,6 +3,7 @@
 import com.android.tradefed.device.DeviceNotAvailableException;
 
 public abstract class DeviceAndProfileOwnerHostSideTransferTest extends BaseDevicePolicyTest {
+
     protected static final String TRANSFER_OWNER_OUTGOING_PKG =
             "com.android.cts.transferowneroutgoing";
     protected static final String TRANSFER_OWNER_OUTGOING_APK = "CtsTransferOwnerOutgoingApp.apk";
@@ -14,6 +15,10 @@
             "com.android.cts.transferownerincoming";
     protected static final String TRANSFER_OWNER_INCOMING_APK = "CtsTransferOwnerIncomingApp.apk";
     protected static final String INVALID_TARGET_APK = "CtsIntentReceiverApp.apk";
+    protected static final String TRANSFER_PROFILE_OWNER_OUTGOING_TEST =
+        "com.android.cts.transferowner.TransferProfileOwnerOutgoingTest";
+    protected static final String TRANSFER_PROFILE_OWNER_INCOMING_TEST =
+        "com.android.cts.transferowner.TransferProfileOwnerIncomingTest";
 
     protected int mUserId;
     protected String mOutgoingTestClassName;
@@ -191,9 +196,40 @@
                 mPrimaryUserId);
     }
 
+    public void testTargetDeviceAdminServiceBound() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        runDeviceTestsAsUser(TRANSFER_OWNER_OUTGOING_PKG,
+            mOutgoingTestClassName,
+            "testTransferOwnership", mUserId);
+        runDeviceTestsAsUser(TRANSFER_OWNER_INCOMING_PKG,
+            mIncomingTestClassName,
+            "testAdminServiceIsBound", mUserId);
+    }
+
+    protected void setSameAffiliationId(int profileUserId, String testClassName)
+        throws Exception {
+        runDeviceTestsAsUser(TRANSFER_OWNER_OUTGOING_PKG,
+            testClassName,
+            "testSetAffiliationId1", mPrimaryUserId);
+        runDeviceTestsAsUser(TRANSFER_OWNER_OUTGOING_PKG,
+            testClassName,
+            "testSetAffiliationId1", profileUserId);
+    }
+
+    protected void assertAffiliationIdsAreIntact(int profileUserId,
+        String testClassName) throws Exception {
+        runDeviceTestsAsUser(TRANSFER_OWNER_INCOMING_PKG,
+            testClassName,
+            "testIsAffiliationId1", mPrimaryUserId);
+        runDeviceTestsAsUser(TRANSFER_OWNER_INCOMING_PKG,
+            testClassName,
+            "testIsAffiliationId1", profileUserId);
+    }
+
     /* TODO: Add tests for:
-    * 1. startServiceForOwner
-    * 2. passwordOwner
+    * 1. passwordOwner
     *
     * */
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index fbe75d2..0bd74d1 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -28,7 +28,6 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 
 /**
  * Set of tests for use cases that apply to profile and device owner.
@@ -168,6 +167,19 @@
         executeDeviceTestClass(".CaCertManagementTest");
     }
 
+    public void testApplicationRestrictionIsRestricted() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        installAppAsUser(DELEGATE_APP_APK, mUserId);
+        runDeviceTestsAsUser(DELEGATE_APP_PKG, ".AppRestrictionsIsCallerDelegateHelper",
+            "testAssertCallerIsNotApplicationRestrictionsManagingPackage", mUserId);
+        runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".ApplicationRestrictionsIsCallerDelegateHelper",
+            "testSetApplicationRestrictionsManagingPackageToDelegate", mUserId);
+        runDeviceTestsAsUser(DELEGATE_APP_PKG, ".AppRestrictionsIsCallerDelegateHelper",
+            "testAssertCallerIsApplicationRestrictionsManagingPackage", mUserId);
+    }
+
     public void testApplicationRestrictions() throws Exception {
         if (!mHasFeature) {
             return;
@@ -609,6 +621,32 @@
         }
     }
 
+    // This test currently duplicates the testDelegatedCertInstaller, with one difference:
+    // The Delegated cert installer app is called directly rather than via intents from
+    // the DelegatedCertinstallerTest.
+    public void testDelegatedCertInstallerDirectly() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+
+        installAppAsUser(CERT_INSTALLER_APK, mUserId);
+
+        try {
+            // Set a non-empty device lockscreen password, which is a precondition for installing
+            // private key pairs.
+            changeUserCredential("1234", null, mUserId);
+
+            runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".DelegatedCertInstallerHelper",
+                    "testManualSetCertInstallerDelegate", mUserId);
+            runDeviceTestsAsUser("com.android.cts.certinstaller",
+                    ".DirectDelegatedCertInstallerTest", mUserId);
+            runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".DelegatedCertInstallerHelper",
+                    "testManualClearCertInstallerDelegate", mUserId);
+        } finally {
+            changeUserCredential(null, "1234", 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 {
@@ -844,6 +882,64 @@
         executeDeviceTestClass(".PasswordSufficientInitiallyTest");
     }
 
+    public void testGetCurrentFailedPasswordAttempts() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        final String testPassword = "1234";
+        final String wrongPassword = "12345";
+
+        changeUserCredential(testPassword, null /*oldCredential*/, mUserId);
+        try {
+            // Test that before trying an incorrect password there are 0 failed attempts.
+            executeDeviceTestMethod(".GetCurrentFailedPasswordAttemptsTest",
+                    "testNoFailedPasswordAttempts");
+            // Try an incorrect password.
+            assertFalse(verifyUserCredentialIsCorrect(wrongPassword, mUserId));
+            // Test that now there is one failed attempt.
+            executeDeviceTestMethod(".GetCurrentFailedPasswordAttemptsTest",
+                    "testOneFailedPasswordAttempt");
+            // Try an incorrect password.
+            assertFalse(verifyUserCredentialIsCorrect(wrongPassword, mUserId));
+            // Test that now there are two failed attempts.
+            executeDeviceTestMethod(".GetCurrentFailedPasswordAttemptsTest",
+                    "testTwoFailedPasswordAttempts");
+            // TODO: re-enable the test below when b/110945754 is fixed.
+            // Try the correct password and check the failed attempts number has been reset to 0.
+            // assertTrue(verifyUserCredentialIsCorrect(testPassword, mUserId));
+            // executeDeviceTestMethod(".GetCurrentFailedPasswordAttemptsTest",
+            //         "testNoFailedPasswordAttempts");
+        } finally {
+            changeUserCredential(null /*newCredential*/, testPassword, mUserId);
+        }
+    }
+
+    public void testPasswordExpiration() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        executeDeviceTestClass(".PasswordExpirationTest");
+    }
+
+    public void testGetPasswordExpiration() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        executeDeviceTestMethod(".GetPasswordExpirationTest",
+                "testGetPasswordExpiration");
+        try {
+            executeDeviceTestMethod(".GetPasswordExpirationTest",
+                    "testGetPasswordExpirationUpdatedAfterPasswordReset_beforeReset");
+            // Wait for 20 seconds so we can make sure that the expiration date is refreshed later.
+            Thread.sleep(20000);
+            changeUserCredential("1234", null, mUserId);
+            executeDeviceTestMethod(".GetPasswordExpirationTest",
+                    "testGetPasswordExpirationUpdatedAfterPasswordReset_afterReset");
+        } finally {
+            changeUserCredential(null, "1234", mUserId);
+        }
+    }
+
     public void testSetSystemSetting() throws Exception {
         if (!mHasFeature) {
             return;
@@ -915,6 +1011,29 @@
         }
     }
 
+    public void testPermittedAccessibilityServices() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+
+        executeDeviceTestClass(".AccessibilityServicesTest");
+    }
+
+    public void testPermittedInputMethods() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+
+        executeDeviceTestClass(".InputMethodsTest");
+    }
+
+    public void testSetStorageEncryption() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        executeDeviceTestClass(".StorageEncryptionTest");
+    }
+
     /**
      * Executes a test class on device. Prior to running, turn off background data usage
      * restrictions, and restore the original restrictions after the test.
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index 0146826..4c48a34 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -20,7 +20,6 @@
 import com.android.tradefed.device.DeviceNotAvailableException;
 
 import java.io.File;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -61,17 +60,9 @@
 
     private static final String ARG_NETWORK_LOGGING_BATCH_COUNT = "batchCount";
 
-    private final List<String> NO_SETUP_WIZARD_PROVISIONING_MODE =
-            Arrays.asList("DISABLED", "EMULATOR");
-
     /** CreateAndManageUser is available and an additional user can be created. */
     private boolean mHasCreateAndManageUserFeature;
 
-    /**
-     * Whether Setup Wizard is disabled.
-     */
-    private boolean mSetupWizardDisabled;
-
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -86,8 +77,6 @@
         }
         mHasCreateAndManageUserFeature = mHasFeature && canCreateAdditionalUsers(1)
                 && hasDeviceFeature("android.software.managed_users");
-        mSetupWizardDisabled = NO_SETUP_WIZARD_PROVISIONING_MODE.contains(
-                getDevice().getProperty("ro.setupwizard.mode"));
     }
 
     @Override
@@ -341,19 +330,10 @@
     }
 
     public void testCreateAndManageUser_SkipSetupWizard() throws Exception {
-        if (mSetupWizardDisabled || !mHasCreateAndManageUserFeature) {
-            return;
-        }
-        executeDeviceTestMethod(".CreateAndManageUserTest",
-                "testCreateAndManageUser_SkipSetupWizard");
-    }
-
-    public void testCreateAndManageUser_DontSkipSetupWizard() throws Exception {
-        if (mSetupWizardDisabled || !mHasCreateAndManageUserFeature) {
-            return;
-        }
-        executeDeviceTestMethod(".CreateAndManageUserTest",
-                "testCreateAndManageUser_DontSkipSetupWizard");
+        if (mHasCreateAndManageUserFeature) {
+            executeDeviceTestMethod(".CreateAndManageUserTest",
+                    "testCreateAndManageUser_SkipSetupWizard");
+       }
     }
 
     public void testCreateAndManageUser_AddRestrictionSet() throws Exception {
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DevicePlusProfileOwnerHostSideTransferTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DevicePlusProfileOwnerHostSideTransferTest.java
new file mode 100644
index 0000000..960fbb2
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DevicePlusProfileOwnerHostSideTransferTest.java
@@ -0,0 +1,45 @@
+package com.android.cts.devicepolicy;
+
+import android.util.Log;
+
+/**
+ * Tests the DPC transfer functionality for COMP mode - managed profile on top of a device owner.
+ * Testing is done by having two dummy DPCs, CtsTransferOwnerOutgoingApp and
+ * CtsTransferOwnerIncomingApp. The former is the current DPC and the latter will be the new DPC
+ * after transfer. In order to run the tests from the correct process, first we setup some
+ * policies in the client side in CtsTransferOwnerOutgoingApp and then we verify the policies are
+ * still there in CtsTransferOwnerIncomingApp. Note that these tests are run on the profile owner
+ * user.
+ */
+public class DevicePlusProfileOwnerHostSideTransferTest
+    extends DeviceAndProfileOwnerHostSideTransferTest {
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        // We need managed users to be supported in order to create a profile of the user owner.
+        mHasFeature &= hasDeviceFeature("android.software.managed_users");
+        if (mHasFeature) {
+            installAppAsUser(TRANSFER_OWNER_OUTGOING_APK, mPrimaryUserId);
+            // First set up the device owner.
+            if (setDeviceOwner(TRANSFER_OWNER_OUTGOING_TEST_RECEIVER, mPrimaryUserId,
+                false)) {
+                mOutgoingTestClassName = TRANSFER_PROFILE_OWNER_OUTGOING_TEST;
+                mIncomingTestClassName = TRANSFER_PROFILE_OWNER_INCOMING_TEST;
+
+                // And then set up the managed profile on top of it.
+                final int profileUserId = setupManagedProfileOnDeviceOwner(
+                    TRANSFER_OWNER_OUTGOING_APK, TRANSFER_OWNER_OUTGOING_TEST_RECEIVER);
+                setSameAffiliationId(profileUserId, TRANSFER_PROFILE_OWNER_OUTGOING_TEST);
+
+                installAppAsUser(TRANSFER_OWNER_INCOMING_APK, mPrimaryUserId);
+                installAppAsUser(TRANSFER_OWNER_INCOMING_APK, profileUserId);
+                mUserId = profileUserId;
+            } else {
+                removeAdmin(TRANSFER_OWNER_OUTGOING_TEST_RECEIVER, mUserId);
+                getDevice().uninstallPackage(TRANSFER_OWNER_OUTGOING_PKG);
+                fail("Failed to set device owner");
+            }
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 360e141..d1db198 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -85,7 +85,7 @@
 
     private static final String PROFILE_CREDENTIAL = "1234";
     // This should be sufficiently larger than ProfileTimeoutTestHelper.TIMEOUT_MS
-    private static final int PROFILE_TIMEOUT_DELAY_MS = 10_000;
+    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;
@@ -297,10 +297,12 @@
     }
 
     private void simulateUserInteraction(int timeMs) throws Exception {
+        final long endTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeMs);
         final UserActivityEmulator helper = new UserActivityEmulator(getDevice());
-        for (int i = 0; i < timeMs; i += timeMs/10) {
-            Thread.sleep(timeMs/10);
+        while (System.nanoTime() < endTime) {
             helper.tapScreenCenter();
+            // Just in case to prevent busy loop.
+            Thread.sleep(100);
         }
     }
 
@@ -598,6 +600,18 @@
                 Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
     }
 
+    public void testCrossProfileNotificationListeners_setAndGet() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        installAppAsUser(NOTIFICATION_APK, mProfileUserId);
+        installAppAsUser(NOTIFICATION_APK, mParentUserId);
+
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
+                "testSetAndGetPermittedCrossProfileNotificationListeners", mProfileUserId,
+                Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
+    }
+
     public void testCrossProfileCopyPaste() throws Exception {
         if (!mHasFeature) {
             return;
@@ -836,6 +850,14 @@
                 mProfileUserId);
     }
 
+    public void testDevicePolicyManagerParentSupport() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        runDeviceTestsAsUser(
+                MANAGED_PROFILE_PKG, ".DevicePolicyManagerParentSupportTest", mProfileUserId);
+    }
+
     public void testBluetoothContactSharingDisabled() throws Exception {
         if (!mHasFeature) {
             return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerHostSideTransferTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerHostSideTransferTest.java
index 7aefc33..fa0e60b 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerHostSideTransferTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerHostSideTransferTest.java
@@ -100,24 +100,4 @@
                 "testTransferAffiliatedProfileOwnershipCompleteCallbackIsCalled",
                 mUserId);
     }
-
-    private void assertAffiliationIdsAreIntact(int profileUserId,
-            String testClassName) throws Exception {
-        runDeviceTestsAsUser(TRANSFER_OWNER_INCOMING_PKG,
-                testClassName,
-                "testIsAffiliationId1", mPrimaryUserId);
-        runDeviceTestsAsUser(TRANSFER_OWNER_INCOMING_PKG,
-                testClassName,
-                "testIsAffiliationId1", profileUserId);
-    }
-
-    private void setSameAffiliationId(int profileUserId, String testClassName)
-            throws Exception {
-        runDeviceTestsAsUser(TRANSFER_OWNER_OUTGOING_PKG,
-                testClassName,
-                "testSetAffiliationId1", mPrimaryUserId);
-        runDeviceTestsAsUser(TRANSFER_OWNER_OUTGOING_PKG,
-                testClassName,
-                "testSetAffiliationId1", profileUserId);
-    }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
index 01e55fe..5317ecb 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
@@ -16,14 +16,6 @@
 
 package com.android.cts.devicepolicy;
 
-import android.platform.test.annotations.RequiresDevice;
-
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.log.LogUtil.CLog;
-
-import junit.framework.AssertionFailedError;
-
 /**
  * Set of tests for device owner use cases that also apply to profile owners.
  * Tests that should be run identically in both cases are added in DeviceAndProfileOwnerTest.
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
index b39e1ad..ba30f25 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
@@ -22,7 +22,7 @@
  */
 public class MixedManagedProfileOwnerTest extends DeviceAndProfileOwnerTest {
 
-    protected static final String CLEAR_PROFILE_OWNER_NEGATIVE_TEST_CLASS =
+    private static final String CLEAR_PROFILE_OWNER_NEGATIVE_TEST_CLASS =
             DEVICE_ADMIN_PKG + ".ClearProfileOwnerNegativeTest";
 
     private int mParentUserId = -1;
@@ -170,4 +170,11 @@
     public void testSetSystemSetting() {
         // Managed profile owner cannot set currently whitelisted system settings.
     }
+
+    public void testCannotClearProfileOwner() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        runDeviceTestsAsUser(DEVICE_ADMIN_PKG, CLEAR_PROFILE_OWNER_NEGATIVE_TEST_CLASS, mUserId);
+    }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerHostSideTransferTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerHostSideTransferTest.java
index 5143fb1..ca081fc 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerHostSideTransferTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerHostSideTransferTest.java
@@ -24,10 +24,6 @@
  */
 public class MixedProfileOwnerHostSideTransferTest extends
         DeviceAndProfileOwnerHostSideTransferTest {
-    private static final String TRANSFER_PROFILE_OWNER_OUTGOING_TEST =
-            "com.android.cts.transferowner.TransferProfileOwnerOutgoingTest";
-    private static final String TRANSFER_PROFILE_OWNER_INCOMING_TEST =
-            "com.android.cts.transferowner.TransferProfileOwnerIncomingTest";
 
     @Override
     protected void setUp() throws Exception {
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java
index 803cf36..e1d50bd 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java
@@ -16,8 +16,6 @@
 
 package com.android.cts.devicepolicy;
 
-import android.platform.test.annotations.RequiresDevice;
-
 /**
  * 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.
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTestApi25.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTestApi25.java
index b044441..0dcb82a 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTestApi25.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTestApi25.java
@@ -16,8 +16,6 @@
 
 package com.android.cts.devicepolicy;
 
-import android.platform.test.annotations.RequiresDevice;
-
 /**
  * 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 DeviceAndProfileOwnerTestApi25.
diff --git a/hostsidetests/dumpsys/apps/ProcStatsHelperApp/Android.mk b/hostsidetests/dumpsys/apps/ProcStatsHelperApp/Android.mk
index c6e8104..4ceccf1 100644
--- a/hostsidetests/dumpsys/apps/ProcStatsHelperApp/Android.mk
+++ b/hostsidetests/dumpsys/apps/ProcStatsHelperApp/Android.mk
@@ -32,6 +32,7 @@
     androidx.legacy_legacy-support-v4
 
 LOCAL_SDK_VERSION := test_current
+LOCAL_MIN_SDK_VERSION := 24
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/hostsidetests/edi/AndroidTest.xml b/hostsidetests/edi/AndroidTest.xml
index aef3086..7823b7e 100644
--- a/hostsidetests/edi/AndroidTest.xml
+++ b/hostsidetests/edi/AndroidTest.xml
@@ -15,7 +15,7 @@
 -->
 <configuration description="Config for CTS EDI host test cases">
     <option name="test-suite-tag" value="cts" />
-    <option name="config-descriptor:metadata" key="component" value="misc" />
+    <option name="config-descriptor:metadata" key="component" value="deviceinfo" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsEdiHostTestCases.jar" />
     </test>
diff --git a/hostsidetests/edi/OWNERS b/hostsidetests/edi/OWNERS
new file mode 100644
index 0000000..73518d9
--- /dev/null
+++ b/hostsidetests/edi/OWNERS
@@ -0,0 +1,4 @@
+aaronholden@google.com
+agathaman@google.com
+nickrose@google.com
+samlin@google.com
diff --git a/hostsidetests/gputools/apps/Android.mk b/hostsidetests/gputools/apps/Android.mk
index c913837..4e83540 100644
--- a/hostsidetests/gputools/apps/Android.mk
+++ b/hostsidetests/gputools/apps/Android.mk
@@ -19,7 +19,7 @@
 LOCAL_SRC_FILES := \
     jni/CtsGpuToolsJniOnLoad.cpp \
     jni/android_gputools_cts_RootlessGpuDebug.cpp
-LOCAL_CFLAGS += -std=c++14 -Wall -Werror
+LOCAL_CFLAGS += -Wall -Werror
 LOCAL_SHARED_LIBRARIES := libandroid libvulkan liblog
 LOCAL_NDK_STL_VARIANT := c++_static
 LOCAL_SDK_VERSION := current
diff --git a/hostsidetests/gputools/layers/Android.mk b/hostsidetests/gputools/layers/Android.mk
index 201fafd..cf85515 100644
--- a/hostsidetests/gputools/layers/Android.mk
+++ b/hostsidetests/gputools/layers/Android.mk
@@ -19,7 +19,7 @@
 LOCAL_MODULE := libVkLayer_nullLayerA
 LOCAL_MODULE_TAGS := tests
 LOCAL_SRC_FILES := jni/nullLayer.cpp
-LOCAL_CFLAGS += -std=c++14 -Wall -Werror -fvisibility=hidden -DLAYERNAME="A"
+LOCAL_CFLAGS += -Wall -Werror -fvisibility=hidden -DLAYERNAME="A"
 LOCAL_SHARED_LIBRARIES := libandroid libvulkan liblog
 LOCAL_NDK_STL_VARIANT := c++_static
 LOCAL_SDK_VERSION := current
@@ -30,7 +30,7 @@
 LOCAL_MODULE := libVkLayer_nullLayerB
 LOCAL_MODULE_TAGS := tests
 LOCAL_SRC_FILES := jni/nullLayer.cpp
-LOCAL_CFLAGS += -std=c++14 -Wall -Werror -fvisibility=hidden -DLAYERNAME="B"
+LOCAL_CFLAGS += -Wall -Werror -fvisibility=hidden -DLAYERNAME="B"
 LOCAL_SHARED_LIBRARIES := libandroid libvulkan liblog
 LOCAL_NDK_STL_VARIANT := c++_static
 LOCAL_SDK_VERSION := current
@@ -41,7 +41,7 @@
 LOCAL_MODULE := libVkLayer_nullLayerC
 LOCAL_MODULE_TAGS := tests
 LOCAL_SRC_FILES := jni/nullLayer.cpp
-LOCAL_CFLAGS += -std=c++14 -Wall -Werror -fvisibility=hidden -DLAYERNAME="C"
+LOCAL_CFLAGS += -Wall -Werror -fvisibility=hidden -DLAYERNAME="C"
 LOCAL_SHARED_LIBRARIES := libandroid libvulkan liblog
 LOCAL_NDK_STL_VARIANT := c++_static
 LOCAL_SDK_VERSION := current
diff --git a/hostsidetests/incident/apps/errorsapp/AndroidManifest.xml b/hostsidetests/incident/apps/errorsapp/AndroidManifest.xml
index 41fde5c..f472e900 100644
--- a/hostsidetests/incident/apps/errorsapp/AndroidManifest.xml
+++ b/hostsidetests/incident/apps/errorsapp/AndroidManifest.xml
@@ -20,6 +20,7 @@
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
     <uses-permission android:name="android.permission.READ_LOGS"/>
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/hostsidetests/incident/apps/errorsapp/src/com/android/server/cts/errors/ErrorsTests.java b/hostsidetests/incident/apps/errorsapp/src/com/android/server/cts/errors/ErrorsTests.java
index f5ce5d9..0c1ce19 100644
--- a/hostsidetests/incident/apps/errorsapp/src/com/android/server/cts/errors/ErrorsTests.java
+++ b/hostsidetests/incident/apps/errorsapp/src/com/android/server/cts/errors/ErrorsTests.java
@@ -26,13 +26,13 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 /**
  * Used by ErrorTest. Spawns misbehaving activities so reports will appear in Dropbox.
  */
@@ -57,6 +57,9 @@
         mDropbox = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);
         mResultsReceivedSignal = new CountDownLatch(1);
         mStartMs = System.currentTimeMillis();
+
+        InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+                "appops set " + mContext.getPackageName() + " android:get_usage_stats allow");
     }
 
     @Test
diff --git a/hostsidetests/incident/src/com/android/server/cts/BatteryIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/BatteryIncidentTest.java
index e48aeb1..c038eb2 100644
--- a/hostsidetests/incident/src/com/android/server/cts/BatteryIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/BatteryIncidentTest.java
@@ -28,7 +28,7 @@
     private static final String LEANBACK_FEATURE = "android.software.leanback";
 
     public void testBatteryServiceDump() throws Exception {
-        if (hasBattery(getDevice())) {
+        if (!hasBattery(getDevice())) {
             return;
         }
 
diff --git a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
index 65cb2a5..db7234f 100644
--- a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
@@ -350,21 +350,33 @@
         batteryOnScreenOff();
         installPackage(DEVICE_SIDE_TEST_APK, true);
         // Whitelist this app against background location request throttling
+        String origWhitelist = getDevice().executeShellCommand(
+                "settings get global location_background_throttle_package_whitelist").trim();
         getDevice().executeShellCommand(String.format(
                 "settings put global location_background_throttle_package_whitelist %s",
                 DEVICE_SIDE_TEST_PACKAGE));
 
-        // Background test.
-        executeBackground(ACTION_GPS, 60_000);
-        assertValueRange("sr", gpsSensorNumber, 6, 1, 1); // count
-        assertValueRange("sr", gpsSensorNumber, 7, 1, 1); // background_count
+        try {
+            // Background test.
+            executeBackground(ACTION_GPS, 60_000);
+            assertValueRange("sr", gpsSensorNumber, 6, 1, 1); // count
+            assertValueRange("sr", gpsSensorNumber, 7, 1, 1); // background_count
 
-        // Foreground test.
-        executeForeground(ACTION_GPS, 60_000);
-        assertValueRange("sr", gpsSensorNumber, 6, 2, 2); // count
-        assertValueRange("sr", gpsSensorNumber, 7, 1, 1); // background_count
-
-        batteryOffScreenOn();
+            // Foreground test.
+            executeForeground(ACTION_GPS, 60_000);
+            assertValueRange("sr", gpsSensorNumber, 6, 2, 2); // count
+            assertValueRange("sr", gpsSensorNumber, 7, 1, 1); // background_count
+        } finally {
+            if ("null".equals(origWhitelist) || "".equals(origWhitelist)) {
+                getDevice().executeShellCommand(
+                        "settings delete global location_background_throttle_package_whitelist");
+            } else {
+                getDevice().executeShellCommand(String.format(
+                        "settings put global location_background_throttle_package_whitelist %s",
+                        origWhitelist));
+            }
+            batteryOffScreenOn();
+        }
     }
 
     public void testJobBgVsFg() throws Exception {
diff --git a/hostsidetests/incident/src/com/android/server/cts/FingerprintIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/FingerprintIncidentTest.java
index 7f77466..05699c7 100644
--- a/hostsidetests/incident/src/com/android/server/cts/FingerprintIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/FingerprintIncidentTest.java
@@ -16,9 +16,9 @@
 
 package com.android.server.cts;
 
-import com.android.server.fingerprint.FingerprintServiceDumpProto;
-import com.android.server.fingerprint.FingerprintUserStatsProto;
-import com.android.server.fingerprint.PerformanceStatsProto;
+import com.android.server.biometrics.fingerprint.FingerprintServiceDumpProto;
+import com.android.server.biometrics.fingerprint.FingerprintUserStatsProto;
+import com.android.server.biometrics.fingerprint.PerformanceStatsProto;
 
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
diff --git a/hostsidetests/incident/src/com/android/server/cts/WindowManagerIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/WindowManagerIncidentTest.java
index ffce223..91e53d4 100644
--- a/hostsidetests/incident/src/com/android/server/cts/WindowManagerIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/WindowManagerIncidentTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.cts;
 
+import android.view.TransitionTypeEnum;
 import com.android.server.wm.AppTransitionProto;
 import com.android.server.wm.IdentifierProto;
 import com.android.server.wm.RootWindowContainerProto;
@@ -57,7 +58,7 @@
     private static void verifyAppTransitionProto(AppTransitionProto atp, final int filterLevel) throws Exception {
         assertTrue(AppTransitionProto.AppState.getDescriptor().getValues()
                 .contains(atp.getAppTransitionState().getValueDescriptor()));
-        assertTrue(AppTransitionProto.TransitionType.getDescriptor().getValues()
+        assertTrue(TransitionTypeEnum.getDescriptor().getValues()
                 .contains(atp.getLastUsedAppTransition().getValueDescriptor()));
     }
 }
diff --git a/hostsidetests/inputmethodservice/deviceside/devicetest/Android.mk b/hostsidetests/inputmethodservice/deviceside/devicetest/Android.mk
index 1727ad7..a85b4d2 100644
--- a/hostsidetests/inputmethodservice/deviceside/devicetest/Android.mk
+++ b/hostsidetests/inputmethodservice/deviceside/devicetest/Android.mk
@@ -41,5 +41,6 @@
 LOCAL_PACKAGE_NAME := CtsInputMethodServiceDeviceTests
 
 LOCAL_SDK_VERSION := test_current
+LOCAL_MIN_SDK_VERSION := 19
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/inputmethodservice/deviceside/edittextapp/Android.mk b/hostsidetests/inputmethodservice/deviceside/edittextapp/Android.mk
index 30fdb08..a089809 100644
--- a/hostsidetests/inputmethodservice/deviceside/edittextapp/Android.mk
+++ b/hostsidetests/inputmethodservice/deviceside/edittextapp/Android.mk
@@ -36,5 +36,6 @@
 LOCAL_PACKAGE_NAME := EditTextApp
 
 LOCAL_SDK_VERSION := test_current
+LOCAL_MIN_SDK_VERSION := 28
 
 include $(BUILD_PACKAGE)
diff --git a/hostsidetests/inputmethodservice/deviceside/ime1/Android.mk b/hostsidetests/inputmethodservice/deviceside/ime1/Android.mk
index 850b89f..3b98a87 100644
--- a/hostsidetests/inputmethodservice/deviceside/ime1/Android.mk
+++ b/hostsidetests/inputmethodservice/deviceside/ime1/Android.mk
@@ -37,5 +37,6 @@
 LOCAL_PACKAGE_NAME := CtsInputMethod1
 
 LOCAL_SDK_VERSION := test_current
+LOCAL_MIN_SDK_VERSION := 19
 
 include $(BUILD_PACKAGE)
diff --git a/hostsidetests/inputmethodservice/deviceside/ime2/Android.mk b/hostsidetests/inputmethodservice/deviceside/ime2/Android.mk
index fcd146c..7b9ebeb 100644
--- a/hostsidetests/inputmethodservice/deviceside/ime2/Android.mk
+++ b/hostsidetests/inputmethodservice/deviceside/ime2/Android.mk
@@ -37,5 +37,6 @@
 LOCAL_PACKAGE_NAME := CtsInputMethod2
 
 LOCAL_SDK_VERSION := test_current
+LOCAL_MIN_SDK_VERSION := 19
 
 include $(BUILD_PACKAGE)
diff --git a/hostsidetests/inputmethodservice/deviceside/provider/Android.mk b/hostsidetests/inputmethodservice/deviceside/provider/Android.mk
index b8f308d..365f792 100644
--- a/hostsidetests/inputmethodservice/deviceside/provider/Android.mk
+++ b/hostsidetests/inputmethodservice/deviceside/provider/Android.mk
@@ -36,5 +36,6 @@
 LOCAL_PACKAGE_NAME := CtsInputMethodServiceEventProvider
 
 LOCAL_SDK_VERSION := test_current
+LOCAL_MIN_SDK_VERSION := 19
 
 include $(BUILD_PACKAGE)
diff --git a/hostsidetests/media/app/MediaSessionTest/Android.mk b/hostsidetests/media/app/MediaSessionTest/Android.mk
index 174a744..5be80de 100644
--- a/hostsidetests/media/app/MediaSessionTest/Android.mk
+++ b/hostsidetests/media/app/MediaSessionTest/Android.mk
@@ -35,5 +35,6 @@
 
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 26
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/media/bitstreams/DynamicConfig.xml b/hostsidetests/media/bitstreams/DynamicConfig.xml
index 028e3a5..5350e9e 100644
--- a/hostsidetests/media/bitstreams/DynamicConfig.xml
+++ b/hostsidetests/media/bitstreams/DynamicConfig.xml
@@ -111,13 +111,13 @@
     <value>mime=video/avc,size=327741,width=1280,height=720,frame-rate=50,profile=1,level=2048,bitrate=4084648,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/levels/crowd_128x96p50f32_200kbps_level_2_bp.mp4">
-    <value>mime=video/avc,size=13018,width=128,height=96,frame-rate=50,profile=1,level=32,bitrate=150624,package=standard</value>
+    <value>mime=video/avc,size=13018,width=128,height=96,frame-rate=50,profile=1,level=32,bitrate=150624,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/levels/crowd_16x16p50f32_200kbps_level_1_bp.mp4">
     <value>mime=video/avc,size=5193,width=16,height=16,frame-rate=50,profile=1,level=1,bitrate=52824,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/levels/crowd_176x144p50f32_200kbps_level_2_bp.mp4">
-    <value>mime=video/avc,size=13097,width=176,height=144,frame-rate=50,profile=1,level=32,bitrate=151624,package=standard</value>
+    <value>mime=video/avc,size=13097,width=176,height=144,frame-rate=50,profile=1,level=32,bitrate=151624,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/levels/crowd_1920x1080p50f32_8200kbps_level_4.2_bp.mp4">
     <value>mime=video/avc,size=814724,width=1920,height=1080,frame-rate=50,profile=1,level=8192,bitrate=10171848,package=standard</value>
@@ -126,19 +126,19 @@
     <value>mime=video/avc,size=824354,width=1920,height=1088,frame-rate=50,profile=1,level=16384,bitrate=10292296,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/levels/crowd_352x288p50f32_400kbps_level_2.1_bp.mp4">
-    <value>mime=video/avc,size=28452,width=352,height=288,frame-rate=50,profile=1,level=64,bitrate=343472,package=standard</value>
+    <value>mime=video/avc,size=28452,width=352,height=288,frame-rate=50,profile=1,level=64,bitrate=343472,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/levels/crowd_3840x2160p50f32_24000kbps_level_5.2_bp.mp4">
     <value>mime=video/avc,size=2978730,width=3840,height=2160,frame-rate=50,profile=1,level=65536,bitrate=37221944,package=full</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/levels/crowd_640x480p50f32_1200kbps_level_3.1_bp.mp4">
-    <value>mime=video/avc,size=96734,width=640,height=480,frame-rate=50,profile=1,level=512,bitrate=1197000,package=standard</value>
+    <value>mime=video/avc,size=96734,width=640,height=480,frame-rate=50,profile=1,level=512,bitrate=1197000,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/levels/crowd_64x64p50f32_200kbps_level_2_bp.mp4">
-    <value>mime=video/avc,size=11979,width=64,height=64,frame-rate=50,profile=1,level=32,bitrate=137632,package=standard</value>
+    <value>mime=video/avc,size=11979,width=64,height=64,frame-rate=50,profile=1,level=32,bitrate=137632,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/levels/crowd_720x480p50f32_1300kbps_level_3_bp.mp4">
-    <value>mime=video/avc,size=105729,width=720,height=480,frame-rate=50,profile=1,level=256,bitrate=1309496,package=standard</value>
+    <value>mime=video/avc,size=105729,width=720,height=480,frame-rate=50,profile=1,level=256,bitrate=1309496,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/levels/crowd_720x576p50f32_1600kbps_level_3.1_bp.mp4">
     <value>mime=video/avc,size=132868,width=720,height=576,frame-rate=50,profile=1,level=512,bitrate=1648680,package=standard</value>
@@ -813,16 +813,16 @@
     <value>mime=video/avc,size=454069,width=1280,height=960,frame-rate=50,profile=1,level=2048,bitrate=5663744,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_128x128p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13123,width=128,height=128,frame-rate=50,profile=1,level=16,bitrate=151944,package=standard</value>
+    <value>mime=video/avc,size=13123,width=128,height=128,frame-rate=50,profile=1,level=16,bitrate=151944,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_128x192p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13547,width=128,height=192,frame-rate=50,profile=1,level=16,bitrate=157248,package=standard</value>
+    <value>mime=video/avc,size=13547,width=128,height=192,frame-rate=50,profile=1,level=16,bitrate=157248,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_128x72p50f32_200kbps_bp.mp4">
     <value>mime=video/avc,size=12585,width=128,height=72,frame-rate=50,profile=1,level=16,bitrate=145200,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_128x96p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13018,width=128,height=96,frame-rate=50,profile=1,level=16,bitrate=150624,package=standard</value>
+    <value>mime=video/avc,size=13018,width=128,height=96,frame-rate=50,profile=1,level=16,bitrate=150624,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_1440x1080p50f32_6200kbps_bp.mp4">
     <value>mime=video/avc,size=591640,width=1440,height=1080,frame-rate=50,profile=1,level=8192,bitrate=7383360,package=standard</value>
@@ -840,25 +840,25 @@
     <value>mime=video/avc,size=518511,width=1440,height=960,frame-rate=50,profile=1,level=8192,bitrate=6469272,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_144x144p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13183,width=144,height=144,frame-rate=50,profile=1,level=16,bitrate=152696,package=standard</value>
+    <value>mime=video/avc,size=13183,width=144,height=144,frame-rate=50,profile=1,level=16,bitrate=152696,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_144x176p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13598,width=144,height=176,frame-rate=50,profile=1,level=16,bitrate=157880,package=standard</value>
+    <value>mime=video/avc,size=13598,width=144,height=176,frame-rate=50,profile=1,level=16,bitrate=157880,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_144x192p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13579,width=144,height=192,frame-rate=50,profile=1,level=16,bitrate=157648,package=standard</value>
+    <value>mime=video/avc,size=13579,width=144,height=192,frame-rate=50,profile=1,level=16,bitrate=157648,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_144x256p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13643,width=144,height=256,frame-rate=50,profile=1,level=16,bitrate=158432,package=standard</value>
+    <value>mime=video/avc,size=13643,width=144,height=256,frame-rate=50,profile=1,level=16,bitrate=158432,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_144x96p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13137,width=144,height=96,frame-rate=50,profile=1,level=16,bitrate=152112,package=standard</value>
+    <value>mime=video/avc,size=13137,width=144,height=96,frame-rate=50,profile=1,level=16,bitrate=152112,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_1536x2048p50f32_12500kbps_bp.mp4">
     <value>mime=video/avc,size=1273812,width=1536,height=2048,frame-rate=50,profile=1,level=32768,bitrate=15910520,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_160x240p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13683,width=160,height=240,frame-rate=50,profile=1,level=16,bitrate=158944,package=standard</value>
+    <value>mime=video/avc,size=13683,width=160,height=240,frame-rate=50,profile=1,level=16,bitrate=158944,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_16x1080p50f32_200kbps_bp.mp4">
     <value>mime=video/avc,size=13341,width=16,height=1080,frame-rate=50,profile=1,level=64,bitrate=154656,package=standard</value>
@@ -876,13 +876,13 @@
     <value>mime=video/avc,size=13311,width=16,height=720,frame-rate=50,profile=1,level=16,bitrate=154280,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_176x144p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13097,width=176,height=144,frame-rate=50,profile=1,level=16,bitrate=151624,package=standard</value>
+    <value>mime=video/avc,size=13097,width=176,height=144,frame-rate=50,profile=1,level=16,bitrate=151624,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_176x176p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13645,width=176,height=176,frame-rate=50,profile=1,level=16,bitrate=158472,package=standard</value>
+    <value>mime=video/avc,size=13645,width=176,height=176,frame-rate=50,profile=1,level=16,bitrate=158472,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_176x96p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=12950,width=176,height=96,frame-rate=50,profile=1,level=16,bitrate=149768,package=standard</value>
+    <value>mime=video/avc,size=12950,width=176,height=96,frame-rate=50,profile=1,level=16,bitrate=149768,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_1920x1080p50f32_8200kbps_bp.mp4">
     <value>mime=video/avc,size=814719,width=1920,height=1080,frame-rate=50,profile=1,level=8192,bitrate=10171848,package=standard</value>
@@ -909,19 +909,19 @@
     <value>mime=video/avc,size=27567,width=1920,height=64,frame-rate=50,profile=1,level=512,bitrate=332472,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_192x128p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13034,width=192,height=128,frame-rate=50,profile=1,level=16,bitrate=150832,package=standard</value>
+    <value>mime=video/avc,size=13034,width=192,height=128,frame-rate=50,profile=1,level=16,bitrate=150832,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_192x144p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13134,width=192,height=144,frame-rate=50,profile=1,level=16,bitrate=152080,package=standard</value>
+    <value>mime=video/avc,size=13134,width=192,height=144,frame-rate=50,profile=1,level=16,bitrate=152080,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_192x192p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13553,width=192,height=192,frame-rate=50,profile=1,level=16,bitrate=157320,package=standard</value>
+    <value>mime=video/avc,size=13553,width=192,height=192,frame-rate=50,profile=1,level=16,bitrate=157320,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_192x256p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13572,width=192,height=256,frame-rate=50,profile=1,level=16,bitrate=157544,package=standard</value>
+    <value>mime=video/avc,size=13572,width=192,height=256,frame-rate=50,profile=1,level=16,bitrate=157544,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_192x288p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13479,width=192,height=288,frame-rate=50,profile=1,level=16,bitrate=156384,package=standard</value>
+    <value>mime=video/avc,size=13479,width=192,height=288,frame-rate=50,profile=1,level=16,bitrate=156384,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_2048x1152p50f32_9400kbps_bp.mp4">
     <value>mime=video/avc,size=911171,width=2048,height=1152,frame-rate=50,profile=1,level=16384,bitrate=11377512,package=standard</value>
@@ -942,13 +942,13 @@
     <value>mime=video/avc,size=2649479,width=2304,height=4096,frame-rate=50,profile=1,level=65536,bitrate=33106360,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_240x160p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13592,width=240,height=160,frame-rate=50,profile=1,level=16,bitrate=157808,package=standard</value>
+    <value>mime=video/avc,size=13592,width=240,height=160,frame-rate=50,profile=1,level=16,bitrate=157808,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_240x240p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13504,width=240,height=240,frame-rate=50,profile=1,level=16,bitrate=156712,package=standard</value>
+    <value>mime=video/avc,size=13504,width=240,height=240,frame-rate=50,profile=1,level=16,bitrate=156712,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_240x320p50f32_300kbps_bp.mp4">
-    <value>mime=video/avc,size=21313,width=240,height=320,frame-rate=50,profile=1,level=64,bitrate=254312,package=standard</value>
+    <value>mime=video/avc,size=21313,width=240,height=320,frame-rate=50,profile=1,level=64,bitrate=254312,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_240x360p50f32_300kbps_bp.mp4">
     <value>mime=video/avc,size=21460,width=240,height=360,frame-rate=50,profile=1,level=64,bitrate=256136,package=standard</value>
@@ -969,13 +969,13 @@
     <value>mime=video/avc,size=2658588,width=2560,height=3840,frame-rate=50,profile=1,level=65536,bitrate=33220232,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_256x144p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13556,width=256,height=144,frame-rate=50,profile=1,level=16,bitrate=157344,package=standard</value>
+    <value>mime=video/avc,size=13556,width=256,height=144,frame-rate=50,profile=1,level=16,bitrate=157344,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_256x192p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13510,width=256,height=192,frame-rate=50,profile=1,level=16,bitrate=156768,package=standard</value>
+    <value>mime=video/avc,size=13510,width=256,height=192,frame-rate=50,profile=1,level=16,bitrate=156768,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_256x256p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13449,width=256,height=256,frame-rate=50,profile=1,level=64,bitrate=156000,package=standard</value>
+    <value>mime=video/avc,size=13449,width=256,height=256,frame-rate=50,profile=1,level=64,bitrate=156000,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_264x352p50f32_300kbps_bp.mp4">
     <value>mime=video/avc,size=21344,width=264,height=352,frame-rate=50,profile=1,level=64,bitrate=254672,package=standard</value>
@@ -984,31 +984,31 @@
     <value>mime=video/avc,size=2716331,width=2880,height=3840,frame-rate=50,profile=1,level=65536,bitrate=33942024,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_288x192p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13354,width=288,height=192,frame-rate=50,profile=1,level=16,bitrate=154824,package=standard</value>
+    <value>mime=video/avc,size=13354,width=288,height=192,frame-rate=50,profile=1,level=16,bitrate=154824,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_288x216p50f32_200kbps_bp.mp4">
     <value>mime=video/avc,size=13576,width=288,height=216,frame-rate=50,profile=1,level=64,bitrate=157584,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_288x288p50f32_300kbps_bp.mp4">
-    <value>mime=video/avc,size=20762,width=288,height=288,frame-rate=50,profile=1,level=64,bitrate=247408,package=standard</value>
+    <value>mime=video/avc,size=20762,width=288,height=288,frame-rate=50,profile=1,level=64,bitrate=247408,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_288x352p50f32_400kbps_bp.mp4">
-    <value>mime=video/avc,size=28900,width=288,height=352,frame-rate=50,profile=1,level=64,bitrate=349136,package=standard</value>
+    <value>mime=video/avc,size=28900,width=288,height=352,frame-rate=50,profile=1,level=64,bitrate=349136,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_288x512p50f32_500kbps_bp.mp4">
-    <value>mime=video/avc,size=37103,width=288,height=512,frame-rate=50,profile=1,level=256,bitrate=451672,package=standard</value>
+    <value>mime=video/avc,size=37103,width=288,height=512,frame-rate=50,profile=1,level=256,bitrate=451672,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_3072x4096p50f32_24000kbps_bp.mp4">
     <value>mime=video/avc,size=2790761,width=3072,height=4096,frame-rate=50,profile=1,level=65536,bitrate=34872384,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_320x240p50f32_300kbps_bp.mp4">
-    <value>mime=video/avc,size=20979,width=320,height=240,frame-rate=50,profile=1,level=64,bitrate=250136,package=standard</value>
+    <value>mime=video/avc,size=20979,width=320,height=240,frame-rate=50,profile=1,level=64,bitrate=250136,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_320x320p50f32_400kbps_bp.mp4">
-    <value>mime=video/avc,size=29141,width=320,height=320,frame-rate=50,profile=1,level=128,bitrate=352144,package=standard</value>
+    <value>mime=video/avc,size=29141,width=320,height=320,frame-rate=50,profile=1,level=128,bitrate=352144,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_320x480p50f32_600kbps_bp.mp4">
-    <value>mime=video/avc,size=44862,width=320,height=480,frame-rate=50,profile=1,level=256,bitrate=548656,package=standard</value>
+    <value>mime=video/avc,size=44862,width=320,height=480,frame-rate=50,profile=1,level=256,bitrate=548656,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_32x24p50f32_200kbps_bp.mp4">
     <value>mime=video/avc,size=8809,width=32,height=24,frame-rate=50,profile=1,level=16,bitrate=98024,package=standard</value>
@@ -1023,10 +1023,10 @@
     <value>mime=video/avc,size=20950,width=352,height=264,frame-rate=50,profile=1,level=64,bitrate=249744,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_352x288p50f32_400kbps_bp.mp4">
-    <value>mime=video/avc,size=28447,width=352,height=288,frame-rate=50,profile=1,level=64,bitrate=343472,package=standard</value>
+    <value>mime=video/avc,size=28447,width=352,height=288,frame-rate=50,profile=1,level=64,bitrate=343472,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_352x352p50f32_400kbps_bp.mp4">
-    <value>mime=video/avc,size=29170,width=352,height=352,frame-rate=50,profile=1,level=256,bitrate=352512,package=standard</value>
+    <value>mime=video/avc,size=29170,width=352,height=352,frame-rate=50,profile=1,level=256,bitrate=352512,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_356x638p50f32_900kbps_bp.mp4">
     <value>mime=video/avc,size=70522,width=356,height=638,frame-rate=50,profile=1,level=512,bitrate=869408,package=standard</value>
@@ -1071,7 +1071,7 @@
     <value>mime=video/avc,size=2886460,width=3840,height=3840,frame-rate=50,profile=1,level=65536,bitrate=36068632,package=full</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_384x512p50f32_700kbps_bp.mp4">
-    <value>mime=video/avc,size=53478,width=384,height=512,frame-rate=50,profile=1,level=256,bitrate=656360,package=standard</value>
+    <value>mime=video/avc,size=53478,width=384,height=512,frame-rate=50,profile=1,level=256,bitrate=656360,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_4096x2304p50f32_24000kbps_bp.mp4">
     <value>mime=video/avc,size=2580560,width=4096,height=2304,frame-rate=50,profile=1,level=65536,bitrate=32244872,package=standard</value>
@@ -1083,19 +1083,19 @@
     <value>mime=video/avc,size=62178,width=472,height=472,frame-rate=50,profile=1,level=512,bitrate=765112,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_480x320p50f32_600kbps_bp.mp4">
-    <value>mime=video/avc,size=45288,width=480,height=320,frame-rate=50,profile=1,level=256,bitrate=553984,package=standard</value>
+    <value>mime=video/avc,size=45288,width=480,height=320,frame-rate=50,profile=1,level=256,bitrate=553984,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_480x360p50f32_600kbps_bp.mp4">
     <value>mime=video/avc,size=45502,width=480,height=360,frame-rate=50,profile=1,level=256,bitrate=556648,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_480x480p50f32_900kbps_bp.mp4">
-    <value>mime=video/avc,size=70488,width=480,height=480,frame-rate=50,profile=1,level=512,bitrate=868984,package=standard</value>
+    <value>mime=video/avc,size=70488,width=480,height=480,frame-rate=50,profile=1,level=512,bitrate=868984,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_480x640p50f32_1200kbps_bp.mp4">
-    <value>mime=video/avc,size=96257,width=480,height=640,frame-rate=50,profile=1,level=512,bitrate=1191096,package=standard</value>
+    <value>mime=video/avc,size=96257,width=480,height=640,frame-rate=50,profile=1,level=512,bitrate=1191096,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_480x720p50f32_1300kbps_bp.mp4">
-    <value>mime=video/avc,size=105435,width=480,height=720,frame-rate=50,profile=1,level=512,bitrate=1305824,package=standard</value>
+    <value>mime=video/avc,size=105435,width=480,height=720,frame-rate=50,profile=1,level=512,bitrate=1305824,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_480x848p50f32_1600kbps_bp.mp4">
     <value>mime=video/avc,size=130172,width=480,height=848,frame-rate=50,profile=1,level=512,bitrate=1615032,package=standard</value>
@@ -1104,10 +1104,10 @@
     <value>mime=video/avc,size=11955,width=48,height=64,frame-rate=50,profile=1,level=16,bitrate=137344,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_512x288p50f32_500kbps_bp.mp4">
-    <value>mime=video/avc,size=37248,width=512,height=288,frame-rate=50,profile=1,level=256,bitrate=453480,package=standard</value>
+    <value>mime=video/avc,size=37248,width=512,height=288,frame-rate=50,profile=1,level=256,bitrate=453480,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_512x384p50f32_700kbps_bp.mp4">
-    <value>mime=video/avc,size=53597,width=512,height=384,frame-rate=50,profile=1,level=256,bitrate=657848,package=standard</value>
+    <value>mime=video/avc,size=53597,width=512,height=384,frame-rate=50,profile=1,level=256,bitrate=657848,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_512x512p50f32_1000kbps_bp.mp4">
     <value>mime=video/avc,size=79003,width=512,height=512,frame-rate=50,profile=1,level=512,bitrate=975432,package=standard</value>
@@ -1149,7 +1149,7 @@
     <value>mime=video/avc,size=71029,width=640,height=360,frame-rate=50,profile=1,level=512,bitrate=875736,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_640x480p50f32_1200kbps_bp.mp4">
-    <value>mime=video/avc,size=96729,width=640,height=480,frame-rate=50,profile=1,level=512,bitrate=1197000,package=standard</value>
+    <value>mime=video/avc,size=96729,width=640,height=480,frame-rate=50,profile=1,level=512,bitrate=1197000,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_640x640p50f32_1600kbps_bp.mp4">
     <value>mime=video/avc,size=132674,width=640,height=640,frame-rate=50,profile=1,level=512,bitrate=1646320,package=standard</value>
@@ -1173,13 +1173,13 @@
     <value>mime=video/avc,size=11463,width=64,height=48,frame-rate=50,profile=1,level=16,bitrate=131200,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_64x64p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=11979,width=64,height=64,frame-rate=50,profile=1,level=16,bitrate=137632,package=standard</value>
+    <value>mime=video/avc,size=11979,width=64,height=64,frame-rate=50,profile=1,level=16,bitrate=137632,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_64x720p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13766,width=64,height=720,frame-rate=50,profile=1,level=16,bitrate=159968,package=standard</value>
+    <value>mime=video/avc,size=13766,width=64,height=720,frame-rate=50,profile=1,level=16,bitrate=159968,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_64x96p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=12767,width=64,height=96,frame-rate=50,profile=1,level=16,bitrate=147480,package=standard</value>
+    <value>mime=video/avc,size=12767,width=64,height=96,frame-rate=50,profile=1,level=16,bitrate=147480,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_704x528p50f32_1400kbps_bp.mp4">
     <value>mime=video/avc,size=114746,width=704,height=528,frame-rate=50,profile=1,level=512,bitrate=1422224,package=standard</value>
@@ -1197,13 +1197,13 @@
     <value>mime=video/avc,size=13146,width=720,height=16,frame-rate=50,profile=1,level=16,bitrate=152224,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_720x480p50f32_1300kbps_bp.mp4">
-    <value>mime=video/avc,size=105729,width=720,height=480,frame-rate=50,profile=1,level=512,bitrate=1309496,package=standard</value>
+    <value>mime=video/avc,size=105729,width=720,height=480,frame-rate=50,profile=1,level=512,bitrate=1309496,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_720x576p50f32_1600kbps_bp.mp4">
     <value>mime=video/avc,size=132863,width=720,height=576,frame-rate=50,profile=1,level=512,bitrate=1648680,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_720x64p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13556,width=720,height=64,frame-rate=50,profile=1,level=16,bitrate=157344,package=standard</value>
+    <value>mime=video/avc,size=13556,width=720,height=64,frame-rate=50,profile=1,level=16,bitrate=157344,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_720x720p50f32_2000kbps_bp.mp4">
     <value>mime=video/avc,size=170358,width=720,height=720,frame-rate=50,profile=1,level=512,bitrate=2117368,package=standard</value>
@@ -1254,22 +1254,22 @@
     <value>mime=video/avc,size=322999,width=960,height=960,frame-rate=50,profile=1,level=1024,bitrate=4025384,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_96x128p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13156,width=96,height=128,frame-rate=50,profile=1,level=16,bitrate=152344,package=standard</value>
+    <value>mime=video/avc,size=13156,width=96,height=128,frame-rate=50,profile=1,level=16,bitrate=152344,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_96x144p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13301,width=96,height=144,frame-rate=50,profile=1,level=16,bitrate=154160,package=standard</value>
+    <value>mime=video/avc,size=13301,width=96,height=144,frame-rate=50,profile=1,level=16,bitrate=154160,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_96x176p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13579,width=96,height=176,frame-rate=50,profile=1,level=16,bitrate=157632,package=standard</value>
+    <value>mime=video/avc,size=13579,width=96,height=176,frame-rate=50,profile=1,level=16,bitrate=157632,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_96x64p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=12221,width=96,height=64,frame-rate=50,profile=1,level=16,bitrate=140656,package=standard</value>
+    <value>mime=video/avc,size=12221,width=96,height=64,frame-rate=50,profile=1,level=16,bitrate=140656,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_96x72p50f32_200kbps_bp.mp4">
     <value>mime=video/avc,size=12682,width=96,height=72,frame-rate=50,profile=1,level=16,bitrate=146408,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/resolutions/crowd_96x96p50f32_200kbps_bp.mp4">
-    <value>mime=video/avc,size=13050,width=96,height=96,frame-rate=50,profile=1,level=16,bitrate=151024,package=standard</value>
+    <value>mime=video/avc,size=13050,width=96,height=96,frame-rate=50,profile=1,level=16,bitrate=151024,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/bp/slices/crowd_1280x720p50f32/crowd_1280x720p50f32_1024byte_slices_bp.mp4">
     <value>mime=video/avc,size=276000,width=1280,height=720,frame-rate=50,profile=1,level=1024,bitrate=3437880,package=standard</value>
@@ -4341,13 +4341,13 @@
     <value>mime=video/avc,size=319856,width=1280,height=720,frame-rate=50,profile=2,level=2048,bitrate=3982320,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/levels/crowd_128x96p50f32_200kbps_level_2_mp.mp4">
-    <value>mime=video/avc,size=12333,width=128,height=96,frame-rate=50,profile=2,level=32,bitrate=139320,package=standard</value>
+    <value>mime=video/avc,size=12333,width=128,height=96,frame-rate=50,profile=2,level=32,bitrate=139320,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/levels/crowd_16x16p50f32_200kbps_level_1_mp.mp4">
     <value>mime=video/avc,size=5415,width=16,height=16,frame-rate=50,profile=2,level=1,bitrate=54544,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/levels/crowd_176x144p50f32_200kbps_level_2_mp.mp4">
-    <value>mime=video/avc,size=12174,width=176,height=144,frame-rate=50,profile=2,level=32,bitrate=136824,package=standard</value>
+    <value>mime=video/avc,size=12174,width=176,height=144,frame-rate=50,profile=2,level=32,bitrate=136824,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/levels/crowd_1920x1080p50f32_8200kbps_level_4.2_mp.mp4">
     <value>mime=video/avc,size=821392,width=1920,height=1080,frame-rate=50,profile=2,level=8192,bitrate=10251448,package=standard</value>
@@ -4356,19 +4356,19 @@
     <value>mime=video/avc,size=829236,width=1920,height=1088,frame-rate=50,profile=2,level=16384,bitrate=10349568,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/levels/crowd_352x288p50f32_400kbps_level_2.1_mp.mp4">
-    <value>mime=video/avc,size=26857,width=352,height=288,frame-rate=50,profile=2,level=64,bitrate=320680,package=standard</value>
+    <value>mime=video/avc,size=26857,width=352,height=288,frame-rate=50,profile=2,level=64,bitrate=320680,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/levels/crowd_3840x2160p50f32_24000kbps_level_5.2_mp.mp4">
     <value>mime=video/avc,size=3353147,width=3840,height=2160,frame-rate=50,profile=2,level=65536,bitrate=41898400,package=full</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/levels/crowd_640x480p50f32_1200kbps_level_3.1_mp.mp4">
-    <value>mime=video/avc,size=91490,width=640,height=480,frame-rate=50,profile=2,level=512,bitrate=1127808,package=standard</value>
+    <value>mime=video/avc,size=91490,width=640,height=480,frame-rate=50,profile=2,level=512,bitrate=1127808,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/levels/crowd_64x64p50f32_200kbps_level_2_mp.mp4">
-    <value>mime=video/avc,size=11818,width=64,height=64,frame-rate=50,profile=2,level=32,bitrate=135624,package=standard</value>
+    <value>mime=video/avc,size=11818,width=64,height=64,frame-rate=50,profile=2,level=32,bitrate=135624,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/levels/crowd_720x480p50f32_1300kbps_level_3_mp.mp4">
-    <value>mime=video/avc,size=100078,width=720,height=480,frame-rate=50,profile=2,level=256,bitrate=1235024,package=standard</value>
+    <value>mime=video/avc,size=100078,width=720,height=480,frame-rate=50,profile=2,level=256,bitrate=1235024,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/levels/crowd_720x576p50f32_1600kbps_level_3.1_mp.mp4">
     <value>mime=video/avc,size=125751,width=720,height=576,frame-rate=50,profile=2,level=512,bitrate=1555960,package=standard</value>
@@ -5352,16 +5352,16 @@
     <value>mime=video/avc,size=446270,width=1280,height=960,frame-rate=50,profile=2,level=2048,bitrate=5562496,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_128x128p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12097,width=128,height=128,frame-rate=50,profile=2,level=16,bitrate=135560,package=standard</value>
+    <value>mime=video/avc,size=12097,width=128,height=128,frame-rate=50,profile=2,level=16,bitrate=135560,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_128x192p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12424,width=128,height=192,frame-rate=50,profile=2,level=16,bitrate=139648,package=standard</value>
+    <value>mime=video/avc,size=12424,width=128,height=192,frame-rate=50,profile=2,level=16,bitrate=139648,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_128x72p50f32_200kbps_mp.mp4">
     <value>mime=video/avc,size=12180,width=128,height=72,frame-rate=50,profile=2,level=16,bitrate=137200,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_128x96p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12333,width=128,height=96,frame-rate=50,profile=2,level=16,bitrate=139320,package=standard</value>
+    <value>mime=video/avc,size=12333,width=128,height=96,frame-rate=50,profile=2,level=16,bitrate=139320,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_1440x1080p50f32_6200kbps_mp.mp4">
     <value>mime=video/avc,size=587354,width=1440,height=1080,frame-rate=50,profile=2,level=8192,bitrate=7326032,package=standard</value>
@@ -5379,25 +5379,25 @@
     <value>mime=video/avc,size=512376,width=1440,height=960,frame-rate=50,profile=2,level=8192,bitrate=6388824,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_144x144p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=11994,width=144,height=144,frame-rate=50,profile=2,level=16,bitrate=134672,package=standard</value>
+    <value>mime=video/avc,size=11994,width=144,height=144,frame-rate=50,profile=2,level=16,bitrate=134672,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_144x176p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12433,width=144,height=176,frame-rate=50,profile=2,level=16,bitrate=139856,package=standard</value>
+    <value>mime=video/avc,size=12433,width=144,height=176,frame-rate=50,profile=2,level=16,bitrate=139856,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_144x192p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12328,width=144,height=192,frame-rate=50,profile=2,level=16,bitrate=138648,package=standard</value>
+    <value>mime=video/avc,size=12328,width=144,height=192,frame-rate=50,profile=2,level=16,bitrate=138648,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_144x256p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12710,width=144,height=256,frame-rate=50,profile=2,level=16,bitrate=143512,package=standard</value>
+    <value>mime=video/avc,size=12710,width=144,height=256,frame-rate=50,profile=2,level=16,bitrate=143512,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_144x96p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12152,width=144,height=96,frame-rate=50,profile=2,level=16,bitrate=137056,package=standard</value>
+    <value>mime=video/avc,size=12152,width=144,height=96,frame-rate=50,profile=2,level=16,bitrate=137056,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_1536x2048p50f32_12500kbps_mp.mp4">
     <value>mime=video/avc,size=1314463,width=1536,height=2048,frame-rate=50,profile=2,level=32768,bitrate=16414920,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_160x240p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12753,width=160,height=240,frame-rate=50,profile=2,level=16,bitrate=143960,package=standard</value>
+    <value>mime=video/avc,size=12753,width=160,height=240,frame-rate=50,profile=2,level=16,bitrate=143960,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_16x1080p50f32_200kbps_mp.mp4">
     <value>mime=video/avc,size=12347,width=16,height=1080,frame-rate=50,profile=2,level=64,bitrate=138768,package=standard</value>
@@ -5415,13 +5415,13 @@
     <value>mime=video/avc,size=12456,width=16,height=720,frame-rate=50,profile=2,level=16,bitrate=140656,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_176x144p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12174,width=176,height=144,frame-rate=50,profile=2,level=16,bitrate=136824,package=standard</value>
+    <value>mime=video/avc,size=12174,width=176,height=144,frame-rate=50,profile=2,level=16,bitrate=136824,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_176x176p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12523,width=176,height=176,frame-rate=50,profile=2,level=16,bitrate=140984,package=standard</value>
+    <value>mime=video/avc,size=12523,width=176,height=176,frame-rate=50,profile=2,level=16,bitrate=140984,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_176x96p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12373,width=176,height=96,frame-rate=50,profile=2,level=16,bitrate=140120,package=standard</value>
+    <value>mime=video/avc,size=12373,width=176,height=96,frame-rate=50,profile=2,level=16,bitrate=140120,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_1920x1080p50f32_8200kbps_mp.mp4">
     <value>mime=video/avc,size=821387,width=1920,height=1080,frame-rate=50,profile=2,level=8192,bitrate=10251448,package=standard</value>
@@ -5448,19 +5448,19 @@
     <value>mime=video/avc,size=24539,width=1920,height=64,frame-rate=50,profile=2,level=512,bitrate=290872,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_192x128p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12219,width=192,height=128,frame-rate=50,profile=2,level=16,bitrate=137280,package=standard</value>
+    <value>mime=video/avc,size=12219,width=192,height=128,frame-rate=50,profile=2,level=16,bitrate=137280,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_192x144p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12593,width=192,height=144,frame-rate=50,profile=2,level=16,bitrate=143256,package=standard</value>
+    <value>mime=video/avc,size=12593,width=192,height=144,frame-rate=50,profile=2,level=16,bitrate=143256,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_192x192p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12501,width=192,height=192,frame-rate=50,profile=2,level=16,bitrate=140408,package=standard</value>
+    <value>mime=video/avc,size=12501,width=192,height=192,frame-rate=50,profile=2,level=16,bitrate=140408,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_192x256p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12863,width=192,height=256,frame-rate=50,profile=2,level=16,bitrate=145320,package=standard</value>
+    <value>mime=video/avc,size=12863,width=192,height=256,frame-rate=50,profile=2,level=16,bitrate=145320,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_192x288p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12650,width=192,height=288,frame-rate=50,profile=2,level=16,bitrate=142456,package=standard</value>
+    <value>mime=video/avc,size=12650,width=192,height=288,frame-rate=50,profile=2,level=16,bitrate=142456,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_2048x1152p50f32_9400kbps_mp.mp4">
     <value>mime=video/avc,size=917154,width=2048,height=1152,frame-rate=50,profile=2,level=16384,bitrate=11448560,package=standard</value>
@@ -5481,13 +5481,13 @@
     <value>mime=video/avc,size=2777432,width=2304,height=4096,frame-rate=50,profile=2,level=65536,bitrate=34702008,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_240x160p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12596,width=240,height=160,frame-rate=50,profile=2,level=16,bitrate=141696,package=standard</value>
+    <value>mime=video/avc,size=12596,width=240,height=160,frame-rate=50,profile=2,level=16,bitrate=141696,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_240x240p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12790,width=240,height=240,frame-rate=50,profile=2,level=16,bitrate=144424,package=standard</value>
+    <value>mime=video/avc,size=12790,width=240,height=240,frame-rate=50,profile=2,level=16,bitrate=144424,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_240x320p50f32_300kbps_mp.mp4">
-    <value>mime=video/avc,size=19720,width=240,height=320,frame-rate=50,profile=2,level=64,bitrate=230832,package=standard</value>
+    <value>mime=video/avc,size=19720,width=240,height=320,frame-rate=50,profile=2,level=64,bitrate=230832,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_240x360p50f32_300kbps_mp.mp4">
     <value>mime=video/avc,size=19801,width=240,height=360,frame-rate=50,profile=2,level=64,bitrate=231736,package=standard</value>
@@ -5508,13 +5508,13 @@
     <value>mime=video/avc,size=2796926,width=2560,height=3840,frame-rate=50,profile=2,level=65536,bitrate=34945696,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_256x144p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12529,width=256,height=144,frame-rate=50,profile=2,level=16,bitrate=140744,package=standard</value>
+    <value>mime=video/avc,size=12529,width=256,height=144,frame-rate=50,profile=2,level=16,bitrate=140744,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_256x192p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12550,width=256,height=192,frame-rate=50,profile=2,level=16,bitrate=141312,package=standard</value>
+    <value>mime=video/avc,size=12550,width=256,height=192,frame-rate=50,profile=2,level=16,bitrate=141312,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_256x256p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12620,width=256,height=256,frame-rate=50,profile=2,level=64,bitrate=142384,package=standard</value>
+    <value>mime=video/avc,size=12620,width=256,height=256,frame-rate=50,profile=2,level=64,bitrate=142384,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_264x352p50f32_300kbps_mp.mp4">
     <value>mime=video/avc,size=19625,width=264,height=352,frame-rate=50,profile=2,level=64,bitrate=229536,package=standard</value>
@@ -5523,28 +5523,28 @@
     <value>mime=video/avc,size=2880482,width=2880,height=3840,frame-rate=50,profile=2,level=65536,bitrate=35990144,package=full</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_288x192p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12507,width=288,height=192,frame-rate=50,profile=2,level=16,bitrate=140672,package=standard</value>
+    <value>mime=video/avc,size=12507,width=288,height=192,frame-rate=50,profile=2,level=16,bitrate=140672,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_288x216p50f32_200kbps_mp.mp4">
     <value>mime=video/avc,size=12750,width=288,height=216,frame-rate=50,profile=2,level=64,bitrate=144096,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_288x288p50f32_300kbps_mp.mp4">
-    <value>mime=video/avc,size=19640,width=288,height=288,frame-rate=50,profile=2,level=64,bitrate=230232,package=standard</value>
+    <value>mime=video/avc,size=19640,width=288,height=288,frame-rate=50,profile=2,level=64,bitrate=230232,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_288x352p50f32_400kbps_mp.mp4">
-    <value>mime=video/avc,size=26931,width=288,height=352,frame-rate=50,profile=2,level=64,bitrate=321072,package=standard</value>
+    <value>mime=video/avc,size=26931,width=288,height=352,frame-rate=50,profile=2,level=64,bitrate=321072,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_288x512p50f32_500kbps_mp.mp4">
-    <value>mime=video/avc,size=34424,width=288,height=512,frame-rate=50,profile=2,level=256,bitrate=414544,package=standard</value>
+    <value>mime=video/avc,size=34424,width=288,height=512,frame-rate=50,profile=2,level=256,bitrate=414544,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_320x240p50f32_300kbps_mp.mp4">
-    <value>mime=video/avc,size=19675,width=320,height=240,frame-rate=50,profile=2,level=64,bitrate=230272,package=standard</value>
+    <value>mime=video/avc,size=19675,width=320,height=240,frame-rate=50,profile=2,level=64,bitrate=230272,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_320x320p50f32_400kbps_mp.mp4">
-    <value>mime=video/avc,size=27750,width=320,height=320,frame-rate=50,profile=2,level=128,bitrate=331808,package=standard</value>
+    <value>mime=video/avc,size=27750,width=320,height=320,frame-rate=50,profile=2,level=128,bitrate=331808,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_320x480p50f32_600kbps_mp.mp4">
-    <value>mime=video/avc,size=41960,width=320,height=480,frame-rate=50,profile=2,level=256,bitrate=508832,package=standard</value>
+    <value>mime=video/avc,size=41960,width=320,height=480,frame-rate=50,profile=2,level=256,bitrate=508832,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_32x24p50f32_200kbps_mp.mp4">
     <value>mime=video/avc,size=8516,width=32,height=24,frame-rate=50,profile=2,level=16,bitrate=94344,package=standard</value>
@@ -5559,10 +5559,10 @@
     <value>mime=video/avc,size=19489,width=352,height=264,frame-rate=50,profile=2,level=64,bitrate=228432,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_352x288p50f32_400kbps_mp.mp4">
-    <value>mime=video/avc,size=26852,width=352,height=288,frame-rate=50,profile=2,level=64,bitrate=320680,package=standard</value>
+    <value>mime=video/avc,size=26852,width=352,height=288,frame-rate=50,profile=2,level=64,bitrate=320680,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_352x352p50f32_400kbps_mp.mp4">
-    <value>mime=video/avc,size=27834,width=352,height=352,frame-rate=50,profile=2,level=256,bitrate=332560,package=standard</value>
+    <value>mime=video/avc,size=27834,width=352,height=352,frame-rate=50,profile=2,level=256,bitrate=332560,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_356x638p50f32_900kbps_mp.mp4">
     <value>mime=video/avc,size=65821,width=356,height=638,frame-rate=50,profile=2,level=512,bitrate=806880,package=standard</value>
@@ -5604,7 +5604,7 @@
     <value>mime=video/avc,size=2837257,width=3840,height=2880,frame-rate=50,profile=2,level=65536,bitrate=35449832,package=full</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_384x512p50f32_700kbps_mp.mp4">
-    <value>mime=video/avc,size=50438,width=384,height=512,frame-rate=50,profile=2,level=256,bitrate=614720,package=standard</value>
+    <value>mime=video/avc,size=50438,width=384,height=512,frame-rate=50,profile=2,level=256,bitrate=614720,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_4096x2304p50f32_24000kbps_mp.mp4">
     <value>mime=video/avc,size=2713484,width=4096,height=2304,frame-rate=50,profile=2,level=65536,bitrate=33902656,package=standard</value>
@@ -5613,19 +5613,19 @@
     <value>mime=video/avc,size=58830,width=472,height=472,frame-rate=50,profile=2,level=512,bitrate=719800,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_480x320p50f32_600kbps_mp.mp4">
-    <value>mime=video/avc,size=42865,width=480,height=320,frame-rate=50,profile=2,level=256,bitrate=520344,package=standard</value>
+    <value>mime=video/avc,size=42865,width=480,height=320,frame-rate=50,profile=2,level=256,bitrate=520344,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_480x360p50f32_600kbps_mp.mp4">
     <value>mime=video/avc,size=42938,width=480,height=360,frame-rate=50,profile=2,level=256,bitrate=521144,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_480x480p50f32_900kbps_mp.mp4">
-    <value>mime=video/avc,size=66588,width=480,height=480,frame-rate=50,profile=2,level=512,bitrate=816784,package=standard</value>
+    <value>mime=video/avc,size=66588,width=480,height=480,frame-rate=50,profile=2,level=512,bitrate=816784,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_480x640p50f32_1200kbps_mp.mp4">
-    <value>mime=video/avc,size=90927,width=480,height=640,frame-rate=50,profile=2,level=512,bitrate=1120736,package=standard</value>
+    <value>mime=video/avc,size=90927,width=480,height=640,frame-rate=50,profile=2,level=512,bitrate=1120736,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_480x720p50f32_1300kbps_mp.mp4">
-    <value>mime=video/avc,size=100070,width=480,height=720,frame-rate=50,profile=2,level=512,bitrate=1235024,package=standard</value>
+    <value>mime=video/avc,size=100070,width=480,height=720,frame-rate=50,profile=2,level=512,bitrate=1235024,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_480x848p50f32_1600kbps_mp.mp4">
     <value>mime=video/avc,size=121917,width=480,height=848,frame-rate=50,profile=2,level=512,bitrate=1508112,package=standard</value>
@@ -5634,10 +5634,10 @@
     <value>mime=video/avc,size=11093,width=48,height=64,frame-rate=50,profile=2,level=16,bitrate=124608,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_512x288p50f32_500kbps_mp.mp4">
-    <value>mime=video/avc,size=35423,width=512,height=288,frame-rate=50,profile=2,level=256,bitrate=427632,package=standard</value>
+    <value>mime=video/avc,size=35423,width=512,height=288,frame-rate=50,profile=2,level=256,bitrate=427632,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_512x384p50f32_700kbps_mp.mp4">
-    <value>mime=video/avc,size=50309,width=512,height=384,frame-rate=50,profile=2,level=256,bitrate=613008,package=standard</value>
+    <value>mime=video/avc,size=50309,width=512,height=384,frame-rate=50,profile=2,level=256,bitrate=613008,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_512x512p50f32_1000kbps_mp.mp4">
     <value>mime=video/avc,size=74581,width=512,height=512,frame-rate=50,profile=2,level=512,bitrate=916496,package=standard</value>
@@ -5679,7 +5679,7 @@
     <value>mime=video/avc,size=66239,width=640,height=360,frame-rate=50,profile=2,level=512,bitrate=812224,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_640x480p50f32_1200kbps_mp.mp4">
-    <value>mime=video/avc,size=91485,width=640,height=480,frame-rate=50,profile=2,level=512,bitrate=1127808,package=standard</value>
+    <value>mime=video/avc,size=91485,width=640,height=480,frame-rate=50,profile=2,level=512,bitrate=1127808,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_640x640p50f32_1600kbps_mp.mp4">
     <value>mime=video/avc,size=125839,width=640,height=640,frame-rate=50,profile=2,level=512,bitrate=1557120,package=standard</value>
@@ -5703,13 +5703,13 @@
     <value>mime=video/avc,size=11198,width=64,height=48,frame-rate=50,profile=2,level=16,bitrate=126824,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_64x64p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=11818,width=64,height=64,frame-rate=50,profile=2,level=16,bitrate=135624,package=standard</value>
+    <value>mime=video/avc,size=11818,width=64,height=64,frame-rate=50,profile=2,level=16,bitrate=135624,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_64x720p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12619,width=64,height=720,frame-rate=50,profile=2,level=16,bitrate=141872,package=standard</value>
+    <value>mime=video/avc,size=12619,width=64,height=720,frame-rate=50,profile=2,level=16,bitrate=141872,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_64x96p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=11987,width=64,height=96,frame-rate=50,profile=2,level=16,bitrate=135280,package=standard</value>
+    <value>mime=video/avc,size=11987,width=64,height=96,frame-rate=50,profile=2,level=16,bitrate=135280,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_704x528p50f32_1400kbps_mp.mp4">
     <value>mime=video/avc,size=108268,width=704,height=528,frame-rate=50,profile=2,level=512,bitrate=1337480,package=standard</value>
@@ -5727,13 +5727,13 @@
     <value>mime=video/avc,size=12892,width=720,height=16,frame-rate=50,profile=2,level=16,bitrate=147208,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_720x480p50f32_1300kbps_mp.mp4">
-    <value>mime=video/avc,size=100078,width=720,height=480,frame-rate=50,profile=2,level=512,bitrate=1235024,package=standard</value>
+    <value>mime=video/avc,size=100078,width=720,height=480,frame-rate=50,profile=2,level=512,bitrate=1235024,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_720x576p50f32_1600kbps_mp.mp4">
     <value>mime=video/avc,size=125746,width=720,height=576,frame-rate=50,profile=2,level=512,bitrate=1555960,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_720x64p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12764,width=720,height=64,frame-rate=50,profile=2,level=16,bitrate=143880,package=standard</value>
+    <value>mime=video/avc,size=12764,width=720,height=64,frame-rate=50,profile=2,level=16,bitrate=143880,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_720x720p50f32_2000kbps_mp.mp4">
     <value>mime=video/avc,size=162293,width=720,height=720,frame-rate=50,profile=2,level=512,bitrate=2012800,package=standard</value>
@@ -5784,22 +5784,22 @@
     <value>mime=video/avc,size=313704,width=960,height=960,frame-rate=50,profile=2,level=1024,bitrate=3905432,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_96x128p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12441,width=96,height=128,frame-rate=50,profile=2,level=16,bitrate=140072,package=standard</value>
+    <value>mime=video/avc,size=12441,width=96,height=128,frame-rate=50,profile=2,level=16,bitrate=140072,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_96x144p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12096,width=96,height=144,frame-rate=50,profile=2,level=16,bitrate=135760,package=standard</value>
+    <value>mime=video/avc,size=12096,width=96,height=144,frame-rate=50,profile=2,level=16,bitrate=135760,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_96x176p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12277,width=96,height=176,frame-rate=50,profile=2,level=16,bitrate=137920,package=standard</value>
+    <value>mime=video/avc,size=12277,width=96,height=176,frame-rate=50,profile=2,level=16,bitrate=137920,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_96x64p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=11982,width=96,height=64,frame-rate=50,profile=2,level=16,bitrate=137672,package=standard</value>
+    <value>mime=video/avc,size=11982,width=96,height=64,frame-rate=50,profile=2,level=16,bitrate=137672,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_96x72p50f32_200kbps_mp.mp4">
     <value>mime=video/avc,size=12083,width=96,height=72,frame-rate=50,profile=2,level=16,bitrate=136368,package=standard</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/resolutions/crowd_96x96p50f32_200kbps_mp.mp4">
-    <value>mime=video/avc,size=12293,width=96,height=96,frame-rate=50,profile=2,level=16,bitrate=138912,package=standard</value>
+    <value>mime=video/avc,size=12293,width=96,height=96,frame-rate=50,profile=2,level=16,bitrate=138912,package=standard,enforce=true</value>
   </entry>
   <entry key="h264/yuv420/8bit/mp/slices/crowd_1280x720p50f32/crowd_1280x720p50f32_1024byte_slices_mp.mp4">
     <value>mime=video/avc,size=271360,width=1280,height=720,frame-rate=50,profile=2,level=1024,bitrate=3376120,package=standard</value>
@@ -25740,22 +25740,22 @@
     <value>mime=video/x-vnd.on2.vp8,size=36285942,width=3840,height=2160,frame-rate=,profile=,level=,bitrate=,package=full</value>
   </entry>
   <entry key="vp8/yuv420/8bit/bitrate/crowd_640x360p24f300_1000kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=1588213,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=1588213,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/bitrate/crowd_640x360p25f300_1000kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=1523834,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=1523834,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/bitrate/crowd_640x360p30f300_1000kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=1274889,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=1274889,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/bitrate/crowd_640x360p48f300_1500kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=1213728,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=1213728,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/bitrate/crowd_640x360p50f300_1500kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=1171849,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=1171849,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/bitrate/crowd_640x360p60f300_1500kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=976943,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=976943,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/bitrate/crowd_854x480p24f300_2500kbps.webm">
     <value>mime=video/x-vnd.on2.vp8,size=3973482,width=854,height=480,frame-rate=,profile=,level=,bitrate=,package=full</value>
@@ -26253,163 +26253,163 @@
     <value>mime=video/x-vnd.on2.vp8,size=4062168,width=3840,height=2160,frame-rate=,profile=,level=,bitrate=,package=full</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_arnr_0f_0s_2t.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_arnr_0f_5s_1t.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_arnr_0f_5s_2t.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_arnr_15f_0s_2t.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_arnr_15f_5s_2t.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_arnr_5f_0s_2t.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_arnr_5f_5s_2t.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_arnr_5f_5s_3t.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_autAltRef_8lag.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_autAltRef.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_cbr.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=122296,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=122296,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_cq10.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_cq16.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_cq32.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_cq4.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_cq63.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_downScale_10_noKey.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_downScale_20_noKey.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_downScale_50_noKey.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_downScale_80_noKey.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_errRes.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=122754,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=122754,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_keyFrmOnly.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=278508,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=278508,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_noFrmPar_alternateKey_1x4.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=278508,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=278508,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_noFrmPar_key240_1x4.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_noFrmPar_key30_1x4.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=139344,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=139344,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_noFrmPar_key60_1x4.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_noFrmPar_key90_1x4.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_noKeyFrm.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_nonErrRes.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_Qp0-10.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=1162750,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=1162750,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_Qp0-63.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_Qp0.webm">
     <value>mime=video/x-vnd.on2.vp8,size=3647597,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=full</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_Qp10-20.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=702681,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=702681,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_Qp20-30.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=435106,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=435106,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_Qp30-40.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=261310,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=261310,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_Qp32.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=382962,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=382962,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_Qp40-50.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=138311,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=138311,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_Qp50-63.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=50558,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=50558,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_Qp63.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=41786,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=41786,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_sharp0.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_sharp1.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=127017,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=127017,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_sharp2.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=127302,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=127302,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_sharp3.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=127145,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=127145,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_sharp4.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=127183,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=127183,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_sharp5.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=127149,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=127149,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_sharp6.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=127092,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=127092,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_sharp7.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=126925,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=126925,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_skip5frm.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=121018,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=121018,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_upScale_100.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_upScale_10.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_upScale_20.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_upScale_50.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_vbr.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=124794,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/params/crowd_854x480p50f32/crowd_854x480p50f32_arnr_0f_0s_2t.webm">
     <value>mime=video/x-vnd.on2.vp8,size=214838,width=854,height=480,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -26616,16 +26616,16 @@
     <value>mime=video/x-vnd.on2.vp8,size=544433,width=1280,height=960,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_128x128p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=22573,width=128,height=128,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=22573,width=128,height=128,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_128x192p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=25964,width=128,height=192,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=25964,width=128,height=192,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_128x72p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=19288,width=128,height=72,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=19288,width=128,height=72,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_128x96p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=21679,width=128,height=96,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=21679,width=128,height=96,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_1440x1080p50f32_3100kbps.webm">
     <value>mime=video/x-vnd.on2.vp8,size=732471,width=1440,height=1080,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -26643,25 +26643,25 @@
     <value>mime=video/x-vnd.on2.vp8,size=607624,width=1440,height=960,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_144x144p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=24431,width=144,height=144,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=24431,width=144,height=144,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_144x176p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=26701,width=144,height=176,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=26701,width=144,height=176,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_144x192p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=26749,width=144,height=192,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=26749,width=144,height=192,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_144x256p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=30324,width=144,height=256,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=30324,width=144,height=256,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_144x96p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=21810,width=144,height=96,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=21810,width=144,height=96,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_1536x2048p50f32_6200kbps.webm">
     <value>mime=video/x-vnd.on2.vp8,size=1594581,width=1536,height=2048,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_160x240p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=30543,width=160,height=240,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=30543,width=160,height=240,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_16x1080p50f32_200kbps.webm">
     <value>mime=video/x-vnd.on2.vp8,size=21812,width=16,height=1080,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -26679,13 +26679,13 @@
     <value>mime=video/x-vnd.on2.vp8,size=20743,width=16,height=720,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_172x172p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=27116,width=172,height=172,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=27116,width=172,height=172,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_176x144p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=26536,width=176,height=144,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=26536,width=176,height=144,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_176x96p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=23213,width=176,height=96,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=23213,width=176,height=96,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_1920x1080p50f32_4100kbps.webm">
     <value>mime=video/x-vnd.on2.vp8,size=957687,width=1920,height=1080,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -26712,19 +26712,19 @@
     <value>mime=video/x-vnd.on2.vp8,size=47544,width=1920,height=64,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_192x128p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=26210,width=192,height=128,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=26210,width=192,height=128,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_192x144p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=27399,width=192,height=144,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=27399,width=192,height=144,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_192x192p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=30298,width=192,height=192,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=30298,width=192,height=192,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_192x256p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=35251,width=192,height=256,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=35251,width=192,height=256,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_192x288p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=38961,width=192,height=288,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=38961,width=192,height=288,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_2048x1152p50f32_4700kbps.webm">
     <value>mime=video/x-vnd.on2.vp8,size=1196505,width=2048,height=1152,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -26739,22 +26739,22 @@
     <value>mime=video/x-vnd.on2.vp8,size=2280972,width=2160,height=3840,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_216x288p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=44099,width=216,height=288,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=44099,width=216,height=288,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_2304x4096p50f32_18800kbps.webm">
     <value>mime=video/x-vnd.on2.vp8,size=2638026,width=2304,height=4096,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_240x160p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=31009,width=240,height=160,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=31009,width=240,height=160,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_240x240p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=40814,width=240,height=240,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=40814,width=240,height=240,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_240x320p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=50030,width=240,height=320,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=50030,width=240,height=320,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_240x360p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=52756,width=240,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=52756,width=240,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_24x32p50f32_200kbps.webm">
     <value>mime=video/x-vnd.on2.vp8,size=7659,width=24,height=32,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -26772,34 +26772,34 @@
     <value>mime=video/x-vnd.on2.vp8,size=2762075,width=2560,height=3840,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_256x144p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=31222,width=256,height=144,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=31222,width=256,height=144,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_256x192p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=35669,width=256,height=192,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=35669,width=256,height=192,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_256x256p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=45820,width=256,height=256,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=45820,width=256,height=256,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_264x352p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=53957,width=264,height=352,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=53957,width=264,height=352,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_2880x3840p50f32_22100kbps.webm">
     <value>mime=video/x-vnd.on2.vp8,size=3080416,width=2880,height=3840,frame-rate=,profile=,level=,bitrate=,package=full</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_288x192p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=39629,width=288,height=192,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=39629,width=288,height=192,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_288x216p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=44825,width=288,height=216,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=44825,width=288,height=216,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_288x288p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=49855,width=288,height=288,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=49855,width=288,height=288,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_288x352p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=54866,width=288,height=352,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=54866,width=288,height=352,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_288x512p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=58929,width=288,height=512,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=58929,width=288,height=512,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_3072x4096p50f32_24000kbps.webm">
     <value>mime=video/x-vnd.on2.vp8,size=3468897,width=3072,height=4096,frame-rate=,profile=,level=,bitrate=,package=full</value>
@@ -26808,13 +26808,13 @@
     <value>mime=video/x-vnd.on2.vp8,size=3546364,width=3120,height=4160,frame-rate=,profile=,level=,bitrate=,package=full</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_320x240p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=49907,width=320,height=240,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=49907,width=320,height=240,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_320x320p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=52171,width=320,height=320,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=52171,width=320,height=320,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_320x480p50f32_300kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=78944,width=320,height=480,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=78944,width=320,height=480,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_32x24p50f32_200kbps.webm">
     <value>mime=video/x-vnd.on2.vp8,size=7192,width=32,height=24,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -26823,46 +26823,46 @@
     <value>mime=video/x-vnd.on2.vp8,size=7411,width=32,height=32,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_350x350p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=52974,width=350,height=350,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=52974,width=350,height=350,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_352x264p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=53324,width=352,height=264,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=53324,width=352,height=264,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_352x288p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=53275,width=352,height=288,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=53275,width=352,height=288,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_352x352p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=56687,width=352,height=352,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=56687,width=352,height=352,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_356x638p50f32_800kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=144407,width=356,height=638,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=144407,width=356,height=638,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_356x640p50f32_800kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=144233,width=356,height=640,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=144233,width=356,height=640,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_358x636p50f32_800kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=144934,width=358,height=636,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=144934,width=358,height=636,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_358x640p50f32_800kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=144989,width=358,height=640,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=144989,width=358,height=640,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_360x240p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=52067,width=360,height=240,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=52067,width=360,height=240,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_360x360p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=56298,width=360,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=56298,width=360,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_360x480p50f32_300kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=80276,width=360,height=480,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=80276,width=360,height=480,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_360x636p50f32_800kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=144803,width=360,height=636,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=144803,width=360,height=636,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_360x638p50f32_800kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=145053,width=360,height=638,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=145053,width=360,height=638,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_360x640p50f32_400kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=109983,width=360,height=640,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=109983,width=360,height=640,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_3840x2160p50f32_16500kbps.webm">
     <value>mime=video/x-vnd.on2.vp8,size=4061905,width=3840,height=2160,frame-rate=,profile=,level=,bitrate=,package=full</value>
@@ -26904,10 +26904,10 @@
     <value>mime=video/x-vnd.on2.vp8,size=111263,width=472,height=472,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_480x320p50f32_300kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=79954,width=480,height=320,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=79954,width=480,height=320,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_480x360p50f32_300kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=81735,width=480,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=81735,width=480,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_480x480p50f32_400kbps.webm">
     <value>mime=video/x-vnd.on2.vp8,size=111711,width=480,height=480,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -26928,7 +26928,7 @@
     <value>mime=video/x-vnd.on2.vp8,size=6358861,width=5120,height=7680,frame-rate=,profile=,level=,bitrate=,package=full</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_512x288p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=58038,width=512,height=288,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=58038,width=512,height=288,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_512x384p50f32_300kbps.webm">
     <value>mime=video/x-vnd.on2.vp8,size=84510,width=512,height=384,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -26958,25 +26958,25 @@
     <value>mime=video/x-vnd.on2.vp8,size=8098503,width=6144,height=8192,frame-rate=,profile=,level=,bitrate=,package=full</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_636x358p50f32_800kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=144270,width=636,height=358,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=144270,width=636,height=358,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_636x360p50f32_800kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=144411,width=636,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=144411,width=636,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_638x356p50f32_800kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=144483,width=638,height=356,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=144483,width=638,height=356,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_638x360p50f32_800kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=145021,width=638,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=145021,width=638,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_640x356p50f32_800kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=144416,width=640,height=356,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=144416,width=640,height=356,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_640x358p50f32_400kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=103745,width=640,height=358,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=103745,width=640,height=358,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_640x360p50f32_400kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=106663,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=106663,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_640x480p50f32_600kbps.webm">
     <value>mime=video/x-vnd.on2.vp8,size=147651,width=640,height=480,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -27003,13 +27003,13 @@
     <value>mime=video/x-vnd.on2.vp8,size=17239,width=64,height=48,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_64x64p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=18514,width=64,height=64,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=18514,width=64,height=64,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_64x720p50f32_200kbps.webm">
     <value>mime=video/x-vnd.on2.vp8,size=32501,width=64,height=720,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_64x96p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=19213,width=64,height=96,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=19213,width=64,height=96,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_704x528p50f32_700kbps.webm">
     <value>mime=video/x-vnd.on2.vp8,size=172909,width=704,height=528,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -27042,10 +27042,10 @@
     <value>mime=video/x-vnd.on2.vp8,size=294296,width=720,height=960,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_72x128p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=21203,width=72,height=128,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=21203,width=72,height=128,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_72x96p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=20015,width=72,height=96,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=20015,width=72,height=96,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_7680x4320p50f32_24000kbps.webm">
     <value>mime=video/x-vnd.on2.vp8,size=8365736,width=7680,height=4320,frame-rate=,profile=,level=,bitrate=,package=full</value>
@@ -27105,22 +27105,22 @@
     <value>mime=video/x-vnd.on2.vp8,size=418761,width=960,height=960,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_96x128p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=21046,width=96,height=128,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=21046,width=96,height=128,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_96x144p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=21094,width=96,height=144,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=21094,width=96,height=144,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_96x176p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=23140,width=96,height=176,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=23140,width=96,height=176,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_96x64p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=19097,width=96,height=64,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=19097,width=96,height=64,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_96x72p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=19164,width=96,height=72,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=19164,width=96,height=72,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp8/yuv420/8bit/resolution/crowd_96x96p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp8,size=20262,width=96,height=96,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp8,size=20262,width=96,height=96,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/10bit/bitrate/crowd_1280x720p24f300_3000kbps.webm">
     <value>mime=video/x-vnd.on2.vp9,size=4825448,width=1280,height=720,frame-rate=,profile=,level=,bitrate=,package=full</value>
@@ -30360,7 +30360,7 @@
     <value>mime=video/x-vnd.on2.vp9,size=40409,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp9/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_frmPar_Qp63_1x2.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=9967,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=9967,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_frmPar_sharp0_1x2.webm">
     <value>mime=video/x-vnd.on2.vp9,size=40277,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -30387,7 +30387,7 @@
     <value>mime=video/x-vnd.on2.vp9,size=40312,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp9/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_frmPar_skip5frm_1x2.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=34224,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=34224,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_frmPar_upScale_100_1x2.webm">
     <value>mime=video/x-vnd.on2.vp9,size=40277,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -30534,7 +30534,7 @@
     <value>mime=video/x-vnd.on2.vp9,size=40457,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp9/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_noFrmPar_Qp63_1x2.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=10122,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=10122,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_noFrmPar_sharp0_1x2.webm">
     <value>mime=video/x-vnd.on2.vp9,size=40149,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -30561,7 +30561,7 @@
     <value>mime=video/x-vnd.on2.vp9,size=39969,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp9/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_noFrmPar_skip5frm_1x2.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=34188,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=34188,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/params/crowd_640x360p50f32/crowd_640x360p50f32_noFrmPar_upScale_100_1x2.webm">
     <value>mime=video/x-vnd.on2.vp9,size=40149,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -30978,10 +30978,10 @@
     <value>mime=video/x-vnd.on2.vp9,size=20315,width=128,height=192,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_128x72p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=22203,width=128,height=72,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=22203,width=128,height=72,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_128x96p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=21764,width=128,height=96,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=21764,width=128,height=96,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_1440x1080p50f32_3100kbps.webm">
     <value>mime=video/x-vnd.on2.vp9,size=256607,width=1440,height=1080,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -30999,7 +30999,7 @@
     <value>mime=video/x-vnd.on2.vp9,size=219548,width=1440,height=960,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_144x144p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=21401,width=144,height=144,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=21401,width=144,height=144,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_144x176p50f32_200kbps.webm">
     <value>mime=video/x-vnd.on2.vp9,size=20035,width=144,height=176,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -31008,10 +31008,10 @@
     <value>mime=video/x-vnd.on2.vp9,size=19508,width=144,height=192,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_144x256p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=19277,width=144,height=256,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=19277,width=144,height=256,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_144x96p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=21945,width=144,height=96,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=21945,width=144,height=96,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_1536x2048p50f32_6200kbps.webm">
     <value>mime=video/x-vnd.on2.vp9,size=491463,width=1536,height=2048,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -31038,10 +31038,10 @@
     <value>mime=video/x-vnd.on2.vp9,size=20623,width=172,height=172,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_176x144p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=21221,width=176,height=144,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=21221,width=176,height=144,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_176x96p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=21803,width=176,height=96,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=21803,width=176,height=96,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_1920x1080p50f32_4100kbps.webm">
     <value>mime=video/x-vnd.on2.vp9,size=347186,width=1920,height=1080,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -31071,13 +31071,13 @@
     <value>mime=video/x-vnd.on2.vp9,size=21688,width=192,height=128,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_192x144p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=21404,width=192,height=144,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=21404,width=192,height=144,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_192x192p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=19632,width=192,height=192,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=19632,width=192,height=192,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_192x256p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=18824,width=192,height=256,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=18824,width=192,height=256,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_192x288p50f32_200kbps.webm">
     <value>mime=video/x-vnd.on2.vp9,size=18313,width=192,height=288,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -31095,7 +31095,7 @@
     <value>mime=video/x-vnd.on2.vp9,size=1428214,width=2160,height=3840,frame-rate=,profile=,level=,bitrate=,package=full</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_216x288p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=17908,width=216,height=288,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=17908,width=216,height=288,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_2304x4096p50f32_18800kbps.webm">
     <value>mime=video/x-vnd.on2.vp9,size=1667866,width=2304,height=4096,frame-rate=,profile=,level=,bitrate=,package=full</value>
@@ -31104,13 +31104,13 @@
     <value>mime=video/x-vnd.on2.vp9,size=19372,width=240,height=160,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_240x240p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=17984,width=240,height=240,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=17984,width=240,height=240,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_240x320p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=16501,width=240,height=320,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=16501,width=240,height=320,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_240x360p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=16476,width=240,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=16476,width=240,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_24x32p50f32_200kbps.webm">
     <value>mime=video/x-vnd.on2.vp9,size=13562,width=24,height=32,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -31131,13 +31131,13 @@
     <value>mime=video/x-vnd.on2.vp9,size=19811,width=256,height=144,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_256x192p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=18844,width=256,height=192,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=18844,width=256,height=192,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_256x256p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=17692,width=256,height=256,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=17692,width=256,height=256,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_264x352p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=16651,width=264,height=352,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=16651,width=264,height=352,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_2880x3840p50f32_22100kbps.webm">
     <value>mime=video/x-vnd.on2.vp9,size=1962521,width=2880,height=3840,frame-rate=,profile=,level=,bitrate=,package=full</value>
@@ -31164,7 +31164,7 @@
     <value>mime=video/x-vnd.on2.vp9,size=2135393,width=3120,height=4160,frame-rate=,profile=,level=,bitrate=,package=full</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_320x240p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=17161,width=320,height=240,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=17161,width=320,height=240,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_320x320p50f32_200kbps.webm">
     <value>mime=video/x-vnd.on2.vp9,size=16450,width=320,height=320,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -31188,7 +31188,7 @@
     <value>mime=video/x-vnd.on2.vp9,size=16441,width=352,height=288,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_352x352p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=17348,width=352,height=352,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=17348,width=352,height=352,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_356x638p50f32_800kbps.webm">
     <value>mime=video/x-vnd.on2.vp9,size=66226,width=356,height=638,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -31203,13 +31203,13 @@
     <value>mime=video/x-vnd.on2.vp9,size=66220,width=358,height=640,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_360x240p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=16499,width=360,height=240,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=16499,width=360,height=240,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_360x360p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=18134,width=360,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=18134,width=360,height=360,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_360x480p50f32_300kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=24969,width=360,height=480,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=24969,width=360,height=480,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_360x636p50f32_800kbps.webm">
     <value>mime=video/x-vnd.on2.vp9,size=66331,width=360,height=636,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -31329,7 +31329,7 @@
     <value>mime=video/x-vnd.on2.vp9,size=75535,width=640,height=356,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_640x358p50f32_400kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=32962,width=640,height=358,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=32962,width=640,height=358,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_640x360p50f32_400kbps.webm">
     <value>mime=video/x-vnd.on2.vp9,size=33152,width=640,height=360,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -31359,13 +31359,13 @@
     <value>mime=video/x-vnd.on2.vp9,size=19429,width=64,height=48,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_64x64p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=20702,width=64,height=64,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=20702,width=64,height=64,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_64x720p50f32_200kbps.webm">
     <value>mime=video/x-vnd.on2.vp9,size=18073,width=64,height=720,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_64x96p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=22194,width=64,height=96,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=22194,width=64,height=96,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_704x528p50f32_700kbps.webm">
     <value>mime=video/x-vnd.on2.vp9,size=56606,width=704,height=528,frame-rate=,profile=,level=,bitrate=,package=standard</value>
@@ -31398,10 +31398,10 @@
     <value>mime=video/x-vnd.on2.vp9,size=104036,width=720,height=960,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_72x128p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=21963,width=72,height=128,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=21963,width=72,height=128,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_72x96p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=22305,width=72,height=96,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=22305,width=72,height=96,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_7680x4320p50f32_24000kbps.webm">
     <value>mime=video/x-vnd.on2.vp9,size=2078023,width=7680,height=4320,frame-rate=,profile=,level=,bitrate=,package=full</value>
@@ -31464,19 +31464,19 @@
     <value>mime=video/x-vnd.on2.vp9,size=21565,width=96,height=128,frame-rate=,profile=,level=,bitrate=,package=standard</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_96x144p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=21704,width=96,height=144,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=21704,width=96,height=144,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_96x176p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=20834,width=96,height=176,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=20834,width=96,height=176,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_96x64p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=21980,width=96,height=64,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=21980,width=96,height=64,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_96x72p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=21496,width=96,height=72,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=21496,width=96,height=72,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv420/8bit/resolution/crowd_96x96p50f32_200kbps.webm">
-    <value>mime=video/x-vnd.on2.vp9,size=22764,width=96,height=96,frame-rate=,profile=,level=,bitrate=,package=standard</value>
+    <value>mime=video/x-vnd.on2.vp9,size=22764,width=96,height=96,frame-rate=,profile=,level=,bitrate=,package=standard,enforce=true</value>
   </entry>
   <entry key="vp9/yuv422/10bit/bitrate/crowd_1280x720p24f300_3000kbps.webm">
     <value>mime=video/x-vnd.on2.vp9,size=5005326,width=1280,height=720,frame-rate=,profile=,level=,bitrate=,package=full</value>
diff --git a/hostsidetests/media/bitstreams/common/src/android/media/cts/bitstreams/MediaBitstreams.java b/hostsidetests/media/bitstreams/common/src/android/media/cts/bitstreams/MediaBitstreams.java
index 13b70a5..fbf7491 100644
--- a/hostsidetests/media/bitstreams/common/src/android/media/cts/bitstreams/MediaBitstreams.java
+++ b/hostsidetests/media/bitstreams/common/src/android/media/cts/bitstreams/MediaBitstreams.java
@@ -61,6 +61,7 @@
     public static final String DYNAMIC_CONFIG_KEY = "key";
     public static final String DYNAMIC_CONFIG_VALUE = "value";
     public static final String DYNAMIC_CONFIG_PACKAGE = "package";
+    public static final String DYNAMIC_CONFIG_ENFORCE = "enforce";
 
     /* utilities */
     /**
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitBpBitstreamsFullTest.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitBpBitstreamsFullTest.java
index 457c636..72bb15a 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitBpBitstreamsFullTest.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitBpBitstreamsFullTest.java
@@ -31,8 +31,8 @@
     }
 
     public H264Yuv420_8bitBpBitstreamsFullTest(String prefix, String path,
-            BitstreamPackage pkg, BitstreamPackage packageToRun) {
-        super(prefix, path, pkg, packageToRun);
+            BitstreamPackage pkg, BitstreamPackage packageToRun, boolean enforce) {
+        super(prefix, path, pkg, packageToRun, enforce);
     }
 
     @Test
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitBpBitstreamsTest.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitBpBitstreamsTest.java
index 78cd436..5bcfe7e 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitBpBitstreamsTest.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitBpBitstreamsTest.java
@@ -29,8 +29,8 @@
     }
 
     public H264Yuv420_8bitBpBitstreamsTest(String prefix, String path,
-            BitstreamPackage pkg, BitstreamPackage packageToRun) {
-        super(prefix, path, pkg, packageToRun);
+            BitstreamPackage pkg, BitstreamPackage packageToRun, boolean enforce) {
+        super(prefix, path, pkg, packageToRun, enforce);
     }
 
 }
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitHpBitstreamsFullTest.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitHpBitstreamsFullTest.java
index 606a169..7db0198f 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitHpBitstreamsFullTest.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitHpBitstreamsFullTest.java
@@ -31,8 +31,8 @@
     }
 
     public H264Yuv420_8bitHpBitstreamsFullTest(String prefix, String path,
-            BitstreamPackage pkg, BitstreamPackage packageToRun) {
-        super(prefix, path, pkg, packageToRun);
+            BitstreamPackage pkg, BitstreamPackage packageToRun, boolean enforce) {
+        super(prefix, path, pkg, packageToRun, enforce);
     }
 
     @Test
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitHpBitstreamsTest.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitHpBitstreamsTest.java
index 792fa5e..6ba3822 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitHpBitstreamsTest.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitHpBitstreamsTest.java
@@ -29,8 +29,8 @@
     }
 
     public H264Yuv420_8bitHpBitstreamsTest(String prefix, String path,
-            BitstreamPackage pkg, BitstreamPackage packageToRun) {
-        super(prefix, path, pkg, packageToRun);
+            BitstreamPackage pkg, BitstreamPackage packageToRun, boolean enforce) {
+        super(prefix, path, pkg, packageToRun, enforce);
     }
 
 }
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitMpBitstreamsFullTest.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitMpBitstreamsFullTest.java
index ebacc3c..4811150 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitMpBitstreamsFullTest.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitMpBitstreamsFullTest.java
@@ -31,8 +31,8 @@
     }
 
     public H264Yuv420_8bitMpBitstreamsFullTest(String prefix, String path,
-            BitstreamPackage pkg, BitstreamPackage packageToRun) {
-        super(prefix, path, pkg, packageToRun);
+            BitstreamPackage pkg, BitstreamPackage packageToRun, boolean enforce) {
+        super(prefix, path, pkg, packageToRun, enforce);
     }
 
     @Test
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitMpBitstreamsTest.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitMpBitstreamsTest.java
index ce80c96..b7f2c67 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitMpBitstreamsTest.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/H264Yuv420_8bitMpBitstreamsTest.java
@@ -29,8 +29,8 @@
     }
 
     public H264Yuv420_8bitMpBitstreamsTest(String prefix, String path,
-            BitstreamPackage pkg, BitstreamPackage packageToRun) {
-        super(prefix, path, pkg, packageToRun);
+            BitstreamPackage pkg, BitstreamPackage packageToRun, boolean enforce) {
+        super(prefix, path, pkg, packageToRun, enforce);
     }
 
 }
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/HevcYuv400BitstreamsFullTest.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/HevcYuv400BitstreamsFullTest.java
index 59ba131..e00283f 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/HevcYuv400BitstreamsFullTest.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/HevcYuv400BitstreamsFullTest.java
@@ -31,8 +31,8 @@
     }
 
     public HevcYuv400BitstreamsFullTest(String prefix, String path,
-            BitstreamPackage pkg, BitstreamPackage packageToRun) {
-        super(prefix, path, pkg, packageToRun);
+            BitstreamPackage pkg, BitstreamPackage packageToRun, boolean enforce) {
+        super(prefix, path, pkg, packageToRun, enforce);
     }
 
     @Test
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/HevcYuv420BitstreamsFullTest.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/HevcYuv420BitstreamsFullTest.java
index 1680ff2..eb0fda7 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/HevcYuv420BitstreamsFullTest.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/HevcYuv420BitstreamsFullTest.java
@@ -31,8 +31,8 @@
     }
 
     public HevcYuv420BitstreamsFullTest(String prefix, String path,
-            BitstreamPackage pkg, BitstreamPackage packageToRun) {
-        super(prefix, path, pkg, packageToRun);
+            BitstreamPackage pkg, BitstreamPackage packageToRun, boolean enforce) {
+        super(prefix, path, pkg, packageToRun, enforce);
     }
 
     @Test
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/HevcYuv420BitstreamsTest.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/HevcYuv420BitstreamsTest.java
index 0fd0c3c..1334c82 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/HevcYuv420BitstreamsTest.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/HevcYuv420BitstreamsTest.java
@@ -29,8 +29,8 @@
     }
 
     public HevcYuv420BitstreamsTest(String prefix, String path,
-            BitstreamPackage pkg, BitstreamPackage packageToRun) {
-        super(prefix, path, pkg, packageToRun);
+            BitstreamPackage pkg, BitstreamPackage packageToRun, boolean enforce) {
+        super(prefix, path, pkg, packageToRun, enforce);
     }
 
 }
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/HevcYuv422BitstreamsFullTest.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/HevcYuv422BitstreamsFullTest.java
index ee7a683..0efdad2 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/HevcYuv422BitstreamsFullTest.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/HevcYuv422BitstreamsFullTest.java
@@ -31,8 +31,8 @@
     }
 
     public HevcYuv422BitstreamsFullTest(String prefix, String path,
-            BitstreamPackage pkg, BitstreamPackage packageToRun) {
-        super(prefix, path, pkg, packageToRun);
+            BitstreamPackage pkg, BitstreamPackage packageToRun, boolean enforce) {
+        super(prefix, path, pkg, packageToRun, enforce);
     }
 
     @Test
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/HevcYuv444BitstreamsFullTest.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/HevcYuv444BitstreamsFullTest.java
index f167cb6..0f564ea 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/HevcYuv444BitstreamsFullTest.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/HevcYuv444BitstreamsFullTest.java
@@ -31,8 +31,8 @@
     }
 
     public HevcYuv444BitstreamsFullTest(String prefix, String path,
-            BitstreamPackage pkg, BitstreamPackage packageToRun) {
-        super(prefix, path, pkg, packageToRun);
+            BitstreamPackage pkg, BitstreamPackage packageToRun, boolean enforce) {
+        super(prefix, path, pkg, packageToRun, enforce);
     }
 
     @Test
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/MediaBitstreamsTest.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/MediaBitstreamsTest.java
index cb21724..3d16367 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/MediaBitstreamsTest.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/MediaBitstreamsTest.java
@@ -53,6 +53,7 @@
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import org.junit.Assert;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.xmlpull.v1.XmlPullParser;
@@ -109,6 +110,7 @@
 
     private BitstreamPackage mPackage = BitstreamPackage.FULL;
     private BitstreamPackage mPackageToRun = BitstreamPackage.STANDARD;
+    private boolean mEnforce = false;
 
     static class ConformanceEntry {
         final String mPath, mCodecName, mStatus;
@@ -177,6 +179,7 @@
                 String format = parser.getText();
                 String[] kvPairs = format.split(",");
                 BitstreamPackage curPackage = BitstreamPackage.FULL;
+                boolean enforce = false;
                 for (String kvPair : kvPairs) {
                     String[] kv = kvPair.split("=");
                     if (MediaBitstreams.DYNAMIC_CONFIG_PACKAGE.equals(kv[0])) {
@@ -186,10 +189,12 @@
                         } catch (Exception e) {
                             CLog.w(e);
                         }
+                    } else if (MediaBitstreams.DYNAMIC_CONFIG_ENFORCE.equals(kv[0])) {
+                        enforce = "true".equals(kv[1]);
                     }
                 }
                 if (curPackage.compareTo(packageToRun) <= 0) {
-                    entries.add(new Object[] {prefix, bitstream, curPackage, packageToRun});
+                    entries.add(new Object[] {prefix, bitstream, curPackage, packageToRun, enforce});
                 }
             }
             return entries;
@@ -201,10 +206,16 @@
 
     public MediaBitstreamsTest(String prefix, String path, BitstreamPackage pkg, BitstreamPackage packageToRun
             ) {
+        this(prefix, path, pkg, packageToRun, false);
+    }
+
+    public MediaBitstreamsTest(String prefix, String path, BitstreamPackage pkg, BitstreamPackage packageToRun,
+            boolean enforce) {
         mPrefix = prefix;
         mPath = path;
         mPackage = pkg;
         mPackageToRun = packageToRun;
+        mEnforce = enforce;
     }
 
     @Override
@@ -464,7 +475,19 @@
                 addConformanceEntry(curMethod, mPath, MediaBitstreams.K_UNAVAILABLE, e.toString());
             }
         }
-        // todo(robertshih): lookup conformance entry; pass/fail based on lookup result
+
+        if (mEnforce) {
+            if (!mResults.containsKey(mPath)) {
+                Assert.fail("no results captured for " + mPath);
+            }
+            List<ConformanceEntry> entries = mResults.get(mPath);
+            for (ConformanceEntry ce : entries) {
+                if (!"true".equals(ce.mStatus)) {
+                    Assert.fail(ce.toString());
+                }
+            }
+        }
+
     }
 
     private void testBitstreamsConformance(String prefix)
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp8BitstreamsFullTest.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp8BitstreamsFullTest.java
index 7755b4a..bbfc2eb 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp8BitstreamsFullTest.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp8BitstreamsFullTest.java
@@ -31,8 +31,8 @@
     }
 
     public Vp8BitstreamsFullTest(String prefix, String path,
-            BitstreamPackage pkg, BitstreamPackage packageToRun) {
-        super(prefix, path, pkg, packageToRun);
+            BitstreamPackage pkg, BitstreamPackage packageToRun, boolean enforce) {
+        super(prefix, path, pkg, packageToRun, enforce);
     }
 
     @Test
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp8BitstreamsTest.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp8BitstreamsTest.java
index de05f93..8587abd 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp8BitstreamsTest.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp8BitstreamsTest.java
@@ -29,8 +29,8 @@
     }
 
     public Vp8BitstreamsTest(String prefix, String path,
-            BitstreamPackage pkg, BitstreamPackage packageToRun) {
-        super(prefix, path, pkg, packageToRun);
+            BitstreamPackage pkg, BitstreamPackage packageToRun, boolean enforce) {
+        super(prefix, path, pkg, packageToRun, enforce);
     }
 
 }
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp9Yuv420BitstreamsFullTest.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp9Yuv420BitstreamsFullTest.java
index 747e3a9..d209011 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp9Yuv420BitstreamsFullTest.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp9Yuv420BitstreamsFullTest.java
@@ -31,8 +31,8 @@
     }
 
     public Vp9Yuv420BitstreamsFullTest(String prefix, String path,
-            BitstreamPackage pkg, BitstreamPackage packageToRun) {
-        super(prefix, path, pkg, packageToRun);
+            BitstreamPackage pkg, BitstreamPackage packageToRun, boolean enforce) {
+        super(prefix, path, pkg, packageToRun, enforce);
     }
 
     @Test
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp9Yuv420BitstreamsTest.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp9Yuv420BitstreamsTest.java
index 76af826..9b048bb 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp9Yuv420BitstreamsTest.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp9Yuv420BitstreamsTest.java
@@ -29,8 +29,8 @@
     }
 
     public Vp9Yuv420BitstreamsTest(String prefix, String path,
-            BitstreamPackage pkg, BitstreamPackage packageToRun) {
-        super(prefix, path, pkg, packageToRun);
+            BitstreamPackage pkg, BitstreamPackage packageToRun, boolean enforce) {
+        super(prefix, path, pkg, packageToRun, enforce);
     }
 
 }
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp9Yuv422BitstreamsFullTest.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp9Yuv422BitstreamsFullTest.java
index 2856e36..a03f1c4 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp9Yuv422BitstreamsFullTest.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp9Yuv422BitstreamsFullTest.java
@@ -31,8 +31,8 @@
     }
 
     public Vp9Yuv422BitstreamsFullTest(String prefix, String path,
-            BitstreamPackage pkg, BitstreamPackage packageToRun) {
-        super(prefix, path, pkg, packageToRun);
+            BitstreamPackage pkg, BitstreamPackage packageToRun, boolean enforce) {
+        super(prefix, path, pkg, packageToRun, enforce);
     }
 
     @Test
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp9Yuv444BitstreamsFullTest.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp9Yuv444BitstreamsFullTest.java
index 46af097..22786fc 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp9Yuv444BitstreamsFullTest.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/Vp9Yuv444BitstreamsFullTest.java
@@ -31,8 +31,8 @@
     }
 
     public Vp9Yuv444BitstreamsFullTest(String prefix, String path,
-            BitstreamPackage pkg, BitstreamPackage packageToRun) {
-        super(prefix, path, pkg, packageToRun);
+            BitstreamPackage pkg, BitstreamPackage packageToRun, boolean enforce) {
+        super(prefix, path, pkg, packageToRun, enforce);
     }
 
     @Test
diff --git a/hostsidetests/security/securityPatch/CVE-2017-0479/Android.mk b/hostsidetests/security/securityPatch/CVE-2017-0479/Android.mk
index aa77648..517481a 100644
--- a/hostsidetests/security/securityPatch/CVE-2017-0479/Android.mk
+++ b/hostsidetests/security/securityPatch/CVE-2017-0479/Android.mk
@@ -26,7 +26,8 @@
         $(TOP)/frameworks/av/include/media/ \
         $(TOP)/frameworks/av/services/ \
         $(TOP)/system/media/audio_utils/include/ \
-        $(TOP)/external/sonic/
+        $(TOP)/external/sonic/ \
+        $(TOP)/external/jsoncpp/include/
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts sts
diff --git a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
index cb189b8..f7a58b1 100644
--- a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
+++ b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
@@ -81,6 +81,7 @@
     private File aospPcFile;
     private File aospSvcFile;
     private File devicePolicyFile;
+    private File deviceSystemPolicyFile;
     private File devicePlatSeappFile;
     private File deviceNonplatSeappFile;
     private File devicePlatFcFile;
@@ -163,6 +164,8 @@
                 deviceNonplatFcFile = getDeviceFile(mDevice, cachedDeviceNonplatFcFiles,
                         "/vendor/etc/selinux/vendor_file_contexts", "vendor_file_contexts");
             }
+            deviceSystemPolicyFile =
+                    android.security.cts.SELinuxHostTest.getDeviceSystemPolicyFile(mDevice);
         } else {
             devicePlatFcFile = getDeviceFile(mDevice, cachedDevicePlatFcFiles,
                     "/plat_file_contexts", "plat_file_contexts");
@@ -815,17 +818,32 @@
         libcpp.deleteOnExit();
     }
 
-    private void assertSepolicyTests(String test, String testExecutable) throws Exception {
+    private void assertSepolicyTests(String test, String testExecutable,
+            boolean includeVendorSepolicy) throws Exception {
         setupLibraries();
         sepolicyTests = copyResourceToTempFile(testExecutable);
         sepolicyTests.setExecutable(true);
-        ProcessBuilder pb = new ProcessBuilder(
-                sepolicyTests.getAbsolutePath(),
-                "-l", libsepolwrap.getAbsolutePath(),
-                "-f", devicePlatFcFile.getAbsolutePath(),
-                "-f", deviceNonplatFcFile.getAbsolutePath(),
-                "-p", devicePolicyFile.getAbsolutePath(),
-                "--test", test);
+
+        List<String> args = new ArrayList<String>();
+        args.add(sepolicyTests.getAbsolutePath());
+        args.add("-l");
+        args.add(libsepolwrap.getAbsolutePath());
+        args.add("-f");
+        args.add(devicePlatFcFile.getAbsolutePath());
+        args.add("--test");
+        args.add(test);
+
+        if (includeVendorSepolicy) {
+            args.add("-f");
+            args.add(deviceNonplatFcFile.getAbsolutePath());
+            args.add("-p");
+            args.add(devicePolicyFile.getAbsolutePath());
+        } else {
+            args.add("-p");
+            args.add(deviceSystemPolicyFile.getAbsolutePath());
+        }
+
+        ProcessBuilder pb = new ProcessBuilder(args);
         Map<String, String> env = pb.environment();
         if (isMac()) {
             env.put("DYLD_LIBRARY_PATH", System.getProperty("java.io.tmpdir"));
@@ -852,7 +870,8 @@
      * @throws Exception
      */
     public void testDataTypeViolators() throws Exception {
-        assertSepolicyTests("TestDataTypeViolations", "/sepolicy_tests");
+        assertSepolicyTests("TestDataTypeViolations", "/sepolicy_tests",
+                PropertyUtil.isVendorApiLevelNewerThan(mDevice, 27) /* includeVendorSepolicy */);
     }
 
     /**
@@ -861,7 +880,8 @@
      * @throws Exception
      */
     public void testProcTypeViolators() throws Exception {
-        assertSepolicyTests("TestProcTypeViolations", "/sepolicy_tests");
+        assertSepolicyTests("TestProcTypeViolations", "/sepolicy_tests",
+                PropertyUtil.isVendorApiLevelNewerThan(mDevice, 27) /* includeVendorSepolicy */);
     }
 
     /**
@@ -870,7 +890,8 @@
      * @throws Exception
      */
     public void testSysfsTypeViolators() throws Exception {
-        assertSepolicyTests("TestSysfsTypeViolations", "/sepolicy_tests");
+        assertSepolicyTests("TestSysfsTypeViolations", "/sepolicy_tests",
+                PropertyUtil.isVendorApiLevelNewerThan(mDevice, 27) /* includeVendorSepolicy */);
     }
 
     /**
@@ -879,7 +900,8 @@
      * @throws Exception
      */
     public void testVendorTypeViolators() throws Exception {
-        assertSepolicyTests("TestVendorTypeViolations", "/sepolicy_tests");
+        assertSepolicyTests("TestVendorTypeViolations", "/sepolicy_tests",
+                PropertyUtil.isVendorApiLevelNewerThan(mDevice, 27) /* includeVendorSepolicy */);
     }
 
     /**
@@ -890,7 +912,8 @@
      * @throws Exception
      */
     public void testCoredomainViolators() throws Exception {
-        assertSepolicyTests("CoredomainViolations", "/treble_sepolicy_tests");
+        assertSepolicyTests("CoredomainViolations", "/treble_sepolicy_tests",
+                PropertyUtil.isVendorApiLevelNewerThan(mDevice, 27) /* includeVendorSepolicy */);
     }
 
    /**
diff --git a/hostsidetests/statsd/Android.mk b/hostsidetests/statsd/Android.mk
index b614cb1..3b7819d 100644
--- a/hostsidetests/statsd/Android.mk
+++ b/hostsidetests/statsd/Android.mk
@@ -28,6 +28,10 @@
 LOCAL_STATIC_JAVA_LIBRARIES := platformprotos
 LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed compatibility-host-util host-libprotobuf-java-full
 
+LOCAL_COMPATIBILITY_SUPPORT_FILES := $(LOCAL_PATH)/PROCSTATSQ_PROCS_STATE_TOP_DURATION.pbtxt \
+	$(LOCAL_PATH)/PROCSTATSQ_PROCS_STATE_CACHED_EMPTY_DURATION.pbtxt \
+	$(LOCAL_PATH)/PROCSTATSQ_PROCS_STATE_PSS_VALUE.pbtxt
+
 include $(BUILD_HOST_JAVA_LIBRARY)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/statsd/PROCSTATSQ_PROCS_STATE_CACHED_EMPTY_DURATION.pbtxt b/hostsidetests/statsd/PROCSTATSQ_PROCS_STATE_CACHED_EMPTY_DURATION.pbtxt
new file mode 100644
index 0000000..41a8e0e
--- /dev/null
+++ b/hostsidetests/statsd/PROCSTATSQ_PROCS_STATE_CACHED_EMPTY_DURATION.pbtxt
@@ -0,0 +1,69 @@
+# DURATION_PROCESS_STATE_IN_CACHED_EMPTY_PER_PROC_NAME_PACKAGE_NAME_VERSION
+id: -6109199674574072698
+duration_metric {
+  id: -7871805656933174442
+  what: -4194528603977557137
+  aggregation_type: SUM
+  dimensions_in_what {
+    field: 3
+    child {
+      field: 2
+    }
+    child {
+      field: 3
+    }
+    child {
+      field: 5
+    }
+  }
+  bucket: ONE_MINUTE
+}
+# PROC_STATE NOT IN CACHED_EMPTY
+atom_matcher {
+  id: -2354884036751182872
+  combination {
+    operation: NOT
+    matcher: -7794766650955623092
+  }
+}
+# PROC_STATE IN CACHED_EMPTY
+atom_matcher {
+  id: -7794766650955623092
+  simple_atom_matcher {
+    atom_id: 3
+    field_value_matcher {
+      field: 4
+      eq_int: 1018
+    }
+  }
+}
+predicate {
+  id: -4194528603977557137
+  simple_predicate {
+    start: -7794766650955623092
+    stop: -2354884036751182872
+    count_nesting: false
+    dimensions {
+      field: 3
+      child {
+        field: 2
+      }
+      child {
+        field: 3
+      }
+      child {
+        field: 5
+      }
+    }
+  }
+}
+allowed_log_source: "AID_GRAPHICS"
+allowed_log_source: "AID_INCIDENTD"
+allowed_log_source: "AID_STATSD"
+allowed_log_source: "AID_RADIO"
+allowed_log_source: "com.android.systemui"
+allowed_log_source: "com.android.vending"
+allowed_log_source: "AID_SYSTEM"
+allowed_log_source: "AID_ROOT"
+allowed_log_source: "AID_BLUETOOTH"
+hash_strings_in_metric_report: false
diff --git a/hostsidetests/statsd/PROCSTATSQ_PROCS_STATE_PSS_VALUE.pbtxt b/hostsidetests/statsd/PROCSTATSQ_PROCS_STATE_PSS_VALUE.pbtxt
new file mode 100644
index 0000000..e6efd55
--- /dev/null
+++ b/hostsidetests/statsd/PROCSTATSQ_PROCS_STATE_PSS_VALUE.pbtxt
@@ -0,0 +1,44 @@
+# VALUE_MAX_PSS_PER_PROC_NAME_PACKAGE_NAME_VERSION
+id: -6109199674574072698
+value_metric {
+  id: 1867856787681329178
+  what: -3480158308153459853
+  value_field {
+    field: 18
+    child {
+      field: 4
+    }
+  }
+  dimensions_in_what {
+    field: 18
+    child {
+      field: 2
+    }
+    child {
+      field: 3
+    }
+    child {
+      field: 9
+    }
+  }
+  bucket: ONE_MINUTE
+  aggregation_type: MAX
+}
+# PROCESS_MEMORY_STAT_REPORTED
+atom_matcher {
+  id: -3480158308153459853
+  simple_atom_matcher {
+    atom_id: 18
+  }
+}
+allowed_log_source: "AID_GRAPHICS"
+allowed_log_source: "AID_INCIDENTD"
+allowed_log_source: "AID_STATSD"
+allowed_log_source: "AID_RADIO"
+allowed_log_source: "com.android.systemui"
+allowed_log_source: "com.android.vending"
+allowed_log_source: "AID_SYSTEM"
+allowed_log_source: "AID_ROOT"
+allowed_log_source: "AID_BLUETOOTH"
+
+hash_strings_in_metric_report: false
diff --git a/hostsidetests/statsd/PROCSTATSQ_PROCS_STATE_TOP_DURATION.pbtxt b/hostsidetests/statsd/PROCSTATSQ_PROCS_STATE_TOP_DURATION.pbtxt
new file mode 100644
index 0000000..e17727a
--- /dev/null
+++ b/hostsidetests/statsd/PROCSTATSQ_PROCS_STATE_TOP_DURATION.pbtxt
@@ -0,0 +1,90 @@
+# DURATION_PROCESS_STATE_IN_TOP_PER_PROC_NAME_PACKAGE_NAME_VERSION
+id: -6109199674574072698
+duration_metric {
+  id: -1365360216258753370
+  what: -8800411078553365796
+  aggregation_type: SUM
+  dimensions_in_what {
+    field: 3
+    child {
+      field: 2
+    }
+    child {
+      field: 3
+    }
+    child {
+      field: 5
+    }
+  }
+  bucket: ONE_MINUTE
+}
+# PROC_STATE NOT IN TOP
+atom_matcher {
+  id: -7829668247086356765
+  combination {
+    operation: NOT
+    matcher: -2987742411590785849
+  }
+}
+# PROCESS_STATE TOP
+atom_matcher {
+  id: 509484152027467470
+  simple_atom_matcher {
+    atom_id: 3
+    field_value_matcher {
+      field: 4
+      eq_int: 1002
+    }
+  }
+}
+# PROCESS_STATE TOP_SLEEPING
+atom_matcher {
+  id: -3293304223207806916
+  simple_atom_matcher {
+    atom_id: 3
+    field_value_matcher {
+      field: 4
+      eq_int: 1011
+    }
+  }
+}
+# PROC_STATE IN TOP
+atom_matcher {
+  id: -2987742411590785849
+  combination {
+    operation: OR
+    matcher: 509484152027467470
+    matcher: -3293304223207806916
+  }
+}
+predicate {
+  id: -8800411078553365796
+  simple_predicate {
+    start: -2987742411590785849
+    stop: -7829668247086356765
+    count_nesting: false
+    dimensions {
+      field: 3
+      child {
+        field: 2
+      }
+      child {
+        field: 3
+      }
+      child {
+        field: 5
+      }
+    }
+  }
+}
+allowed_log_source: "AID_GRAPHICS"
+allowed_log_source: "AID_INCIDENTD"
+allowed_log_source: "AID_STATSD"
+allowed_log_source: "AID_RADIO"
+allowed_log_source: "com.android.systemui"
+allowed_log_source: "com.android.vending"
+allowed_log_source: "AID_SYSTEM"
+allowed_log_source: "AID_ROOT"
+allowed_log_source: "AID_BLUETOOTH"
+
+hash_strings_in_metric_report: false
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
index ad72959..98be839 100644
--- a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
@@ -37,11 +37,13 @@
     public static final String KEY_ACTION = "action";
     public static final String ACTION_END_IMMEDIATELY = "action.end_immediately";
     public static final String ACTION_SLEEP_WHILE_TOP = "action.sleep_top";
+    public static final String ACTION_LONG_SLEEP_WHILE_TOP = "action.long_sleep_top";
     public static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay";
     public static final String ACTION_CRASH = "action.crash";
 
     public static final int SLEEP_OF_ACTION_SLEEP_WHILE_TOP = 2_000;
     public static final int SLEEP_OF_ACTION_SHOW_APPLICATION_OVERLAY = 2_000;
+    public static final int LONG_SLEEP_WHILE_TOP = 60_000;
 
     @Override
     public void onCreate(Bundle bundle) {
@@ -61,7 +63,10 @@
                 finish();
                 break;
             case ACTION_SLEEP_WHILE_TOP:
-                doSleepWhileTop();
+                doSleepWhileTop(SLEEP_OF_ACTION_SLEEP_WHILE_TOP);
+                break;
+            case ACTION_LONG_SLEEP_WHILE_TOP:
+                doSleepWhileTop(LONG_SLEEP_WHILE_TOP);
                 break;
             case ACTION_SHOW_APPLICATION_OVERLAY:
                 doShowApplicationOverlay();
@@ -76,11 +81,11 @@
     }
 
     /** Does nothing, but asynchronously. */
-    private void doSleepWhileTop() {
+    private void doSleepWhileTop(int sleepTime) {
         new AsyncTask<Void, Void, Void>() {
             @Override
             protected Void doInBackground(Void... params) {
-                AtomTests.sleep(SLEEP_OF_ACTION_SLEEP_WHILE_TOP);
+                AtomTests.sleep(sleepTime);
                 return null;
             }
 
diff --git a/hostsidetests/statsd/src/android/cts/statsd/alert/AnomalyDetectionTests.java b/hostsidetests/statsd/src/android/cts/statsd/alert/AnomalyDetectionTests.java
index e78a9cd..3109138 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/alert/AnomalyDetectionTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/alert/AnomalyDetectionTests.java
@@ -33,9 +33,9 @@
 import com.android.os.AtomsProto.AnomalyDetected;
 import com.android.os.AtomsProto.AppBreadcrumbReported;
 import com.android.os.AtomsProto.Atom;
+import com.android.os.AtomsProto.KernelWakelock;
 import com.android.os.StatsLog.EventMetricData;
 import com.android.tradefed.log.LogUtil.CLog;
-
 import java.util.List;
 
 /**
@@ -46,7 +46,7 @@
     private static final String TAG = "Statsd.AnomalyDetectionTests";
 
     private static final boolean INCIDENTD_TESTS_ENABLED = false;
-    private static final boolean PERFETTO_TESTS_ENABLED = false;
+    private static final boolean PERFETTO_TESTS_ENABLED = true;
 
     private static final int WAIT_AFTER_BREADCRUMB_MS = 2000;
 
@@ -279,14 +279,15 @@
                 return;
         }
 
+        if (PERFETTO_TESTS_ENABLED) resetPerfettoGuardrails();
+
         StatsdConfig.Builder config = getBaseConfig(4, 0, 6 /* threshold: value > 6 */)
                 .addSubscription(Subscription.newBuilder()
                         .setId(SUBSCRIPTION_ID_PERFETTO)
                         .setRuleType(Subscription.RuleType.ALERT)
                         .setRuleId(ALERT_ID)
                         .setPerfettoDetails(PerfettoDetails.newBuilder()
-                                .setTraceConfig(createPerfettoTraceConfig())
-                        )
+                                .setTraceConfig(getPerfettoConfig()))
                 )
                 .addValueMetric(ValueMetric.newBuilder()
                         .setId(METRIC_ID)
@@ -306,7 +307,7 @@
         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("Pefetto", didPerfettoStartSince(markTime));
+        if (PERFETTO_TESTS_ENABLED) assertFalse(isSystemTracingEnabled());
 
         doAppBreadcrumbReportedStart(14); // value = 14 > trigger
         Thread.sleep(WAIT_AFTER_BREADCRUMB_MS);
@@ -315,7 +316,7 @@
         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("No perfetto", didPerfettoStartSince(markTime));
+        if (PERFETTO_TESTS_ENABLED) assertTrue(isSystemTracingEnabled());
     }
 
     // Tests that anomaly detection for gauge works.
@@ -356,6 +357,54 @@
         if (INCIDENTD_TESTS_ENABLED) assertTrue("No incident", didIncidentdFireSince(markTime));
     }
 
+    // Test that anomaly detection for pulled metrics work.
+    public void testPulledAnomalyDetection() throws Exception {
+        if (statsdDisabled()) {
+            return;
+        }
+
+        final int ATOM_ID = Atom.KERNEL_WAKELOCK_FIELD_NUMBER;  // A pulled atom
+        final int SLICE_BY_FIELD = KernelWakelock.NAME_FIELD_NUMBER;
+        final int VALUE_FIELD = KernelWakelock.VERSION_FIELD_NUMBER;  // Something that will be > 0.
+        final int ATOM_MATCHER_ID = 300;
+
+        StatsdConfig.Builder config = getBaseConfig(10, 20, 0 /* threshold: value > 0 */)
+                .addAllowedLogSource("AID_SYSTEM")
+                // Track the ATOM_ID pulled atom
+                .addAtomMatcher(StatsdConfigProto.AtomMatcher.newBuilder()
+                        .setId(ATOM_MATCHER_ID)
+                        .setSimpleAtomMatcher(StatsdConfigProto.SimpleAtomMatcher.newBuilder()
+                                .setAtomId(ATOM_ID)))
+                .addGaugeMetric(GaugeMetric.newBuilder()
+                        .setId(METRIC_ID)
+                        .setWhat(ATOM_MATCHER_ID)
+                        .setBucket(TimeUnit.CTS)
+                        .setSamplingType(GaugeMetric.SamplingType.RANDOM_ONE_SAMPLE)
+                        // Slice by SLICE_BY_FIELD (typical usecase)
+                        .setDimensionsInWhat(FieldMatcher.newBuilder()
+                                .setField(ATOM_ID)
+                                .addChild(FieldMatcher.newBuilder().setField(SLICE_BY_FIELD))
+                        )
+                        // Track the VALUE_FIELD (anomaly detection requires exactly one field here)
+                        .setGaugeFieldsFilter(
+                                FieldFilter.newBuilder().setFields(FieldMatcher.newBuilder()
+                                        .setField(ATOM_ID)
+                                        .addChild(FieldMatcher.newBuilder().setField(VALUE_FIELD))
+                                )
+                        )
+                );
+        uploadConfig(config);
+
+        Thread.sleep(6_000); // Wait long enough to ensure AlarmManager signals >= 1 pull
+
+        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());
+    }
+
+
     private final StatsdConfig.Builder getBaseConfig(int numBuckets,
                                                      int refractorySecs,
                                                      long triggerIfSumGt) throws Exception {
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
index 225ebf5..e33d42c 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
@@ -20,7 +20,8 @@
 
 import android.os.BatteryStatsProto;
 import android.service.batterystats.BatteryStatsServiceDumpProto;
-import android.view.DisplayStateEnum;
+import android.service.procstats.ProcessStatsProto;
+import android.service.procstats.ProcessStatsServiceDumpProto;
 
 import com.android.annotations.Nullable;
 import com.android.internal.os.StatsdConfigProto.AtomMatcher;
@@ -36,9 +37,10 @@
 import com.android.internal.os.StatsdConfigProto.TimeUnit;
 import com.android.os.AtomsProto.AppBreadcrumbReported;
 import com.android.os.AtomsProto.Atom;
-import com.android.os.AtomsProto.ScreenStateChanged;
 import com.android.os.StatsLog.ConfigMetricsReport;
 import com.android.os.StatsLog.ConfigMetricsReportList;
+import com.android.os.StatsLog.DurationMetricData;
+import com.android.os.StatsLog.ValueMetricData;
 import com.android.os.StatsLog.EventMetricData;
 import com.android.os.StatsLog.GaugeMetricData;
 import com.android.os.StatsLog.StatsLogReport;
@@ -46,6 +48,9 @@
 import com.android.tradefed.log.LogUtil;
 
 import com.google.common.io.Files;
+import com.google.protobuf.ByteString;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
 
 import java.io.File;
 import java.text.SimpleDateFormat;
@@ -57,11 +62,6 @@
 import java.util.Set;
 import java.util.function.Function;
 
-import perfetto.protos.PerfettoConfig.DataSourceConfig;
-import perfetto.protos.PerfettoConfig.TraceConfig;
-import perfetto.protos.PerfettoConfig.TraceConfig.BufferConfig;
-import perfetto.protos.PerfettoConfig.TraceConfig.DataSource;
-
 /**
  * Base class for testing Statsd atoms.
  * Validates reporting of statsd logging based on different events
@@ -71,6 +71,7 @@
     public static final String UPDATE_CONFIG_CMD = "cmd stats config update";
     public static final String DUMP_REPORT_CMD = "cmd stats dump-report";
     public static final String DUMP_BATTERYSTATS_CMD = "dumpsys batterystats";
+    public static final String DUMP_PROCSTATS_CMD = "dumpsys procstats";
     public static final String REMOVE_CONFIG_CMD = "cmd stats config remove";
     public static final String CONFIG_UID = "1000";
     /** ID of the config, which evaluates to -1572883457. */
@@ -115,32 +116,58 @@
         return log.contains(INCIDENTD_STARTED_STRING);
     }
 
-    /**
-     * Determines whether logcat indicates that perfetto fired since the given device date.
-     */
-    protected boolean didPerfettoStartSince(String date) throws Exception {
-        final String PERFETTO_TAG = "perfetto";
-        final String PERFETTO_STARTED_STRING = "Enabled tracing";
-        final String PERFETTO_STARTED_REGEX = ".*" + PERFETTO_STARTED_STRING + ".*";
-        // TODO: Do something more robust than this in case of delayed logging.
-        Thread.sleep(1000);
-        String log = getLogcatSince(date, String.format(
-                "-s %s -e %s", PERFETTO_TAG, PERFETTO_STARTED_REGEX));
-        return log.contains(PERFETTO_STARTED_STRING);
-    }
-
     protected boolean checkDeviceFor(String methodName) throws Exception {
         try {
             installPackage(DEVICE_SIDE_TEST_APK, true);
             runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".Checkers", methodName);
             // Test passes, meaning that the answer is true.
+            LogUtil.CLog.d(methodName + "() indicates true.");
             return true;
         } catch (AssertionError e) {
             // Method is designed to fail if the answer is false.
+            LogUtil.CLog.d(methodName + "() indicates false.");
             return false;
         }
     }
 
+    /**
+     * Returns a protobuf-encoded perfetto config that enables the kernel
+     * ftrace tracer with sched_switch for 10 seconds.
+     * See https://android.googlesource.com/platform/external/perfetto/+/master/docs/trace-config.md
+     * for details on how to generate this.
+     */
+    protected ByteString getPerfettoConfig() {
+        return ByteString.copyFrom(new byte[] { 0xa, 0x3, 0x8, (byte) 0x80, 0x1, 0x12, 0x23, 0xa,
+                        0x21, 0xa, 0xc, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x2e, 0x66, 0x74, 0x72, 0x61,
+                        0x63, 0x65, 0x10, 0x0, (byte) 0xa2, 0x6, 0xe, 0xa, 0xc, 0x73, 0x63, 0x68,
+                        0x65, 0x64, 0x5f, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x18, (byte) 0x90,
+                        0x4e });
+    }
+
+    /**
+     * Resets the state of the Perfetto guardrails. This avoids that the test fails if it's
+     * run too close of for too many times and hits the upload limit.
+     */
+    protected void resetPerfettoGuardrails() throws Exception {
+        final String cmd = "perfetto --reset-guardrails";
+        CommandResult cr = getDevice().executeShellV2Command(cmd);
+        if (cr.getStatus() != CommandStatus.SUCCESS)
+            throw new Exception(String.format("Error while executing %s: %s %s", cmd, cr.getStdout(), cr.getStderr()));
+    }
+
+    /**
+     * Determines whether perfetto enabled the kernel ftrace tracer.
+     */
+    protected boolean isSystemTracingEnabled() throws Exception {
+        final String path = "/sys/kernel/debug/tracing/tracing_on";
+        String tracing_on = getDevice().executeShellCommand("cat " + path);
+        if (tracing_on.startsWith("0"))
+            return false;
+        if (tracing_on.startsWith("1"))
+            return true;
+        throw new Exception(String.format("Unexpected state for %s = %s", path, tracing_on));
+    }
+
     protected static StatsdConfig.Builder createConfigBuilder() {
         return StatsdConfig.newBuilder().setId(CONFIG_ID)
                 .addAllowedLogSource("AID_SYSTEM")
@@ -216,6 +243,48 @@
         return data;
     }
 
+    /**
+     * Gets the statsd report and extract duration metric data.
+     * Note that this also deletes that report from statsd.
+     */
+    protected List<DurationMetricData> getDurationMetricDataList() throws Exception {
+        ConfigMetricsReportList reportList = getReportList();
+        assertTrue("Expected one report", reportList.getReportsCount() == 1);
+        ConfigMetricsReport report = reportList.getReports(0);
+
+        List<DurationMetricData> data = new ArrayList<>();
+        for (StatsLogReport metric : report.getMetricsList()) {
+            data.addAll(metric.getDurationMetrics().getDataList());
+        }
+
+        LogUtil.CLog.d("Got DurationMetricDataList as following:\n");
+        for (DurationMetricData d : data) {
+            LogUtil.CLog.d("Duration " + d);
+        }
+        return data;
+    }
+
+    /**
+     * Gets the statsd report and extract value metric data.
+     * Note that this also deletes that report from statsd.
+     */
+    protected List<ValueMetricData> getValueMetricDataList() throws Exception {
+        ConfigMetricsReportList reportList = getReportList();
+        assertTrue("Expected one report", reportList.getReportsCount() == 1);
+        ConfigMetricsReport report = reportList.getReports(0);
+
+        List<ValueMetricData> data = new ArrayList<>();
+        for (StatsLogReport metric : report.getMetricsList()) {
+            data.addAll(metric.getValueMetrics().getDataList());
+        }
+
+        LogUtil.CLog.d("Got ValueMetricDataList as following:\n");
+        for (ValueMetricData d : data) {
+            LogUtil.CLog.d("Value " + d);
+        }
+        return data;
+    }
+
     protected StatsLogReport getStatsLogReport() throws Exception {
         ConfigMetricsReportList reportList = getReportList();
         assertTrue(reportList.getReportsCount() == 1);
@@ -230,7 +299,7 @@
         try {
             ConfigMetricsReportList reportList = getDump(ConfigMetricsReportList.parser(),
                     String.join(" ", DUMP_REPORT_CMD, String.valueOf(CONFIG_ID),
-                            "--proto"));
+                            "--include_current_bucket", "--proto"));
             return reportList;
         } catch (com.google.protobuf.InvalidProtocolBufferException e) {
             LogUtil.CLog.e("Failed to fetch and parse the statsd output report. "
@@ -253,24 +322,28 @@
         }
     }
 
+    protected List<ProcessStatsProto> getProcStatsProto() throws Exception {
+        try {
+            List<ProcessStatsProto> processStatsProtoList = getDump(
+                    ProcessStatsServiceDumpProto.parser(),
+                    String.join(" ", DUMP_PROCSTATS_CMD,
+                            "--proto")).getProcstatsNow().getProcessStatsList();
+            LogUtil.CLog.d("Got procstats:\n ");
+            for (ProcessStatsProto processStatsProto : processStatsProtoList) {
+                LogUtil.CLog.d(processStatsProto.toString());
+            }
+            return processStatsProtoList;
+        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+            LogUtil.CLog.e("Failed to dump procstats proto");
+            throw (e);
+        }
+    }
+
     /** Creates a FieldValueMatcher.Builder corresponding to the given field. */
     protected static FieldValueMatcher.Builder createFvm(int field) {
         return FieldValueMatcher.newBuilder().setField(field);
     }
 
-    protected static TraceConfig createPerfettoTraceConfig() {
-        return TraceConfig.newBuilder()
-            .addBuffers(BufferConfig.newBuilder().setSizeKb(32))
-            .addDataSources(DataSource.newBuilder()
-                .setConfig(DataSourceConfig.newBuilder()
-                    .setName("linux.ftrace")
-                    .setTargetBuffer(0)
-                    .build()
-                )
-            )
-            .build();
-    }
-
     protected void addAtomEvent(StatsdConfig.Builder conf, int atomTag) throws Exception {
         addAtomEvent(conf, atomTag, new ArrayList<FieldValueMatcher.Builder>());
     }
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/BaseTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/atom/BaseTestCase.java
index cd396ec..bfd69b7 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/BaseTestCase.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/BaseTestCase.java
@@ -92,6 +92,10 @@
         assertNull("Failed to install " + appFileName + ": " + result, result);
     }
 
+    protected CompatibilityBuildHelper getBuildHelper() {
+        return new CompatibilityBuildHelper(mCtsBuild);
+    }
+
     /**
      * Run a device side test.
      *
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/DeviceAtomTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/atom/DeviceAtomTestCase.java
index c14a89f..b4af492 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/DeviceAtomTestCase.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/DeviceAtomTestCase.java
@@ -221,4 +221,8 @@
     protected void resetBatteryStats() throws Exception {
         getDevice().executeShellCommand("dumpsys batterystats --reset");
     }
+
+    protected void clearProcStats() throws Exception {
+        getDevice().executeShellCommand("dumpsys procstats --clear");
+    }
 }
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java
index cb0f758..f567cd9 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java
@@ -31,29 +31,10 @@
 /**
  * Statsd atom tests that are done via app, for atoms that report a uid.
  */
-public class ProcStateAtomTests extends DeviceAtomTestCase {
+public class ProcStateAtomTests extends ProcStateTestCase {
 
     private static final String TAG = "Statsd.ProcStateAtomTests";
 
-    private static final String DEVICE_SIDE_BG_SERVICE_COMPONENT
-            = "com.android.server.cts.device.statsd/.StatsdCtsBackgroundService";
-    private static final String DEVICE_SIDE_FG_ACTIVITY_COMPONENT
-            = "com.android.server.cts.device.statsd/.StatsdCtsForegroundActivity";
-    private static final String DEVICE_SIDE_FG_SERVICE_COMPONENT
-            = "com.android.server.cts.device.statsd/.StatsdCtsForegroundService";
-
-    // Constants from the device-side tests (not directly accessible here).
-    public static final String KEY_ACTION = "action";
-    public static final String ACTION_END_IMMEDIATELY = "action.end_immediately";
-    public static final String ACTION_BACKGROUND_SLEEP = "action.background_sleep";
-    public static final String ACTION_SLEEP_WHILE_TOP = "action.sleep_top";
-    public static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay";
-
-    // Sleep times (ms) that actions invoke device-side.
-    public static final int SLEEP_OF_ACTION_SLEEP_WHILE_TOP = 2_000;
-    public static final int SLEEP_OF_ACTION_BACKGROUND_SLEEP = 2_000;
-    public static final int SLEEP_OF_FOREGROUND_SERVICE = 2_000;
-
     private static final int WAIT_TIME_FOR_CONFIG_UPDATE_MS = 200;
     // ActivityManager can take a while to register screen state changes, mandating an extra delay.
     private static final int WAIT_TIME_FOR_CONFIG_AND_SCREEN_MS = 1_000;
@@ -270,38 +251,6 @@
                 ALL_STATES.contains(ProcessStateEnum.PROCESS_STATE_UNKNOWN_TO_PROTO_VALUE));
     }
 
-    /**
-     * Runs a (background) service to perform the given action.
-     * @param actionValue the action code constants indicating the desired action to perform.
-     */
-    private void executeBackgroundService(String actionValue) throws Exception {
-        allowBackgroundServices();
-        getDevice().executeShellCommand(String.format(
-                "am startservice -n '%s' -e %s %s",
-                DEVICE_SIDE_BG_SERVICE_COMPONENT,
-                KEY_ACTION, actionValue));
-    }
-
-    /**
-     * Runs an activity (in the foreground) to perform the given action.
-     * @param actionValue the action code constants indicating the desired action to perform.
-     */
-    private void executeForegroundActivity(String actionValue) throws Exception {
-        getDevice().executeShellCommand(String.format(
-                "am start -n '%s' -e %s %s",
-                DEVICE_SIDE_FG_ACTIVITY_COMPONENT,
-                KEY_ACTION, actionValue));
-    }
-
-    /**
-     * Runs a simple foreground service.
-     */
-    private void executeForegroundService() throws Exception {
-        executeForegroundActivity(ACTION_END_IMMEDIATELY);
-        getDevice().executeShellCommand(String.format(
-                "am startservice -n '%s'", DEVICE_SIDE_FG_SERVICE_COMPONENT));
-    }
-
     /** Returns the a set containing elements of a that are not elements of b. */
     private Set<Integer> difference(Set<Integer> a, Set<Integer> b) {
         Set<Integer> result = new HashSet<Integer>(a);
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateTestCase.java
new file mode 100644
index 0000000..cd65a37
--- /dev/null
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateTestCase.java
@@ -0,0 +1,90 @@
+/*
+ * 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.cts.statsd.atom;
+
+import android.app.ProcessStateEnum; // From enums.proto for atoms.proto's UidProcessStateChanged.
+
+import com.android.os.AtomsProto.Atom;
+import com.android.os.StatsLog.EventMetricData;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Base class for manipulating process states
+ */
+public class ProcStateTestCase extends DeviceAtomTestCase {
+
+  private static final String TAG = "Statsd.ProcStateTestCase";
+
+  private static final String DEVICE_SIDE_BG_SERVICE_COMPONENT
+          = "com.android.server.cts.device.statsd/.StatsdCtsBackgroundService";
+  private static final String DEVICE_SIDE_FG_ACTIVITY_COMPONENT
+          = "com.android.server.cts.device.statsd/.StatsdCtsForegroundActivity";
+  private static final String DEVICE_SIDE_FG_SERVICE_COMPONENT
+          = "com.android.server.cts.device.statsd/.StatsdCtsForegroundService";
+
+  // Constants from the device-side tests (not directly accessible here).
+  public static final String KEY_ACTION = "action";
+  public static final String ACTION_END_IMMEDIATELY = "action.end_immediately";
+  public static final String ACTION_BACKGROUND_SLEEP = "action.background_sleep";
+  public static final String ACTION_SLEEP_WHILE_TOP = "action.sleep_top";
+  public static final String ACTION_LONG_SLEEP_WHILE_TOP = "action.long_sleep_top";
+  public static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay";
+
+  // Sleep times (ms) that actions invoke device-side.
+  public static final int SLEEP_OF_ACTION_SLEEP_WHILE_TOP = 2_000;
+  public static final int SLEEP_OF_ACTION_LONG_SLEEP_WHILE_TOP = 60_000;
+  public static final int SLEEP_OF_ACTION_BACKGROUND_SLEEP = 2_000;
+  public static final int SLEEP_OF_FOREGROUND_SERVICE = 2_000;
+
+  /**
+   * Runs a (background) service to perform the given action.
+   * @param actionValue the action code constants indicating the desired action to perform.
+   */
+  protected void executeBackgroundService(String actionValue) throws Exception {
+    allowBackgroundServices();
+    getDevice().executeShellCommand(String.format(
+            "am startservice -n '%s' -e %s %s",
+            DEVICE_SIDE_BG_SERVICE_COMPONENT,
+            KEY_ACTION, actionValue));
+  }
+
+  /**
+   * Runs an activity (in the foreground) to perform the given action.
+   * @param actionValue the action code constants indicating the desired action to perform.
+   */
+  protected void executeForegroundActivity(String actionValue) throws Exception {
+    getDevice().executeShellCommand(String.format(
+            "am start -n '%s' -e %s %s",
+            DEVICE_SIDE_FG_ACTIVITY_COMPONENT,
+            KEY_ACTION, actionValue));
+  }
+
+  /**
+   * Runs a simple foreground service.
+   */
+  protected void executeForegroundService() throws Exception {
+    executeForegroundActivity(ACTION_END_IMMEDIATELY);
+    getDevice().executeShellCommand(String.format(
+            "am startservice -n '%s'", DEVICE_SIDE_FG_SERVICE_COMPONENT));
+  }
+}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index 2dda663..36fbefe 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -345,24 +345,37 @@
         }
         if (!hasFeature(FEATURE_LOCATION_GPS, true)) return;
         // Whitelist this app against background location request throttling
+        String origWhitelist = getDevice().executeShellCommand(
+                "settings get global location_background_throttle_package_whitelist").trim();
         getDevice().executeShellCommand(String.format(
                 "settings put global location_background_throttle_package_whitelist %s",
                 DEVICE_SIDE_TEST_PACKAGE));
 
-        final int atom = Atom.GPS_SCAN_STATE_CHANGED_FIELD_NUMBER;
-        final int key = GpsScanStateChanged.STATE_FIELD_NUMBER;
-        final int stateOn = GpsScanStateChanged.State.ON_VALUE;
-        final int stateOff = GpsScanStateChanged.State.OFF_VALUE;
-        final int minTimeDiffMillis = 500;
-        final int maxTimeDiffMillis = 60_000;
+        try {
+            final int atom = Atom.GPS_SCAN_STATE_CHANGED_FIELD_NUMBER;
+            final int key = GpsScanStateChanged.STATE_FIELD_NUMBER;
+            final int stateOn = GpsScanStateChanged.State.ON_VALUE;
+            final int stateOff = GpsScanStateChanged.State.OFF_VALUE;
+            final int minTimeDiffMillis = 500;
+            final int maxTimeDiffMillis = 60_000;
 
-        List<EventMetricData> data = doDeviceMethodOnOff("testGpsScan", atom, key,
-                stateOn, stateOff, minTimeDiffMillis, maxTimeDiffMillis, true);
+            List<EventMetricData> data = doDeviceMethodOnOff("testGpsScan", atom, key,
+                    stateOn, stateOff, minTimeDiffMillis, maxTimeDiffMillis, true);
 
-        GpsScanStateChanged a0 = data.get(0).getAtom().getGpsScanStateChanged();
-        GpsScanStateChanged a1 = data.get(1).getAtom().getGpsScanStateChanged();
-        assertTrue(a0.getState().getNumber() == stateOn);
-        assertTrue(a1.getState().getNumber() == stateOff);
+            GpsScanStateChanged a0 = data.get(0).getAtom().getGpsScanStateChanged();
+            GpsScanStateChanged a1 = data.get(1).getAtom().getGpsScanStateChanged();
+            assertTrue(a0.getState().getNumber() == stateOn);
+            assertTrue(a1.getState().getNumber() == stateOff);
+        } finally {
+            if ("null".equals(origWhitelist) || "".equals(origWhitelist)) {
+                getDevice().executeShellCommand(
+                        "settings delete global location_background_throttle_package_whitelist");
+            } else {
+                getDevice().executeShellCommand(String.format(
+                        "settings put global location_background_throttle_package_whitelist %s",
+                        origWhitelist));
+            }
+        }
     }
 
     public void testOverlayState() throws Exception {
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java
index 9340fd2..ce95159b 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java
@@ -113,7 +113,7 @@
     int totalValue = 0;
     for (ValueBucketInfo bucketInfo : data.getBucketInfoList()) {
       MetricsUtils.assertBucketTimePresent(bucketInfo);
-      totalValue += (int) bucketInfo.getValue();
+      totalValue += (int) bucketInfo.getValueLong();
     }
     assertEquals(totalValue, 8);
   }
@@ -190,7 +190,7 @@
     int totalValue = 0;
     for (ValueBucketInfo bucketInfo : data.getBucketInfoList()) {
       MetricsUtils.assertBucketTimePresent(bucketInfo);
-      totalValue += (int) bucketInfo.getValue();
+      totalValue += (int) bucketInfo.getValueLong();
     }
     // At most we lose one full min bucket
     assertTrue(totalValue > (130_000 - 60_000));
@@ -272,7 +272,7 @@
     int totalValue = 0;
     for (ValueBucketInfo bucketInfo : data.getBucketInfoList()) {
       MetricsUtils.assertBucketTimePresent(bucketInfo);
-      totalValue += (int) bucketInfo.getValue();
+      totalValue += (int) bucketInfo.getValueLong();
     }
     // At most we lose one full min bucket
     assertTrue(totalValue > (GAP_INTERVAL*NUM_EVENTS - 60_000));
diff --git a/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java b/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java
new file mode 100644
index 0000000..1747856
--- /dev/null
+++ b/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java
@@ -0,0 +1,262 @@
+/*
+ * 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.cts.statsd.validation;
+
+import static org.junit.Assert.assertTrue;
+
+import android.cts.statsd.atom.ProcStateAtomTests;
+import android.cts.statsd.atom.ProcStateTestCase;
+import android.service.procstats.ProcessStatsProto;
+import android.service.procstats.ProcessStatsProto.State;
+import android.service.procstats.ProcessStatsProto.State.ProcessState;
+
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.os.StatsLog.DimensionsValue;
+import com.android.os.StatsLog.DurationBucketInfo;
+import com.android.os.StatsLog.DurationMetricData;
+import com.android.os.StatsLog.ValueBucketInfo;
+import com.android.os.StatsLog.ValueMetricData;
+import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.FileUtil;
+
+import com.google.protobuf.TextFormat;
+import com.google.protobuf.TextFormat.ParseException;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Side-by-side comparison between statsd and procstats.
+ */
+public class ProcStatsValidationTests extends ProcStateTestCase {
+
+    private static final String TAG = "Statsd.ProcStatsValidationTests";
+
+    private static final int EXTRA_WAIT_TIME_MS = 1_000; // as buffer when proc state changing.
+
+    private StatsdConfig getConfig(String fileName) throws IOException {
+        try {
+            // TODO: Ideally, we should use real metrics that are also pushed to the fleet.
+            File configFile = getBuildHelper().getTestFile(fileName);
+            String configStr = FileUtil.readStringFromFile(configFile);
+            StatsdConfig.Builder builder = StatsdConfig.newBuilder();
+            TextFormat.merge(configStr, builder);
+            return builder.build();
+        } catch (ParseException e) {
+            LogUtil.CLog.e(
+                    "Failed to parse the config! line: " + e.getLine() + " col: " + e.getColumn(),
+                    e);
+        }
+        return null;
+    }
+
+    // Test process state top duration for test package.
+    // TODO: replace this with exclusive state for all states when statsd features are added
+    public void testProcessStateTopDuration() throws Exception {
+        if (statsdDisabled()) {
+            return;
+        }
+        final String fileName = "PROCSTATSQ_PROCS_STATE_TOP_DURATION.pbtxt";
+        StatsdConfig config = getConfig(fileName);
+        LogUtil.CLog.d("Updating the following config:\n" + config.toString());
+        uploadConfig(config);
+        clearProcStats();
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        // foreground service
+        executeForegroundService();
+        Thread.sleep(SLEEP_OF_FOREGROUND_SERVICE + EXTRA_WAIT_TIME_MS);
+        // background
+        executeBackgroundService(ACTION_BACKGROUND_SLEEP);
+        Thread.sleep(SLEEP_OF_ACTION_BACKGROUND_SLEEP + EXTRA_WAIT_TIME_MS);
+        // top
+        executeForegroundActivity(ACTION_SLEEP_WHILE_TOP);
+        Thread.sleep(SLEEP_OF_ACTION_SLEEP_WHILE_TOP + EXTRA_WAIT_TIME_MS);
+        // Start extremely short-lived activity, so app goes into cache state (#1 - #3 above).
+        executeBackgroundService(ACTION_END_IMMEDIATELY);
+        final int cacheTime = 2_000; // process should be in cached state for up to this long
+        Thread.sleep(cacheTime);
+        // foreground
+        // overlay should take 2 sec to appear. So this makes it 4 sec in TOP
+        executeForegroundActivity(ACTION_SHOW_APPLICATION_OVERLAY);
+        Thread.sleep(EXTRA_WAIT_TIME_MS + 5_000);
+
+        // Sorted list of events in order in which they occurred.
+        List<DurationMetricData> statsdData = getDurationMetricDataList();
+
+        List<ProcessStatsProto> processStatsProtoList = getProcStatsProto();
+
+        LogUtil.CLog.d("======================");
+
+        String statsdPkgName = "com.android.server.cts.device.statsd";
+        double durationInTopStatsd = 0;
+        for (DurationMetricData d : statsdData) {
+            List<DimensionsValue> dimensionsValuesInWhat = d.getDimensionLeafValuesInWhatList();
+            if (dimensionsValuesInWhat.get(0).getValueStr().equals(statsdPkgName)
+                    && dimensionsValuesInWhat.get(1).getValueStr().equals(statsdPkgName)) {
+                LogUtil.CLog.d(d.toString());
+                for (DurationBucketInfo bucket : d.getBucketInfoList()) {
+                    durationInTopStatsd += bucket.getDurationNanos() / 1000 / 1000;
+                }
+            }
+        }
+
+        double durationInTopProcStats = 0;
+        for (ProcessStatsProto p : processStatsProtoList) {
+            if (p.getProcess().equals(statsdPkgName)) {
+                LogUtil.CLog.d(p.toString());
+                for (State s : p.getStatesList()) {
+                    if (s.getProcessState() == ProcessState.TOP) {
+                        durationInTopProcStats += s.getDurationMs();
+                    }
+                }
+            }
+        }
+        // Verify that duration is within 1% difference
+        assertTrue(Math.abs(durationInTopStatsd - durationInTopProcStats) / durationInTopProcStats
+                < 0.1);
+    }
+
+    public void testProcessStateCachedEmptyDuration() throws Exception {
+        if (statsdDisabled()) {
+            return;
+        }
+        final String fileName = "PROCSTATSQ_PROCS_STATE_CACHED_EMPTY_DURATION.pbtxt";
+        StatsdConfig config = getConfig(fileName);
+        LogUtil.CLog.d("Updating the following config:\n" + config.toString());
+        uploadConfig(config);
+        clearProcStats();
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        // foreground service
+        executeForegroundService();
+        Thread.sleep(SLEEP_OF_FOREGROUND_SERVICE + EXTRA_WAIT_TIME_MS);
+        // background
+        executeBackgroundService(ACTION_BACKGROUND_SLEEP);
+        Thread.sleep(SLEEP_OF_ACTION_BACKGROUND_SLEEP + EXTRA_WAIT_TIME_MS);
+        // top
+        executeForegroundActivity(ACTION_SLEEP_WHILE_TOP);
+        Thread.sleep(SLEEP_OF_ACTION_SLEEP_WHILE_TOP + EXTRA_WAIT_TIME_MS);
+        // Start extremely short-lived activity, so app goes into cache state (#1 - #3 above).
+        executeBackgroundService(ACTION_END_IMMEDIATELY);
+        final int cacheTime = 2_000; // process should be in cached state for up to this long
+        Thread.sleep(cacheTime);
+        // foreground
+        // overlay should take 2 sec to appear. So this makes it 4 sec in TOP
+        executeForegroundActivity(ACTION_SHOW_APPLICATION_OVERLAY);
+        Thread.sleep(EXTRA_WAIT_TIME_MS + 5_000);
+
+        // Sorted list of events in order in which they occurred.
+        List<DurationMetricData> statsdData = getDurationMetricDataList();
+
+        List<ProcessStatsProto> processStatsProtoList = getProcStatsProto();
+
+        LogUtil.CLog.d("======================");
+
+        String statsdPkgName = "com.android.server.cts.device.statsd";
+        double durationInStatsd = 0;
+        for (DurationMetricData d : statsdData) {
+            List<DimensionsValue> dimensionsValuesInWhat = d.getDimensionLeafValuesInWhatList();
+            if (dimensionsValuesInWhat.get(0).getValueStr().equals(statsdPkgName)
+                    && dimensionsValuesInWhat.get(1).getValueStr().equals(statsdPkgName)) {
+                LogUtil.CLog.d(d.toString());
+                for (DurationBucketInfo bucket : d.getBucketInfoList()) {
+                    durationInStatsd += bucket.getDurationNanos() / 1000 / 1000;
+                }
+            }
+        }
+
+        double durationInProcStats = 0;
+        for (ProcessStatsProto p : processStatsProtoList) {
+            if (p.getProcess().equals(statsdPkgName)) {
+                LogUtil.CLog.d(p.toString());
+                for (State s : p.getStatesList()) {
+                    if (s.getProcessState() == ProcessState.CACHED_EMPTY) {
+                        durationInProcStats += s.getDurationMs();
+                    }
+                }
+            }
+        }
+        // Verify that duration is within 1% difference
+        assertTrue(Math.abs(durationInStatsd - durationInProcStats) / durationInProcStats
+                < 0.1);
+    }
+
+    public void testProcessStatePssValue() throws Exception {
+        if (statsdDisabled()) {
+            return;
+        }
+        final String fileName = "PROCSTATSQ_PROCS_STATE_PSS_VALUE.pbtxt";
+        StatsdConfig config = getConfig(fileName);
+        LogUtil.CLog.d("Updating the following config:\n" + config.toString());
+        uploadConfig(config);
+        clearProcStats();
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        // foreground service
+        executeForegroundService();
+        Thread.sleep(SLEEP_OF_FOREGROUND_SERVICE + EXTRA_WAIT_TIME_MS);
+        // background
+        executeBackgroundService(ACTION_BACKGROUND_SLEEP);
+        Thread.sleep(SLEEP_OF_ACTION_BACKGROUND_SLEEP + EXTRA_WAIT_TIME_MS);
+        // top
+        executeForegroundActivity(ACTION_LONG_SLEEP_WHILE_TOP);
+        Thread.sleep(SLEEP_OF_ACTION_LONG_SLEEP_WHILE_TOP + EXTRA_WAIT_TIME_MS);
+        // Start extremely short-lived activity, so app goes into cache state (#1 - #3 above).
+        executeBackgroundService(ACTION_END_IMMEDIATELY);
+        final int cacheTime = 2_000; // process should be in cached state for up to this long
+        Thread.sleep(cacheTime);
+        // foreground
+        // overlay should take 2 sec to appear. So this makes it 4 sec in TOP
+        executeForegroundActivity(ACTION_SHOW_APPLICATION_OVERLAY);
+        Thread.sleep(EXTRA_WAIT_TIME_MS + 5_000);
+
+        // Sorted list of events in order in which they occurred.
+        List<ValueMetricData> statsdData = getValueMetricDataList();
+
+        List<ProcessStatsProto> processStatsProtoList = getProcStatsProto();
+
+        LogUtil.CLog.d("======================");
+
+        String statsdPkgName = "com.android.server.cts.device.statsd";
+        double valueInStatsd = 0;
+        for (ValueMetricData d : statsdData) {
+            List<DimensionsValue> dimensionsValuesInWhat = d.getDimensionLeafValuesInWhatList();
+            if (dimensionsValuesInWhat.get(0).getValueStr().equals(statsdPkgName)
+                    && dimensionsValuesInWhat.get(1).getValueStr().equals(statsdPkgName)) {
+                LogUtil.CLog.d(d.toString());
+                for (ValueBucketInfo bucket : d.getBucketInfoList()) {
+                    valueInStatsd = Math.max(bucket.getValueLong(), valueInStatsd);
+                }
+            }
+        }
+
+        double valueInProcStats = 0;
+        for (ProcessStatsProto p : processStatsProtoList) {
+            if (p.getProcess().equals(statsdPkgName)) {
+                LogUtil.CLog.d(p.toString());
+                for (State s : p.getStatesList()) {
+                    valueInProcStats = Math.max(s.getPss().getMax(), valueInProcStats);
+                }
+            }
+        }
+        assertTrue(valueInProcStats > 0);
+        assertTrue(valueInStatsd == valueInProcStats);
+    }
+}
diff --git a/hostsidetests/sustainedperf/shadertoy_android/Android.mk b/hostsidetests/sustainedperf/shadertoy_android/Android.mk
index 2e1bbdd..190ce5d 100644
--- a/hostsidetests/sustainedperf/shadertoy_android/Android.mk
+++ b/hostsidetests/sustainedperf/shadertoy_android/Android.mk
@@ -31,6 +31,7 @@
 LOCAL_PACKAGE_NAME := CtsSustainedPerformanceTestCases
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 5
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/hostsidetests/theme/app/Android.mk b/hostsidetests/theme/app/Android.mk
index de63355..c2cc382 100644
--- a/hostsidetests/theme/app/Android.mk
+++ b/hostsidetests/theme/app/Android.mk
@@ -39,5 +39,6 @@
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
 LOCAL_SDK_VERSION := 23
+#LOCAL_MIN_SDK_VERSION := 17
 
 include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/theme/app/src/android/theme/app/ThemeDeviceActivity.java b/hostsidetests/theme/app/src/android/theme/app/ThemeDeviceActivity.java
index 8709f14..9469a0c 100644
--- a/hostsidetests/theme/app/src/android/theme/app/ThemeDeviceActivity.java
+++ b/hostsidetests/theme/app/src/android/theme/app/ThemeDeviceActivity.java
@@ -18,6 +18,7 @@
 
 import android.app.Activity;
 import android.content.Intent;
+import android.content.res.Configuration;
 import android.os.Build;
 import android.os.Bundle;
 import android.theme.app.modifiers.DatePickerModifier;
@@ -76,6 +77,11 @@
         mTheme = THEMES[themeIndex];
 
         setTheme(mTheme.id);
+
+        // Force text scaling to 1.0 regardless of system default.
+        Configuration config = new Configuration();
+        config.fontScale = 1.0f;
+        getResources().updateConfiguration(config, null);
         setContentView(R.layout.theme_test);
 
         mViewGroup = (ReferenceViewGroup) findViewById(R.id.reference_view_group);
diff --git a/hostsidetests/theme/assets/Q/260dpi.zip b/hostsidetests/theme/assets/Q/260dpi.zip
index 68423a45..ba5d363 100644
--- a/hostsidetests/theme/assets/Q/260dpi.zip
+++ b/hostsidetests/theme/assets/Q/260dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/280dpi.zip b/hostsidetests/theme/assets/Q/280dpi.zip
index 9a09eb4..34ea14c 100644
--- a/hostsidetests/theme/assets/Q/280dpi.zip
+++ b/hostsidetests/theme/assets/Q/280dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/300dpi.zip b/hostsidetests/theme/assets/Q/300dpi.zip
index 35390b9d..7595d24 100644
--- a/hostsidetests/theme/assets/Q/300dpi.zip
+++ b/hostsidetests/theme/assets/Q/300dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/340dpi.zip b/hostsidetests/theme/assets/Q/340dpi.zip
index a65cc9f..8ce960c 100644
--- a/hostsidetests/theme/assets/Q/340dpi.zip
+++ b/hostsidetests/theme/assets/Q/340dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/360dpi.zip b/hostsidetests/theme/assets/Q/360dpi.zip
index a7be2b6..aae3adc 100644
--- a/hostsidetests/theme/assets/Q/360dpi.zip
+++ b/hostsidetests/theme/assets/Q/360dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/400dpi.zip b/hostsidetests/theme/assets/Q/400dpi.zip
index f8e62bb..363d602 100644
--- a/hostsidetests/theme/assets/Q/400dpi.zip
+++ b/hostsidetests/theme/assets/Q/400dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/420dpi.zip b/hostsidetests/theme/assets/Q/420dpi.zip
index 3d70848..0f2ce47 100644
--- a/hostsidetests/theme/assets/Q/420dpi.zip
+++ b/hostsidetests/theme/assets/Q/420dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/440dpi.zip b/hostsidetests/theme/assets/Q/440dpi.zip
new file mode 100644
index 0000000..2328c61
--- /dev/null
+++ b/hostsidetests/theme/assets/Q/440dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/560dpi.zip b/hostsidetests/theme/assets/Q/560dpi.zip
index e1527ed..5f1bb0b 100644
--- a/hostsidetests/theme/assets/Q/560dpi.zip
+++ b/hostsidetests/theme/assets/Q/560dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/hdpi.zip b/hostsidetests/theme/assets/Q/hdpi.zip
index 59c6efc..6d82318 100644
--- a/hostsidetests/theme/assets/Q/hdpi.zip
+++ b/hostsidetests/theme/assets/Q/hdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/ldpi.zip b/hostsidetests/theme/assets/Q/ldpi.zip
index 8e408af..cc60027 100644
--- a/hostsidetests/theme/assets/Q/ldpi.zip
+++ b/hostsidetests/theme/assets/Q/ldpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/mdpi.zip b/hostsidetests/theme/assets/Q/mdpi.zip
index 9aebe9d..66d41d4 100644
--- a/hostsidetests/theme/assets/Q/mdpi.zip
+++ b/hostsidetests/theme/assets/Q/mdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/tvdpi.zip b/hostsidetests/theme/assets/Q/tvdpi.zip
index 55525fb..b43032f 100644
--- a/hostsidetests/theme/assets/Q/tvdpi.zip
+++ b/hostsidetests/theme/assets/Q/tvdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/xhdpi.zip b/hostsidetests/theme/assets/Q/xhdpi.zip
index 3fecabe..64905f3 100644
--- a/hostsidetests/theme/assets/Q/xhdpi.zip
+++ b/hostsidetests/theme/assets/Q/xhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/xxhdpi.zip b/hostsidetests/theme/assets/Q/xxhdpi.zip
index 14d7680..b2cb422 100644
--- a/hostsidetests/theme/assets/Q/xxhdpi.zip
+++ b/hostsidetests/theme/assets/Q/xxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/xxxhdpi.zip b/hostsidetests/theme/assets/Q/xxxhdpi.zip
index aa8d087..d00dbbd 100644
--- a/hostsidetests/theme/assets/Q/xxxhdpi.zip
+++ b/hostsidetests/theme/assets/Q/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 e559f18..05e0d95 100644
--- a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
+++ b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
@@ -273,7 +273,8 @@
         final Pattern p = Pattern.compile("Override density: (\\d+)");
         final Matcher m = p.matcher(output);
         if (m.find()) {
-            return Integer.parseInt(m.group(1));
+            throw new RuntimeException("Cannot test device running at non-default density: "
+                    + Integer.parseInt(m.group(1)));
         }
 
         final String densityProp;
diff --git a/hostsidetests/usage/AndroidTest.xml b/hostsidetests/usage/AndroidTest.xml
index 7078705..9ccc7e1 100644
--- a/hostsidetests/usage/AndroidTest.xml
+++ b/hostsidetests/usage/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS App Usage host 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="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsAppUsageTestApp.apk" />
diff --git a/hostsidetests/usb/SerialTestApp/Android.mk b/hostsidetests/usb/SerialTestApp/Android.mk
index 601c0ba..4baa577 100644
--- a/hostsidetests/usb/SerialTestApp/Android.mk
+++ b/hostsidetests/usb/SerialTestApp/Android.mk
@@ -29,6 +29,7 @@
 LOCAL_PACKAGE_NAME := CtsUsbSerialTestApp
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 27
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
diff --git a/hostsidetests/webkit/AndroidTest.xml b/hostsidetests/webkit/AndroidTest.xml
index 414fad4..927d28f 100644
--- a/hostsidetests/webkit/AndroidTest.xml
+++ b/hostsidetests/webkit/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS WebView host test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="webview" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsWebViewStartupApp.apk" />
diff --git a/hostsidetests/wifibroadcasts/Android.mk b/hostsidetests/wifibroadcasts/Android.mk
new file mode 100644
index 0000000..584cc4c
--- /dev/null
+++ b/hostsidetests/wifibroadcasts/Android.mk
@@ -0,0 +1,32 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE_TAGS := tests
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
+
+LOCAL_MODULE := CtsWifiBroadcastsHostTestCases
+
+LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed compatibility-host-util
+
+include $(BUILD_CTS_HOST_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/wifibroadcasts/AndroidTest.xml b/hostsidetests/wifibroadcasts/AndroidTest.xml
new file mode 100644
index 0000000..2572638
--- /dev/null
+++ b/hostsidetests/wifibroadcasts/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for CTS WifiBroadcasts host test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="misc" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsWifiBroadcastsDeviceApp.apk" />
+    </target_preparer>
+    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+        <option name="jar" value="CtsWifiBroadcastsHostTestCases.jar" />
+    </test>
+</configuration>
diff --git a/hostsidetests/wifibroadcasts/app/Android.mk b/hostsidetests/wifibroadcasts/app/Android.mk
new file mode 100644
index 0000000..ae4bcd1
--- /dev/null
+++ b/hostsidetests/wifibroadcasts/app/Android.mk
@@ -0,0 +1,37 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Don't include this package in any target
+LOCAL_MODULE_TAGS := tests
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_DEX_PREOPT := false
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
+
+LOCAL_PACKAGE_NAME := CtsWifiBroadcastsDeviceApp
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/wifibroadcasts/app/AndroidManifest.xml b/hostsidetests/wifibroadcasts/app/AndroidManifest.xml
new file mode 100644
index 0000000..7bc2d00
--- /dev/null
+++ b/hostsidetests/wifibroadcasts/app/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.wifibroadcasts.app">
+
+    <application>
+        <activity android:name=".WifiBroadcastsDeviceActivity" >
+            <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/wifibroadcasts/app/src/android/wifibroadcasts/app/WifiBroadcastsDeviceActivity.java b/hostsidetests/wifibroadcasts/app/src/android/wifibroadcasts/app/WifiBroadcastsDeviceActivity.java
new file mode 100644
index 0000000..996f1dfa
--- /dev/null
+++ b/hostsidetests/wifibroadcasts/app/src/android/wifibroadcasts/app/WifiBroadcastsDeviceActivity.java
@@ -0,0 +1,60 @@
+/*
+ * 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.wifibroadcasts.app;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Toast;
+
+/**
+ * Logs to Logcat when unexpected broadcasts are received
+ */
+public class WifiBroadcastsDeviceActivity extends Activity {
+
+    private static final String TAG = WifiBroadcastsDeviceActivity.class.getSimpleName();
+
+    private final Context mContext = this;
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            Toast.makeText(mContext, action, Toast.LENGTH_SHORT).show();
+            if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
+                Log.i(TAG, "UNEXPECTED WIFI BROADCAST RECEIVED - " + action);
+            }
+        }
+    };
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        final IntentFilter filter = new IntentFilter();
+        String action = WifiManager.RSSI_CHANGED_ACTION;
+        filter.addAction(action);
+        mContext.registerReceiver(mReceiver, filter);
+        Log.i(TAG, "Registered " + action);
+        Toast.makeText(mContext, "Started", Toast.LENGTH_SHORT).show();
+    }
+
+}
diff --git a/hostsidetests/wifibroadcasts/src/android/wifibroadcasts/cts/WifiBroadcastsHostJUnit4Test.java b/hostsidetests/wifibroadcasts/src/android/wifibroadcasts/cts/WifiBroadcastsHostJUnit4Test.java
new file mode 100644
index 0000000..4c36792
--- /dev/null
+++ b/hostsidetests/wifibroadcasts/src/android/wifibroadcasts/cts/WifiBroadcastsHostJUnit4Test.java
@@ -0,0 +1,118 @@
+/*
+ * 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.wifibroadcasts.cts;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.IDeviceTest;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+import java.util.Scanner;
+
+/**
+ * Test to check the APK logs to Logcat.
+ *
+ * When this test builds, it also builds
+ * {@link android.wifibroadcasts.app.WifiBroadcastsDeviceActivity} into an
+ * APK which is then installed at runtime and started. That activity prints a message to
+ * Logcat if an unexpected broadcast is received.
+ *
+ * Instead of extending DeviceTestCase, this JUnit4 test extends IDeviceTest and is run with
+ * tradefed's DeviceJUnit4ClassRunner
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class WifiBroadcastsHostJUnit4Test implements IDeviceTest {
+
+    /**
+     * The package name of the APK.
+     */
+    private static final String PACKAGE = "android.wifibroadcasts.app";
+
+    /**
+     * The class name of the main activity in the APK.
+     */
+    private static final String CLASS = "WifiBroadcastsDeviceActivity";
+
+    /**
+     * The command to launch the main activity.
+     */
+    private static final String START_COMMAND = String.format(
+            "am start -W -a android.intent.action.MAIN -n %s/%s.%s", PACKAGE, PACKAGE, CLASS);
+
+    /**
+     * The command to clear the main activity.
+     */
+    private static final String CLEAR_COMMAND = String.format("pm clear %s", PACKAGE);
+
+    /**
+     * The prohibited string to look for.
+     */
+    private static final String PROHIBITED_STRING = "UNEXPECTED WIFI BROADCAST RECEIVED";
+
+    private ITestDevice mDevice;
+
+    @Override
+    public void setDevice(ITestDevice device) {
+        mDevice = device;
+    }
+
+    @Override
+    public ITestDevice getDevice() {
+        return mDevice;
+    }
+
+    /**
+     * Tests the string was successfully logged to Logcat from the activity.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testLogcat() throws Exception {
+        ITestDevice device = getDevice();
+        assertNotNull("Device not set", device);
+        // Clear activity
+        device.executeShellCommand(CLEAR_COMMAND);
+        // No mobile data or wifi to start with
+        device.executeShellCommand("svc data disable; svc wifi disable");
+        // Clear logcat.
+        device.executeAdbCommand("logcat", "-c");
+        // Start the APK and wait for it to complete.
+        device.executeShellCommand(START_COMMAND);
+        // Bring up wifi for a while
+        device.executeShellCommand("svc wifi enable; sleep 10; svc wifi disable");
+        // Dump logcat.
+        String logs = device.executeAdbCommand("logcat", "-v", "brief", "-d", CLASS + ":I", "*:S");
+        // Search for prohibited string.
+        Scanner in = new Scanner(logs);
+        try {
+            while (in.hasNextLine()) {
+                String line = in.nextLine();
+                if (line.startsWith("I/" + CLASS)) {
+                    String payload = line.split(":")[1].trim();
+                    assertFalse(payload, payload.contains(PROHIBITED_STRING));
+                }
+            }
+        } finally {
+            in.close();
+        }
+    }
+}
diff --git a/libs/deviceutillegacy/Android.bp b/libs/deviceutillegacy/Android.bp
new file mode 100644
index 0000000..5221c43
--- /dev/null
+++ b/libs/deviceutillegacy/Android.bp
@@ -0,0 +1,29 @@
+// 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.
+
+java_library_static {
+    name: "ctsdeviceutillegacy",
+
+    static_libs: [
+        "compatibility-device-util",
+        "junit",
+    ],
+
+    libs: ["android.test.base.stubs"],
+
+    srcs: ["src/**/*.java"],
+
+    sdk_version: "test_current",
+
+}
diff --git a/libs/deviceutillegacy/src/android/webkit/cts/WebViewOnUiThread.java b/libs/deviceutillegacy/src/android/webkit/cts/WebViewOnUiThread.java
index 7db9a76..90628db 100644
--- a/libs/deviceutillegacy/src/android/webkit/cts/WebViewOnUiThread.java
+++ b/libs/deviceutillegacy/src/android/webkit/cts/WebViewOnUiThread.java
@@ -59,6 +59,10 @@
  * wraps a WebView so that calls are ensured to arrive on the UI thread.
  *
  * All methods may be run on either the UI thread or test thread.
+ *
+ * This should remain functionally equivalent to androidx.webkit.WebViewOnUiThread.
+ * Modifications to this class should be reflected in that class as necessary. See
+ * http://go/modifying-webview-cts.
  */
 public class WebViewOnUiThread {
     /**
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/DeviceIdleJobsTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
similarity index 85%
rename from tests/JobScheduler/src/android/jobscheduler/cts/DeviceIdleJobsTest.java
rename to tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
index e624a62..ffe684d 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/DeviceIdleJobsTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
@@ -43,6 +43,7 @@
 import android.util.Log;
 
 import com.android.compatibility.common.util.AppStandbyUtils;
+import com.android.compatibility.common.util.BatteryUtils;
 
 import org.junit.After;
 import org.junit.Before;
@@ -50,12 +51,12 @@
 import org.junit.runner.RunWith;
 
 /**
- * Tests that temp whitelisted apps can run jobs if all the other constraints are met
+ * Tests related to job throttling -- device idle, app standby and battery saver.
  */
 @RunWith(AndroidJUnit4.class)
 @LargeTest
-public class DeviceIdleJobsTest {
-    private static final String TAG = DeviceIdleJobsTest.class.getSimpleName();
+public class JobThrottlingTest {
+    private static final String TAG = JobThrottlingTest.class.getSimpleName();
     private static final String TEST_APP_PACKAGE = "android.jobscheduler.cts.jobtestapp";
     private static final String TEST_APP_RECEIVER = TEST_APP_PACKAGE + ".TestJobSchedulerReceiver";
     private static final String TEST_APP_ACTIVITY = TEST_APP_PACKAGE + ".TestActivity";
@@ -100,7 +101,7 @@
                     break;
                 case ACTION_DEVICE_IDLE_MODE_CHANGED:
                 case ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED:
-                    synchronized (DeviceIdleJobsTest.this) {
+                    synchronized (JobThrottlingTest.this) {
                         mDeviceInDoze = mPowerManager.isDeviceIdleMode();
                         Log.d(TAG, "mDeviceInDoze: " + mDeviceInDoze);
                     }
@@ -151,7 +152,7 @@
         assertFalse("Job started without being tempwhitelisted", awaitJobStart(5_000));
         tempWhitelistTestApp(5_000);
         assertTrue("Job with allow_while_idle flag did not start when the app was tempwhitelisted",
-                awaitJobStart(DEFAULT_WAIT_TIMEOUT));
+                awaitJobStart(5_000));
     }
 
     @Test
@@ -190,24 +191,71 @@
     public void testJobsInNeverApp() throws Exception {
         assumeTrue("app standby not enabled", mAppStandbyEnabled);
 
-        enterFakeUnpluggedState();
+        BatteryUtils.runDumpsysBatteryUnplug();
         setTestPackageStandbyBucket(Bucket.NEVER);
         Thread.sleep(DEFAULT_WAIT_TIMEOUT);
         sendScheduleJobBroadcast(false);
         assertFalse("New job started in NEVER standby", awaitJobStart(3_000));
-        resetFakeUnpluggedState();
     }
 
     @Test
     public void testUidActiveBypassesStandby() throws Exception {
-        enterFakeUnpluggedState();
+        BatteryUtils.runDumpsysBatteryUnplug();
         setTestPackageStandbyBucket(Bucket.NEVER);
         tempWhitelistTestApp(6_000);
         Thread.sleep(DEFAULT_WAIT_TIMEOUT);
         sendScheduleJobBroadcast(false);
         assertTrue("New job in uid-active app failed to start in NEVER standby",
                 awaitJobStart(4_000));
-        resetFakeUnpluggedState();
+    }
+
+    @Test
+    public void testBatterySaverOff() throws Exception {
+        BatteryUtils.assumeBatterySaverFeature();
+
+        BatteryUtils.runDumpsysBatteryUnplug();
+        BatteryUtils.enableBatterySaver(false);
+        sendScheduleJobBroadcast(false);
+        assertTrue("New job failed to start with battery saver OFF", awaitJobStart(3_000));
+    }
+
+    @Test
+    public void testBatterySaverOn() throws Exception {
+        BatteryUtils.assumeBatterySaverFeature();
+
+        BatteryUtils.runDumpsysBatteryUnplug();
+        BatteryUtils.enableBatterySaver(true);
+        sendScheduleJobBroadcast(false);
+        assertFalse("New job started with battery saver ON", awaitJobStart(3_000));
+    }
+
+    @Test
+    public void testUidActiveBypassesBatterySaverOn() throws Exception {
+        BatteryUtils.assumeBatterySaverFeature();
+
+        BatteryUtils.runDumpsysBatteryUnplug();
+        BatteryUtils.enableBatterySaver(true);
+        tempWhitelistTestApp(6_000);
+        sendScheduleJobBroadcast(false);
+        assertTrue("New job in uid-active app failed to start with battery saver OFF",
+                awaitJobStart(3_000));
+    }
+
+    @Test
+    public void testBatterySaverOnThenUidActive() throws Exception {
+        BatteryUtils.assumeBatterySaverFeature();
+
+        // Enable battery saver, and schedule a job. It shouldn't run.
+        BatteryUtils.runDumpsysBatteryUnplug();
+        BatteryUtils.enableBatterySaver(true);
+        sendScheduleJobBroadcast(false);
+        assertFalse("New job started with battery saver ON", awaitJobStart(3_000));
+
+
+        // Then make the UID active. Now the job should run.
+        tempWhitelistTestApp(120_000);
+        assertTrue("New job in uid-active app failed to start with battery saver OFF",
+                awaitJobStart(120_000));
     }
 
     @After
@@ -221,6 +269,8 @@
         mContext.sendBroadcast(cancelJobsIntent);
         mContext.sendBroadcast(new Intent(TestActivity.ACTION_FINISH_ACTIVITY));
         mContext.unregisterReceiver(mReceiver);
+        BatteryUtils.runDumpsysBatteryReset();
+
         Thread.sleep(500); // To avoid any race between unregister and the next register in setUp
         waitUntilTestAppNotInTempWhitelist();
     }
@@ -255,7 +305,7 @@
         mUiDevice.executeShellCommand("cmd deviceidle " + (idle ? "force-idle" : "unforce"));
         assertTrue("Could not change device idle state to " + idle,
                 waitUntilTrue(SHELL_TIMEOUT, () -> {
-                    synchronized (DeviceIdleJobsTest.this) {
+                    synchronized (JobThrottlingTest.this) {
                         return mDeviceInDoze == idle;
                     }
                 }));
@@ -286,14 +336,6 @@
                 + " " + bucketName);
     }
 
-    private void enterFakeUnpluggedState() throws Exception {
-        mUiDevice.executeShellCommand("dumpsys battery unplug");
-    }
-
-    private void resetFakeUnpluggedState() throws Exception  {
-        mUiDevice.executeShellCommand("dumpsys battery reset");
-    }
-
     private boolean waitUntilTestAppNotInTempWhitelist() throws Exception {
         long now;
         boolean interrupted = false;
diff --git a/tests/JobSchedulerSharedUid/jobperm/Android.mk b/tests/JobSchedulerSharedUid/jobperm/Android.mk
index 8be235f..a95f0b3 100644
--- a/tests/JobSchedulerSharedUid/jobperm/Android.mk
+++ b/tests/JobSchedulerSharedUid/jobperm/Android.mk
@@ -20,6 +20,7 @@
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-annotations \
     compatibility-device-util \
 
 LOCAL_SRC_FILES := \
diff --git a/tests/JobSchedulerSharedUid/jobperm/src/android/jobscheduler/cts/shareduid/jobperm/JobPermProvider.java b/tests/JobSchedulerSharedUid/jobperm/src/android/jobscheduler/cts/shareduid/jobperm/JobPermProvider.java
index 5c6b4b2..330ccac 100644
--- a/tests/JobSchedulerSharedUid/jobperm/src/android/jobscheduler/cts/shareduid/jobperm/JobPermProvider.java
+++ b/tests/JobSchedulerSharedUid/jobperm/src/android/jobscheduler/cts/shareduid/jobperm/JobPermProvider.java
@@ -16,8 +16,6 @@
 
 package android.jobscheduler.cts.shareduid.jobperm;
 
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.content.ContentProvider;
 import android.content.ContentValues;
 import android.content.Intent;
@@ -25,6 +23,8 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 
 import java.io.File;
 import java.io.FileNotFoundException;
diff --git a/tests/accessibility/AndroidManifest.xml b/tests/accessibility/AndroidManifest.xml
index da993ed..6063e0b 100644
--- a/tests/accessibility/AndroidManifest.xml
+++ b/tests/accessibility/AndroidManifest.xml
@@ -54,6 +54,12 @@
             <meta-data android:name="android.accessibilityservice"
                        android:resource="@xml/speaking_and_vibrating_accessibilityservice" />
         </service>
+
+        <activity
+            android:label="@string/some_description"
+            android:name=".DummyActivity"
+            android:screenOrientation="locked"/>
+
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/accessibility/AndroidTest.xml b/tests/accessibility/AndroidTest.xml
index 6d5bac28..c221e56 100644
--- a/tests/accessibility/AndroidTest.xml
+++ b/tests/accessibility/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS Accessibility 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="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="cmd accessibility set-bind-instant-service-allowed true" />
         <option name="teardown-command" value="cmd accessibility set-bind-instant-service-allowed false" />
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityDelegateTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityDelegateTest.java
new file mode 100644
index 0000000..a3efbe8
--- /dev/null
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityDelegateTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility.cts;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+import android.view.View.AccessibilityDelegate;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.LinearLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests AccessibilityDelegate functionality
+ */
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityDelegateTest {
+
+    private LinearLayout mParentView;
+    private View mChildView;
+
+    @Rule
+    public ActivityTestRule<DummyActivity> mActivityRule =
+            new ActivityTestRule<>(DummyActivity.class, false, false);
+
+    @Before
+    public void setUp() throws Exception {
+        Activity activity = mActivityRule.launchActivity(null);
+        LinearLayout grandparent = new LinearLayout(activity);
+        mParentView = new LinearLayout(activity);
+        mChildView = new View(activity);
+        grandparent.addView(mParentView);
+        mParentView.addView(mChildView);
+    }
+
+    @Test
+    public void testAccessibilityDelegateGetAndSet() {
+        AccessibilityDelegate delegate = new AccessibilityDelegate();
+        mParentView.setAccessibilityDelegate(delegate);
+        assertThat(mParentView.getAccessibilityDelegate(), is(equalTo(delegate)));
+    }
+
+    @Test
+    public void testViewDelegatesToAccessibilityDelegate() {
+        AccessibilityDelegate mockDelegate = mock(AccessibilityDelegate.class);
+        mParentView.setAccessibilityDelegate(mockDelegate);
+        final AccessibilityEvent event = AccessibilityEvent.obtain();
+
+        mParentView.sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT);
+        verify(mockDelegate).sendAccessibilityEvent(
+                mParentView, AccessibilityEvent.TYPE_ANNOUNCEMENT);
+
+        mParentView.sendAccessibilityEventUnchecked(event);
+        verify(mockDelegate).sendAccessibilityEventUnchecked(mParentView, event);
+
+        mParentView.dispatchPopulateAccessibilityEvent(event);
+        verify(mockDelegate).dispatchPopulateAccessibilityEvent(mParentView, event);
+
+        mParentView.onPopulateAccessibilityEvent(event);
+        verify(mockDelegate).onPopulateAccessibilityEvent(mParentView, event);
+
+        mParentView.onInitializeAccessibilityEvent(event);
+        verify(mockDelegate).onInitializeAccessibilityEvent(mParentView, event);
+
+        final AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+        mParentView.onInitializeAccessibilityNodeInfo(info);
+        verify(mockDelegate).onInitializeAccessibilityNodeInfo(mParentView, info);
+
+        mParentView.requestSendAccessibilityEvent(mChildView, event);
+        verify(mockDelegate).onRequestSendAccessibilityEvent(mParentView, mChildView, event);
+
+        mParentView.getAccessibilityNodeProvider();
+        verify(mockDelegate).getAccessibilityNodeProvider(mParentView);
+
+        final Bundle bundle = new Bundle();
+        mParentView.performAccessibilityAction(
+                    AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, bundle);
+        verify(mockDelegate).performAccessibilityAction(
+                mParentView, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, bundle);
+    }
+}
diff --git a/tests/accessibility/src/android/view/accessibility/cts/DummyActivity.java b/tests/accessibility/src/android/view/accessibility/cts/DummyActivity.java
new file mode 100644
index 0000000..b184fe8
--- /dev/null
+++ b/tests/accessibility/src/android/view/accessibility/cts/DummyActivity.java
@@ -0,0 +1,22 @@
+/*
+ * 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.view.accessibility.cts;
+
+import android.app.Activity;
+
+public class DummyActivity extends Activity {
+}
diff --git a/tests/accessibilityservice/AndroidManifest.xml b/tests/accessibilityservice/AndroidManifest.xml
index 5214410..029dc51 100644
--- a/tests/accessibilityservice/AndroidManifest.xml
+++ b/tests/accessibilityservice/AndroidManifest.xml
@@ -30,38 +30,38 @@
         <activity
             android:label="@string/accessibility_end_to_end_test_activity"
             android:name=".activities.AccessibilityEndToEndActivity"
-            android:screenOrientation="portrait"/>
+            android:screenOrientation="locked"/>
 
         <activity
             android:label="@string/accessibility_query_window_test_activity"
             android:name=".activities.AccessibilityWindowQueryActivity"
             android:supportsPictureInPicture="true"
-            android:screenOrientation="portrait"/>
+            android:screenOrientation="locked"/>
 
         <activity
             android:label="@string/accessibility_view_tree_reporting_test_activity"
             android:name=".activities.AccessibilityViewTreeReportingActivity"
-            android:screenOrientation="portrait"/>
+            android:screenOrientation="locked"/>
 
         <activity
             android:label="@string/accessibility_focus_and_input_focus_sync_test_activity"
             android:name=".activities.AccessibilityFocusAndInputFocusSyncActivity"
-            android:screenOrientation="portrait"/>
+            android:screenOrientation="locked"/>
 
         <activity
             android:label="@string/accessibility_text_traversal_test_activity"
             android:name=".activities.AccessibilityTextTraversalActivity"
-            android:screenOrientation="portrait"/>
+            android:screenOrientation="locked"/>
 
         <activity android:label="Activity for testing window accessibility reporting"
              android:name=".activities.AccessibilityWindowReportingActivity"
              android:supportsPictureInPicture="true"
-             android:screenOrientation="portrait"/>
+             android:screenOrientation="locked"/>
 
         <activity
             android:label="Full screen activity for gesture dispatch testing"
             android:name=".AccessibilityGestureDispatchTest$GestureDispatchActivity"
-            android:screenOrientation="portrait" />
+            android:screenOrientation="locked" />
 
         <activity
             android:label="@string/accessibility_soft_keyboard_modes_activity"
diff --git a/tests/accessibilityservice/AndroidTest.xml b/tests/accessibilityservice/AndroidTest.xml
index 20a8bff..bf0abac 100644
--- a/tests/accessibilityservice/AndroidTest.xml
+++ b/tests/accessibilityservice/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS accessibility service 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="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="cmd accessibility set-bind-instant-service-allowed true" />
         <option name="teardown-command" value="cmd accessibility set-bind-instant-service-allowed false" />
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
index caae032..6f935d7 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
@@ -13,146 +13,122 @@
  */
 
 package android.accessibilityservice.cts;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_WITH_HARD_KEYBOARD;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
 
 import android.accessibilityservice.AccessibilityService.SoftKeyboardController;
-import android.accessibilityservice.AccessibilityServiceInfo;
-import android.accessibilityservice.cts.R;
+import android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener;
 import android.accessibilityservice.cts.activities.AccessibilityTestActivity;
-import android.app.Activity;
+import android.accessibilityservice.cts.utils.AsyncUtils;
+import android.accessibilityservice.cts.utils.RunOnMainUtils;
 import android.app.Instrumentation;
-import android.app.UiAutomation;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.ResultReceiver;
 import android.os.SystemClock;
 import android.platform.test.annotations.AppModeFull;
-import android.test.ActivityInstrumentationTestCase2;
-import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityWindowInfo;
-import android.view.inputmethod.InputMethodManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
 
-import java.util.List;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicBoolean;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Test cases for testing the accessibility APIs for interacting with the soft keyboard show mode.
  */
+@RunWith(AndroidJUnit4.class)
 @AppModeFull
-public class AccessibilitySoftKeyboardModesTest extends ActivityInstrumentationTestCase2
-        <AccessibilitySoftKeyboardModesTest.SoftKeyboardModesActivity> {
-
-    private static final long TIMEOUT_PROPAGATE_SETTING = 5000;
-
-    /**
-     * Timeout required for pending Binder calls or event processing to
-     * complete.
-     */
-    private static final long TIMEOUT_ASYNC_PROCESSING = 5000;
-
-    /**
-     * The timeout since the last accessibility event to consider the device idle.
-     */
-    private static final long TIMEOUT_ACCESSIBILITY_STATE_IDLE = 500;
-
-    /**
-     * The timeout since {@link InputMethodManager#showSoftInput(View, int, ResultReceiver)}
-     * is called to {@link ResultReceiver#onReceiveResult(int, Bundle)} is called back.
-     */
-    private static final int TIMEOUT_SHOW_SOFTINPUT_RESULT = 2000;
-
-    private static final int SHOW_MODE_AUTO = 0;
-    private static final int SHOW_MODE_HIDDEN = 1;
-
+public class AccessibilitySoftKeyboardModesTest {
     private int mLastCallbackValue;
 
+    private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
     private InstrumentedAccessibilityService mService;
-    private SoftKeyboardController mKeyboardController;
-    private UiAutomation mUiAutomation;
-    private Activity mActivity;
-    private View mKeyboardTargetView;
+    private final Object mLock = new Object();
+    private final OnShowModeChangedListener mListener = (c, showMode) -> {
+        synchronized (mLock) {
+            mLastCallbackValue = showMode;
+            mLock.notifyAll();
+        }
+    };
 
-    private Object mLock = new Object();
 
-    public AccessibilitySoftKeyboardModesTest() {
-        super(SoftKeyboardModesActivity.class);
-    }
 
-    @Override
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
-
-        // If we don't call getActivity(), we get an empty list when requesting the number of
-        // windows on screen.
-        mActivity = getActivity();
-
-        mService = InstrumentedAccessibilityService.enableService(
-                getInstrumentation(), InstrumentedAccessibilityService.class);
-        mKeyboardController = mService.getSoftKeyboardController();
-        mUiAutomation = getInstrumentation()
-                .getUiAutomation(UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
-        AccessibilityServiceInfo info = mUiAutomation.getServiceInfo();
-        info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
-        mUiAutomation.setServiceInfo(info);
-        getInstrumentation().runOnMainSync(
-                () -> mKeyboardTargetView = mActivity.findViewById(R.id.edit_text));
+        mService = InstrumentedAccessibilityService.enableService(mInstrumentation,
+                InstrumentedAccessibilityService.class);
     }
 
-    @Override
+    @After
     public void tearDown() throws Exception {
-        mKeyboardController.setShowMode(SHOW_MODE_AUTO);
-        mService.runOnServiceSync(() -> mService.disableSelf());
-        Activity activity = getActivity();
-        View currentFocus = activity.getCurrentFocus();
-        if (currentFocus != null) {
-            activity.getSystemService(InputMethodManager.class)
-                    .hideSoftInputFromWindow(currentFocus.getWindowToken(), 0);
+        if (mService != null) {
+            mService.runOnServiceSync(() -> mService.disableSelf());
         }
     }
 
+    @Test
     public void testApiReturnValues_shouldChangeValueOnRequestAndSendCallback() throws Exception {
-        mLastCallbackValue = -1;
+        final SoftKeyboardController controller = mService.getSoftKeyboardController();
 
-        final SoftKeyboardController.OnShowModeChangedListener listener =
-                new SoftKeyboardController.OnShowModeChangedListener() {
-                    @Override
-                    public void onShowModeChanged(SoftKeyboardController controller, int showMode) {
-                        synchronized (mLock) {
-                            mLastCallbackValue = showMode;
-                            mLock.notifyAll();
-                        }
-                    }
-                };
-        mKeyboardController.addOnShowModeChangedListener(listener);
+        // Confirm that we start in the default state
+        assertEquals(SHOW_MODE_AUTO, controller.getShowMode());
 
-        // The soft keyboard should be in its default mode.
-        assertEquals(SHOW_MODE_AUTO, mKeyboardController.getShowMode());
-
-        // Set the show mode to SHOW_MODE_HIDDEN.
-        assertTrue(mKeyboardController.setShowMode(SHOW_MODE_HIDDEN));
-
-        // Make sure the mode was changed.
-        assertEquals(SHOW_MODE_HIDDEN, mKeyboardController.getShowMode());
-
-        // Make sure we're getting the callback with the proper value.
-        waitForCallbackValueWithLock(SHOW_MODE_HIDDEN);
-
-        // Make sure we can set the value back to the default.
-        assertTrue(mKeyboardController.setShowMode(SHOW_MODE_AUTO));
-        assertEquals(SHOW_MODE_AUTO, mKeyboardController.getShowMode());
-        waitForCallbackValueWithLock(SHOW_MODE_AUTO);
+        controller.addOnShowModeChangedListener(mListener);
+        assertCanSetAndGetShowModeAndCallbackHappens(SHOW_MODE_HIDDEN, mService);
+        assertCanSetAndGetShowModeAndCallbackHappens(SHOW_MODE_WITH_HARD_KEYBOARD, mService);
+        assertCanSetAndGetShowModeAndCallbackHappens(SHOW_MODE_AUTO, mService);
 
         // Make sure we can remove our listener.
-        assertTrue(mKeyboardController.removeOnShowModeChangedListener(listener));
+        assertTrue(controller.removeOnShowModeChangedListener(mListener));
+    }
+
+    @Test
+    public void secondServiceChangingTheShowMode_updatesModeAndNotifiesFirstService()
+            throws Exception {
+
+        final SoftKeyboardController controller = mService.getSoftKeyboardController();
+        // Confirm that we start in the default state
+        assertEquals(SHOW_MODE_AUTO, controller.getShowMode());
+
+        final InstrumentedAccessibilityService secondService =
+                StubAccessibilityButtonService.enableSelf(mInstrumentation);
+        try {
+            // Listen on the first service
+            controller.addOnShowModeChangedListener(mListener);
+            assertCanSetAndGetShowModeAndCallbackHappens(SHOW_MODE_HIDDEN, mService);
+
+            // Change the mode on the second service
+            assertCanSetAndGetShowModeAndCallbackHappens(SHOW_MODE_WITH_HARD_KEYBOARD,
+                    secondService);
+        } finally {
+            secondService.runOnServiceSync(() -> secondService.disableSelf());
+        }
+
+        // Shutting down the second service, which was controlling the mode, should put us back
+        // to the default
+        waitForCallbackValueWithLock(SHOW_MODE_AUTO);
+        final int showMode = mService.getOnService(() -> controller.getShowMode());
+        assertEquals(SHOW_MODE_AUTO, showMode);
+    }
+
+    private void assertCanSetAndGetShowModeAndCallbackHappens(
+            int mode, InstrumentedAccessibilityService service)
+            throws Exception  {
+        final SoftKeyboardController controller = service.getSoftKeyboardController();
+        mLastCallbackValue = -1;
+        final boolean setShowModeReturns =
+                service.getOnService(() -> controller.setShowMode(mode));
+        assertTrue(setShowModeReturns);
+        waitForCallbackValueWithLock(mode);
+        assertEquals(mode, controller.getShowMode());
     }
 
     private void waitForCallbackValueWithLock(int expectedValue) throws Exception {
-        long timeoutTimeMillis = SystemClock.uptimeMillis() + TIMEOUT_PROPAGATE_SETTING;
+        long timeoutTimeMillis = SystemClock.uptimeMillis() + AsyncUtils.DEFAULT_TIMEOUT_MS;
 
         while (SystemClock.uptimeMillis() < timeoutTimeMillis) {
             synchronized(mLock) {
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/InstrumentedAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/InstrumentedAccessibilityService.java
index fe595a4f..0ff067f 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/InstrumentedAccessibilityService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/InstrumentedAccessibilityService.java
@@ -31,8 +31,10 @@
 import java.lang.ref.WeakReference;
 import java.util.HashMap;
 import java.util.List;
+import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
 
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
@@ -99,6 +101,22 @@
         assertTrue("Timed out waiting for runOnServiceSync()", sr.waitForComplete());
     }
 
+    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);
+            }
+        });
+        if (throwable.get() != null) {
+            throw new RuntimeException(throwable.get());
+        }
+        return returnValue.get();
+    }
+
     public boolean wasOnInterruptCalled() {
         synchronized (mInterruptWaitObject) {
             return mOnInterruptCalled;
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
index 822b2a6..9aa16bc 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
@@ -18,18 +18,26 @@
         .TIMEOUT_ASYNC_PROCESSING;
 
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
 
+import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.app.Activity;
 import android.app.Instrumentation;
 import android.app.UiAutomation;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.graphics.Rect;
 import android.support.test.rule.ActivityTestRule;
 import android.text.TextUtils;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
 
 import java.util.List;
+import java.util.concurrent.TimeoutException;
 
 /**
  * Utilities useful when launching an activity to make sure it's all the way on the screen
@@ -49,6 +57,7 @@
         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);
@@ -97,4 +106,41 @@
         }
         return returnValue;
     }
+
+    public static void homeScreenOrBust(Context context, UiAutomation uiAutomation) {
+        if (isHomeScreenShowing(context, uiAutomation)) return;
+        try {
+            uiAutomation.executeAndWaitForEvent(
+                    () -> uiAutomation.performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME),
+                    (event) -> isHomeScreenShowing(context, uiAutomation),
+                    TIMEOUT_ASYNC_PROCESSING);
+        } catch (TimeoutException te) {
+            fail("Unable to reach home screen");
+        }
+    }
+
+    private static boolean isHomeScreenShowing(Context context, UiAutomation uiAutomation) {
+        final List<AccessibilityWindowInfo> windows = uiAutomation.getWindows();
+        final PackageManager packageManager = context.getPackageManager();
+        final List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(
+                new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME),
+                PackageManager.MATCH_DEFAULT_ONLY);
+
+        // Look for a window with a package name that matches the default home screen
+        for (AccessibilityWindowInfo window : windows) {
+            final AccessibilityNodeInfo root = window.getRoot();
+            if (root != null) {
+                final CharSequence packageName = root.getPackageName();
+                if (packageName != null) {
+                    for (ResolveInfo resolveInfo : resolveInfos) {
+                        if ((resolveInfo.activityInfo != null)
+                                && packageName.equals(resolveInfo.activityInfo.packageName)) {
+                            return true;
+                        }
+                    }
+                }
+            }
+        }
+        return false;
+    }
 }
diff --git a/tests/admin/Android.mk b/tests/admin/Android.mk
index bd5346d..40ce285 100644
--- a/tests/admin/Android.mk
+++ b/tests/admin/Android.mk
@@ -21,7 +21,7 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    ctstestrunner mockito-target-minus-junit4
+    ctstestrunner mockito-target-minus-junit4 truth-prebuilt testng
 
 LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
diff --git a/tests/admin/app/AndroidManifest.xml b/tests/admin/app/AndroidManifest.xml
index 0307487..1d901a2 100644
--- a/tests/admin/app/AndroidManifest.xml
+++ b/tests/admin/app/AndroidManifest.xml
@@ -67,6 +67,24 @@
             </intent-filter>
         </receiver>
 
+        <receiver android:name="android.admin.app.CtsDeviceAdminReceiverVisible"
+                  android:permission="android.permission.BIND_DEVICE_ADMIN">
+            <meta-data android:name="android.app.device_admin"
+                       android:resource="@xml/device_admin_visible" />
+            <intent-filter>
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+            </intent-filter>
+        </receiver>
+
+        <receiver android:name="android.admin.app.CtsDeviceAdminReceiverInvisible"
+                  android:permission="android.permission.BIND_DEVICE_ADMIN">
+            <meta-data android:name="android.app.device_admin"
+                       android:resource="@xml/device_admin_invisible" />
+            <intent-filter>
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+            </intent-filter>
+        </receiver>
+
         <!-- Device Admin that needs to be in the deactivated state in order
              for tests to pass. -->
         <receiver android:name="android.admin.app.CtsDeviceAdminDeactivatedReceiver"
diff --git a/tests/admin/app/res/xml/device_admin_invisible.xml b/tests/admin/app/res/xml/device_admin_invisible.xml
new file mode 100644
index 0000000..6af5a60
--- /dev/null
+++ b/tests/admin/app/res/xml/device_admin_invisible.xml
@@ -0,0 +1,24 @@
+<!--
+ * 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.
+ -->
+
+<device-admin xmlns:android="http://schemas.android.com/apk/res/android" android:visible="false">
+    <support-transfer-ownership />
+    <uses-policies>
+        <limit-password />
+        <reset-password />
+        <wipe-data />
+    </uses-policies>
+</device-admin>
diff --git a/tests/admin/app/res/xml/device_admin_visible.xml b/tests/admin/app/res/xml/device_admin_visible.xml
new file mode 100644
index 0000000..5d37a95
--- /dev/null
+++ b/tests/admin/app/res/xml/device_admin_visible.xml
@@ -0,0 +1,24 @@
+<!--
+ * 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.
+ -->
+
+<device-admin xmlns:android="http://schemas.android.com/apk/res/android" android:visible="true">
+    <support-transfer-ownership />
+    <uses-policies>
+        <limit-password />
+        <reset-password />
+        <wipe-data />
+    </uses-policies>
+</device-admin>
diff --git a/tests/admin/app/src/android/admin/app/CtsDeviceAdminReceiverInvisible.java b/tests/admin/app/src/android/admin/app/CtsDeviceAdminReceiverInvisible.java
new file mode 100644
index 0000000..05f9426
--- /dev/null
+++ b/tests/admin/app/src/android/admin/app/CtsDeviceAdminReceiverInvisible.java
@@ -0,0 +1,22 @@
+/*
+ * 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.admin.app;
+
+import android.app.admin.DeviceAdminReceiver;
+
+public class CtsDeviceAdminReceiverInvisible extends DeviceAdminReceiver {
+}
diff --git a/tests/admin/app/src/android/admin/app/CtsDeviceAdminReceiverVisible.java b/tests/admin/app/src/android/admin/app/CtsDeviceAdminReceiverVisible.java
new file mode 100644
index 0000000..9bf440a
--- /dev/null
+++ b/tests/admin/app/src/android/admin/app/CtsDeviceAdminReceiverVisible.java
@@ -0,0 +1,22 @@
+/*
+ * 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.admin.app;
+
+import android.app.admin.DeviceAdminReceiver;
+
+public class CtsDeviceAdminReceiverVisible extends DeviceAdminReceiver {
+}
diff --git a/tests/admin/src/android/admin/cts/DeviceAdminInfoTest.java b/tests/admin/src/android/admin/cts/DeviceAdminInfoTest.java
index 1790503..a0533cd 100644
--- a/tests/admin/src/android/admin/cts/DeviceAdminInfoTest.java
+++ b/tests/admin/src/android/admin/cts/DeviceAdminInfoTest.java
@@ -16,8 +16,11 @@
 
 package android.admin.cts;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import android.app.admin.DeviceAdminInfo;
 import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.test.AndroidTestCase;
@@ -60,6 +63,16 @@
         return new ComponentName("android.admin.app", "android.admin.app.CtsDeviceAdminProfileOwner");
     }
 
+    static ComponentName getVisibleComponent() {
+        return new ComponentName(
+                "android.admin.app", "android.admin.app.CtsDeviceAdminReceiverVisible");
+    }
+
+    static ComponentName getInvisibleComponent() {
+        return new ComponentName(
+                "android.admin.app", "android.admin.app.CtsDeviceAdminReceiverInvisible");
+    }
+
     public void testDeviceAdminInfo() throws Exception {
         if (!mDeviceAdmin) {
             Log.w(TAG, "Skipping testDeviceAdminInfo");
@@ -70,27 +83,27 @@
                 PackageManager.GET_META_DATA);
 
         DeviceAdminInfo info = new DeviceAdminInfo(mContext, resolveInfo);
-        assertEquals(mComponent, info.getComponent());
-        assertEquals(mComponent.getPackageName(), info.getPackageName());
-        assertEquals(mComponent.getClassName(), info.getReceiverName());
+        assertThat(mComponent).isEqualTo(info.getComponent());
+        assertThat(mComponent.getPackageName()).isEqualTo(info.getPackageName());
+        assertThat(mComponent.getClassName()).isEqualTo(info.getReceiverName());
 
-        assertFalse(info.supportsTransferOwnership());
-        assertTrue(info.usesPolicy(DeviceAdminInfo.USES_POLICY_FORCE_LOCK));
-        assertTrue(info.usesPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD));
-        assertTrue(info.usesPolicy(DeviceAdminInfo.USES_POLICY_RESET_PASSWORD));
-        assertTrue(info.usesPolicy(DeviceAdminInfo.USES_POLICY_WATCH_LOGIN));
-        assertTrue(info.usesPolicy(DeviceAdminInfo.USES_POLICY_WIPE_DATA));
+        assertThat(info.supportsTransferOwnership()).isFalse();
+        assertThat(info.usesPolicy(DeviceAdminInfo.USES_POLICY_FORCE_LOCK)).isTrue();
+        assertThat(info.usesPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD)).isTrue();
+        assertThat(info.usesPolicy(DeviceAdminInfo.USES_POLICY_RESET_PASSWORD)).isTrue();
+        assertThat(info.usesPolicy(DeviceAdminInfo.USES_POLICY_WATCH_LOGIN)).isTrue();
+        assertThat(info.usesPolicy(DeviceAdminInfo.USES_POLICY_WIPE_DATA)).isTrue();
 
-        assertEquals("force-lock",
-                info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_FORCE_LOCK));
-        assertEquals("limit-password",
-                info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD));
-        assertEquals("reset-password",
-                info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_RESET_PASSWORD));
-        assertEquals("watch-login",
-                info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_WATCH_LOGIN));
-        assertEquals("wipe-data",
-                info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_WIPE_DATA));
+        assertThat("force-lock")
+                .isEqualTo(info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_FORCE_LOCK));
+        assertThat("limit-password")
+                .isEqualTo(info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD));
+        assertThat("reset-password")
+                .isEqualTo(info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_RESET_PASSWORD));
+        assertThat("watch-login")
+                .isEqualTo(info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_WATCH_LOGIN));
+        assertThat("wipe-data")
+                .isEqualTo(info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_WIPE_DATA));
     }
 
     public void testDeviceAdminInfo2() throws Exception {
@@ -103,27 +116,27 @@
                 PackageManager.GET_META_DATA);
 
         DeviceAdminInfo info = new DeviceAdminInfo(mContext, resolveInfo);
-        assertEquals(mSecondComponent, info.getComponent());
-        assertEquals(mSecondComponent.getPackageName(), info.getPackageName());
-        assertEquals(mSecondComponent.getClassName(), info.getReceiverName());
+        assertThat(mSecondComponent).isEqualTo(info.getComponent());
+        assertThat(mSecondComponent.getPackageName()).isEqualTo(info.getPackageName());
+        assertThat(mSecondComponent.getClassName()).isEqualTo(info.getReceiverName());
 
-        assertFalse(info.supportsTransferOwnership());
-        assertFalse(info.usesPolicy(DeviceAdminInfo.USES_POLICY_FORCE_LOCK));
-        assertTrue(info.usesPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD));
-        assertTrue(info.usesPolicy(DeviceAdminInfo.USES_POLICY_RESET_PASSWORD));
-        assertFalse(info.usesPolicy(DeviceAdminInfo.USES_POLICY_WATCH_LOGIN));
-        assertTrue(info.usesPolicy(DeviceAdminInfo.USES_POLICY_WIPE_DATA));
+        assertThat(info.supportsTransferOwnership()).isFalse();
+        assertThat(info.usesPolicy(DeviceAdminInfo.USES_POLICY_FORCE_LOCK)).isFalse();
+        assertThat(info.usesPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD)).isTrue();
+        assertThat(info.usesPolicy(DeviceAdminInfo.USES_POLICY_RESET_PASSWORD)).isTrue();
+        assertThat(info.usesPolicy(DeviceAdminInfo.USES_POLICY_WATCH_LOGIN)).isFalse();
+        assertThat(info.usesPolicy(DeviceAdminInfo.USES_POLICY_WIPE_DATA)).isTrue();
 
-        assertEquals("force-lock",
-                info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_FORCE_LOCK));
-        assertEquals("limit-password",
-                info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD));
-        assertEquals("reset-password",
-                info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_RESET_PASSWORD));
-        assertEquals("watch-login",
-                info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_WATCH_LOGIN));
-        assertEquals("wipe-data",
-                info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_WIPE_DATA));
+        assertThat("force-lock")
+                .isEqualTo(info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_FORCE_LOCK));
+        assertThat("limit-password")
+                .isEqualTo(info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD));
+        assertThat("reset-password")
+                .isEqualTo(info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_RESET_PASSWORD));
+        assertThat("watch-login")
+                .isEqualTo(info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_WATCH_LOGIN));
+        assertThat("wipe-data")
+                .isEqualTo(info.getTagForPolicy(DeviceAdminInfo.USES_POLICY_WIPE_DATA));
     }
 
     public void testDeviceAdminInfo3() throws Exception {
@@ -136,6 +149,66 @@
                 PackageManager.GET_META_DATA);
 
         DeviceAdminInfo info = new DeviceAdminInfo(mContext, resolveInfo);
-        assertTrue(info.supportsTransferOwnership());
+        assertThat(info.supportsTransferOwnership()).isTrue();
+    }
+
+    public void testDescribeContents_returnsAtLeastZero() throws Exception {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testDescribeContents_returnsAtLeastZero");
+            return;
+        }
+
+        assertThat(buildDeviceAdminInfo(buildActivityInfo()).describeContents()).isAtLeast(0);
+    }
+
+    public void testGetActivityInfo_returnsActivityInfo() throws Exception {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testGetActivityInfo_returnsActivityInfo");
+            return;
+        }
+        ActivityInfo activityInfo = buildActivityInfo();
+        DeviceAdminInfo deviceAdminInfo = buildDeviceAdminInfo(activityInfo);
+
+        assertThat(deviceAdminInfo.getActivityInfo()).isEqualTo(activityInfo);
+    }
+
+    public void testIsVisible_visibleComponent_returnsTrue() throws Exception {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testIsVisible_visibleComponent_returnsTrue");
+            return;
+        }
+        ActivityInfo activityInfo = buildActivityInfo(getVisibleComponent());
+        DeviceAdminInfo deviceAdminInfo = buildDeviceAdminInfo(activityInfo);
+
+        assertThat(deviceAdminInfo.isVisible()).isTrue();
+    }
+
+    public void testIsVisible_invisibleComponent_returnsFalse() throws Exception {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testIsVisible_invisibleComponent_returnsFalse");
+            return;
+        }
+        ActivityInfo activityInfo = buildActivityInfo(getInvisibleComponent());
+        DeviceAdminInfo deviceAdminInfo = buildDeviceAdminInfo(activityInfo);
+
+        assertThat(deviceAdminInfo.isVisible()).isFalse();
+    }
+
+    private DeviceAdminInfo buildDeviceAdminInfo(ActivityInfo activityInfo) throws Exception {
+        return new DeviceAdminInfo(mContext, buildResolveInfo(activityInfo));
+    }
+
+    private ResolveInfo buildResolveInfo(ActivityInfo activityInfo) {
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = activityInfo;
+        return resolveInfo;
+    }
+
+    private ActivityInfo buildActivityInfo() throws Exception {
+        return buildActivityInfo(mThirdComponent);
+    }
+
+    private ActivityInfo buildActivityInfo(ComponentName componentName) throws Exception {
+        return mPackageManager.getReceiverInfo(componentName, PackageManager.GET_META_DATA);
     }
 }
diff --git a/tests/admin/src/android/admin/cts/DeviceAdminReceiverTest.java b/tests/admin/src/android/admin/cts/DeviceAdminReceiverTest.java
index 4888476..0407811 100644
--- a/tests/admin/src/android/admin/cts/DeviceAdminReceiverTest.java
+++ b/tests/admin/src/android/admin/cts/DeviceAdminReceiverTest.java
@@ -16,10 +16,12 @@
 
 package android.admin.cts;
 
+import static android.app.admin.DeviceAdminReceiver.ACTION_LOCK_TASK_ENTERING;
 import static android.app.admin.DeviceAdminReceiver.ACTION_PASSWORD_CHANGED;
 import static android.app.admin.DeviceAdminReceiver.ACTION_PASSWORD_FAILED;
 import static android.app.admin.DeviceAdminReceiver.ACTION_PASSWORD_SUCCEEDED;
 import static android.app.admin.DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING;
+import static android.app.admin.DeviceAdminReceiver.EXTRA_LOCK_TASK_PACKAGE;
 
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.argThat;
@@ -209,6 +211,34 @@
                 eq(NETWORK_LOGS_TOKEN), eq(NETWORK_LOGS_COUNT));
     }
 
+    @Presubmit
+    public void testOnReceive_enterLockTaskWithoutPackage_callsCallback() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testOnReceive_enterLockTask_callsCallback");
+            return;
+        }
+        final Intent intent = new Intent(ACTION_LOCK_TASK_ENTERING);
+
+        mReceiver.onReceive(mContext, intent);
+
+        verify(mReceiver).onLockTaskModeEntering(mContext, intent, null);
+    }
+
+    @Presubmit
+    public void testOnReceive_enterLockTaskWithPackage_callsCallback() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testOnReceive_enterLockTask_callsCallback");
+            return;
+        }
+        final Intent intent = new Intent(ACTION_LOCK_TASK_ENTERING);
+        final String pkg = "pkg";
+        intent.putExtra(EXTRA_LOCK_TASK_PACKAGE, pkg);
+
+        mReceiver.onReceive(mContext, intent);
+
+        verify(mReceiver).onLockTaskModeEntering(mContext, intent, pkg);
+    }
+
     private Intent actionEq(final String expected) {
         return argThat(x -> expected.equals(x.getAction()));
     }
diff --git a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
index c6240d7c..d25d9a4 100644
--- a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
+++ b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
@@ -17,6 +17,7 @@
 package android.admin.cts;
 
 import static org.junit.Assert.assertNotEquals;
+import static org.testng.Assert.assertThrows;
 
 import android.app.NotificationManager;
 import android.app.NotificationManager.Policy;
@@ -1036,4 +1037,16 @@
             assertProfileOwnerMessage(tolerated.getMessage());
         }
     }
+
+    public void testSetStorageEncryption_noAdmin() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testSetStorageEncryption_noAdmin");
+            return;
+        }
+        final ComponentName notAdmin = new ComponentName("com.test.foo", ".bar");
+        assertThrows(SecurityException.class,
+            () -> mDevicePolicyManager.setStorageEncryption(notAdmin, true));
+        assertThrows(SecurityException.class,
+            () -> mDevicePolicyManager.setStorageEncryption(notAdmin, false));
+    }
 }
diff --git a/tests/app/Android.mk b/tests/app/Android.mk
index e38ad2f..52dcb69 100644
--- a/tests/app/Android.mk
+++ b/tests/app/Android.mk
@@ -47,6 +47,7 @@
 LOCAL_INSTRUMENTATION_FOR := CtsAppTestStubs
 
 LOCAL_SDK_VERSION := test_current
+LOCAL_MIN_SDK_VERSION := 11
 
 include $(BUILD_CTS_PACKAGE)
 
diff --git a/tests/app/AndroidManifest.xml b/tests/app/AndroidManifest.xml
index 96977fb..326719e 100644
--- a/tests/app/AndroidManifest.xml
+++ b/tests/app/AndroidManifest.xml
@@ -22,6 +22,8 @@
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
     <uses-permission android:name="android.permission.BODY_SENSORS" />
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+
     <application android:usesCleartextTraffic="true">
         <uses-library android:name="android.test.runner" />
         <uses-library android:name="org.apache.http.legacy" android:required="false" />
diff --git a/tests/app/AndroidTest.xml b/tests/app/AndroidTest.xml
index fbc5c38..d90edad 100644
--- a/tests/app/AndroidTest.xml
+++ b/tests/app/AndroidTest.xml
@@ -21,12 +21,15 @@
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsSimpleApp.apk" />
         <option name="test-file-name" value="CtsAppTestStubs.apk" />
-        <option name="test-file-name" value="CtsAppTestStubsDifferentUid.apk" />
+        <option name="test-file-name" value="CtsAppTestStubsApp1.apk" />
+        <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="CtsCantSaveState1.apk" />
         <option name="test-file-name" value="CtsCantSaveState2.apk" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
         <option name="package" value="android.app.cts" />
         <option name="runtime-hint" value="6m38s" />
         <option name="hidden-api-checks" value="false"/>
diff --git a/tests/app/app/AndroidManifest.xml b/tests/app/app/AndroidManifest.xml
index e05b8b4..7407f76 100644
--- a/tests/app/app/AndroidManifest.xml
+++ b/tests/app/app/AndroidManifest.xml
@@ -130,6 +130,9 @@
         </service>
 
         <service android:name="android.app.stubs.LocalForegroundService">
+            <intent-filter>
+                <action android:name="android.app.stubs.FOREGROUND_SERVICE" />
+            </intent-filter>
         </service>
 
         <service android:name="android.app.stubs.LocalGrantedService"
@@ -353,6 +356,9 @@
                 <action android:name="android.service.notification.NotificationListenerService" />
             </intent-filter>
         </service>
+
+        <receiver android:name="android.app.stubs.CommandReceiver"
+                  android:exported="true" />
     </application>
 
 </manifest>
diff --git a/tests/app/app/src/android/app/stubs/CommandReceiver.java b/tests/app/app/src/android/app/stubs/CommandReceiver.java
new file mode 100644
index 0000000..26cc10f
--- /dev/null
+++ b/tests/app/app/src/android/app/stubs/CommandReceiver.java
@@ -0,0 +1,151 @@
+/*
+ * 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.app.stubs;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.ArrayMap;
+import android.util.Log;
+
+public class CommandReceiver extends BroadcastReceiver {
+
+    private static final String TAG = "CommandReceiver";
+
+    // Requires flags and targetPackage
+    public static final int COMMAND_BIND_SERVICE = 1;
+    // Requires targetPackage
+    public static final int COMMAND_UNBIND_SERVICE = 2;
+    public static final int COMMAND_START_FOREGROUND_SERVICE = 3;
+    public static final int COMMAND_STOP_FOREGROUND_SERVICE = 4;
+
+    public static final String EXTRA_COMMAND = "android.app.stubs.extra.COMMAND";
+    public static final String EXTRA_TARGET_PACKAGE = "android.app.stubs.extra.TARGET_PACKAGE";
+    public static final String EXTRA_FLAGS = "android.app.stubs.extra.FLAGS";
+
+    public static final String SERVICE_NAME = "android.app.stubs.LocalService";
+
+    private static ArrayMap<String,ServiceConnection> sServiceMap = new ArrayMap<>();
+
+    /**
+     * Handle the different types of binding/unbinding requests.
+     * @param context The Context in which the receiver is running.
+     * @param intent The Intent being received.
+     */
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        int command = intent.getIntExtra(EXTRA_COMMAND, -1);
+        Log.d(TAG + "_" + context.getPackageName(), "Got command " + command + ", intent="
+                + intent);
+        switch (command) {
+            case COMMAND_BIND_SERVICE:
+                doBindService(context, intent);
+                break;
+            case COMMAND_UNBIND_SERVICE:
+                doUnbindService(context, intent);
+                break;
+            case COMMAND_START_FOREGROUND_SERVICE:
+                doStartForegroundService(context);
+                break;
+            case COMMAND_STOP_FOREGROUND_SERVICE:
+                doStopForegroundService(context);
+                break;
+        }
+    }
+
+    private void doBindService(Context context, Intent commandIntent) {
+        context = context.getApplicationContext();
+        if (LocalService.sServiceContext != null) {
+            context = LocalService.sServiceContext;
+        }
+
+        String targetPackage = getTargetPackage(commandIntent);
+        int flags = getFlags(commandIntent);
+
+        Intent bindIntent = new Intent();
+        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);
+        context.unbindService(sServiceMap.remove(targetPackage));
+    }
+
+    private void doStartForegroundService(Context context) {
+        Intent fgsIntent = new Intent(context, LocalForegroundService.class);
+        int command = LocalForegroundService.COMMAND_START_FOREGROUND;
+        fgsIntent.putExtras(LocalForegroundService.newCommand(new Binder(), command));
+        context.startForegroundService(fgsIntent);
+    }
+
+    private void doStopForegroundService(Context context) {
+        Intent fgsIntent = new Intent(context, LocalForegroundService.class);
+        context.stopService(fgsIntent);
+    }
+
+    private String getTargetPackage(Intent intent) {
+        return intent.getStringExtra(EXTRA_TARGET_PACKAGE);
+    }
+
+    private int getFlags(Intent intent) {
+        return intent.getIntExtra(EXTRA_FLAGS, 0);
+    }
+
+    public static void sendCommand(Context context, int command, String sourcePackage,
+            String targetPackage, int flags, Bundle extras) {
+        Intent intent = new Intent();
+        if (command == COMMAND_BIND_SERVICE || command == COMMAND_START_FOREGROUND_SERVICE) {
+            intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        }
+        intent.setComponent(new ComponentName(sourcePackage, "android.app.stubs.CommandReceiver"));
+        intent.putExtra(EXTRA_COMMAND, command);
+        intent.putExtra(EXTRA_FLAGS, flags);
+        intent.putExtra(EXTRA_TARGET_PACKAGE, targetPackage);
+        if (extras != null) {
+            intent.putExtras(extras);
+        }
+        sendCommand(context, intent);
+    }
+
+    private static void sendCommand(Context context, Intent intent) {
+        Log.d(TAG, "Sending broadcast " + intent);
+        context.sendBroadcast(intent);
+    }
+
+    private ServiceConnection addServiceConnection(final String packageName) {
+        ServiceConnection connection = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+            }
+        };
+        sServiceMap.put(packageName, connection);
+        return connection;
+    }
+}
diff --git a/tests/app/app/src/android/app/stubs/LocalService.java b/tests/app/app/src/android/app/stubs/LocalService.java
index f0624b2..fab5321 100644
--- a/tests/app/app/src/android/app/stubs/LocalService.java
+++ b/tests/app/app/src/android/app/stubs/LocalService.java
@@ -17,6 +17,7 @@
 package android.app.stubs;
 
 import android.app.Service;
+import android.content.Context;
 import android.content.Intent;
 import android.os.Binder;
 import android.os.IBinder;
@@ -41,6 +42,8 @@
     public static final int UNBIND_CODE = 4;
     public static final int REBIND_CODE = 5;
 
+    public static Context sServiceContext = null;
+
     private IBinder mReportObject;
     private int mStartCount = 1;
 
@@ -72,6 +75,9 @@
                 bindAction(STARTED_CODE);
             }
         }
+        if (sServiceContext == null) {
+            sServiceContext = this;
+        }
     }
 
     @Override
@@ -83,6 +89,9 @@
 
     @Override
     public IBinder onBind(Intent intent) {
+        if (sServiceContext == null) {
+            sServiceContext = this;
+        }
         return mBinder;
     }
 
diff --git a/tests/app/app1/Android.mk b/tests/app/app1/Android.mk
new file mode 100644
index 0000000..6a008ea7
--- /dev/null
+++ b/tests/app/app1/Android.mk
@@ -0,0 +1,56 @@
+# 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)/../app
+
+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_PROGUARD_ENABLED := disabled
+
+LOCAL_JAVA_LIBRARIES := \
+    android.test.runner.stubs \
+    telephony-common \
+    voip-common \
+    org.apache.http.legacy \
+    android.test.base.stubs \
+
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    compatibility-device-util \
+    ctstestrunner \
+    ctstestserver \
+    mockito-target-minus-junit4 \
+    androidx.legacy_legacy-support-v4
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+              src/android/app/stubs/ISecondary.aidl
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsAppTestStubsApp1
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_AAPT_FLAGS += --rename-manifest-package com.android.app1
+
+# Disable AAPT2 manifest checks to fix:
+# cts/tests/app/app/AndroidManifest.xml:25: error: unexpected element <meta-data> found in <manifest><permission>.
+# TODO(b/79755007): Remove when AAPT2 recognizes the manifest elements.
+LOCAL_AAPT_FLAGS += --warn-manifest-validation
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/app/app2/Android.mk b/tests/app/app2/Android.mk
index 304f79e..541806e 100644
--- a/tests/app/app2/Android.mk
+++ b/tests/app/app2/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 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.
@@ -12,25 +12,45 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH:= $(call my-dir)/../app
 
 include $(CLEAR_VARS)
 
-LOCAL_MODULE_TAGS := tests
+# 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_PROGUARD_ENABLED := disabled
+
+LOCAL_JAVA_LIBRARIES := \
+    android.test.runner.stubs \
+    telephony-common \
+    voip-common \
+    org.apache.http.legacy \
+    android.test.base.stubs \
+
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     compatibility-device-util \
+    ctstestrunner \
+    ctstestserver \
+    mockito-target-minus-junit4 \
+    androidx.legacy_legacy-support-v4
 
-LOCAL_SRC_FILES := \
-    ../app/src/android/app/stubs/LocalService.java
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_PACKAGE_NAME := CtsAppTestStubsDifferentUid
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+              src/android/app/stubs/ISecondary.aidl
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-LOCAL_DEX_PREOPT := false
+LOCAL_PACKAGE_NAME := CtsAppTestStubsApp2
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_AAPT_FLAGS += --rename-manifest-package com.android.app2
+
+# Disable AAPT2 manifest checks to fix:
+# cts/tests/app/app/AndroidManifest.xml:25: error: unexpected element <meta-data> found in <manifest><permission>.
+# TODO(b/79755007): Remove when AAPT2 recognizes the manifest elements.
+LOCAL_AAPT_FLAGS += --warn-manifest-validation
 
 include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/app/app3/Android.mk b/tests/app/app3/Android.mk
new file mode 100644
index 0000000..212c525
--- /dev/null
+++ b/tests/app/app3/Android.mk
@@ -0,0 +1,56 @@
+# 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)/../app
+
+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_PROGUARD_ENABLED := disabled
+
+LOCAL_JAVA_LIBRARIES := \
+    android.test.runner.stubs \
+    telephony-common \
+    voip-common \
+    org.apache.http.legacy \
+    android.test.base.stubs \
+
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    compatibility-device-util \
+    ctstestrunner \
+    ctstestserver \
+    mockito-target-minus-junit4 \
+    androidx.legacy_legacy-support-v4
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+              src/android/app/stubs/ISecondary.aidl
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsAppTestStubsApp3
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_AAPT_FLAGS += --rename-manifest-package com.android.app3
+
+# Disable AAPT2 manifest checks to fix:
+# cts/tests/app/app/AndroidManifest.xml:25: error: unexpected element <meta-data> found in <manifest><permission>.
+# TODO(b/79755007): Remove when AAPT2 recognizes the manifest elements.
+LOCAL_AAPT_FLAGS += --warn-manifest-validation
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/app/src/android/app/cts/ActivityManagerBaseTaskInfoTest.java b/tests/app/src/android/app/cts/ActivityManagerBaseTaskInfoTest.java
new file mode 100644
index 0000000..de6d264
--- /dev/null
+++ b/tests/app/src/android/app/cts/ActivityManagerBaseTaskInfoTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.app.cts;
+
+import static android.content.Intent.ACTION_MAIN;
+
+import android.app.ActivityManager;
+import android.app.TaskInfo;
+import android.content.ComponentName;
+import android.content.Intent;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class ActivityManagerBaseTaskInfoTest {
+
+    protected void fillTaskInfo(TaskInfo taskInfo) {
+        taskInfo.taskId = Integer.MAX_VALUE;
+        taskInfo.isRunning = true;
+        taskInfo.baseIntent = new Intent(ACTION_MAIN);
+        ComponentName cn = new ComponentName("android.app.cts",
+                ActivityManagerBaseTaskInfoTest.class.getSimpleName());
+        taskInfo.baseActivity = cn.clone();
+        taskInfo.topActivity = cn.clone();
+        taskInfo.origActivity = cn.clone();
+        taskInfo.numActivities = Integer.MAX_VALUE;
+        taskInfo.taskDescription = new ActivityManager.TaskDescription("Test");
+    }
+
+    protected void verifyTaskInfo(TaskInfo taskInfo, TaskInfo sourceTaskInfo) {
+        assertEquals(sourceTaskInfo.taskId, taskInfo.taskId);
+
+        assertEquals(sourceTaskInfo.isRunning, taskInfo.isRunning);
+        assertTrue(sourceTaskInfo.baseIntent.filterEquals(taskInfo.baseIntent));
+        assertEquals(sourceTaskInfo.baseActivity, taskInfo.baseActivity);
+        assertEquals(sourceTaskInfo.topActivity, taskInfo.topActivity);
+        assertEquals(sourceTaskInfo.origActivity, taskInfo.origActivity);
+        assertEquals(sourceTaskInfo.numActivities, taskInfo.numActivities);
+        assertEquals(sourceTaskInfo.taskDescription.getLabel(),
+                taskInfo.taskDescription.getLabel());
+    }
+}
diff --git a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
index 7924d93..7047ec7 100644
--- a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
@@ -28,6 +28,8 @@
 import android.app.cts.android.app.cts.tools.UidImportanceListener;
 import android.app.cts.android.app.cts.tools.WaitForBroadcast;
 import android.app.cts.android.app.cts.tools.WatchUidRunner;
+import android.app.stubs.CommandReceiver;
+import android.app.stubs.ScreenOnActivity;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -54,7 +56,12 @@
     private static final String TAG = ActivityManagerProcessStateTest.class.getName();
 
     private static final String STUB_PACKAGE_NAME = "android.app.stubs";
+    private static final String PACKAGE_NAME_APP1 = "com.android.app1";
+    private static final String PACKAGE_NAME_APP2 = "com.android.app2";
+    private static final String PACKAGE_NAME_APP3 = "com.android.app3";
+
     private static final int WAIT_TIME = 2000;
+    private static final int WAITFOR_MSEC = 5000;
     // A secondary test activity from another APK.
     static final String SIMPLE_PACKAGE_NAME = "com.android.cts.launcherapps.simpleapp";
     static final String SIMPLE_SERVICE = ".SimpleService";
@@ -1353,4 +1360,182 @@
             uid1Watcher.finish();
         }
     }
+
+    /**
+     * Test a service binding cycle between two apps, with one of them also running a
+     * foreground service. The other app should also get an FGS proc state. On stopping the
+     * foreground service, app should go back to cached state.
+     * @throws Exception
+     */
+    public void testCycleFgs() throws Exception {
+        ApplicationInfo app1Info = mContext.getPackageManager().getApplicationInfo(
+                PACKAGE_NAME_APP1, 0);
+        ApplicationInfo app3Info = mContext.getPackageManager().getApplicationInfo(
+                PACKAGE_NAME_APP3, 0);
+        WatchUidRunner uid1Watcher = new WatchUidRunner(mInstrumentation, app1Info.uid,
+                WAITFOR_MSEC);
+        WatchUidRunner uid3Watcher = new WatchUidRunner(mInstrumentation, app3Info.uid,
+                WAITFOR_MSEC);
+
+        try {
+            CommandReceiver.sendCommand(mContext,
+                    CommandReceiver.COMMAND_START_FOREGROUND_SERVICE,
+                    PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null);
+            uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE);
+
+            CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE,
+                    PACKAGE_NAME_APP1, PACKAGE_NAME_APP3, 0, null);
+
+            uid3Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE);
+
+            // Create a cycle
+            CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE,
+                    PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null);
+
+            try {
+                uid3Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE,
+                        WatchUidRunner.STATE_CACHED_EMPTY);
+                fail("App3 should not be demoted to cached");
+            } catch (IllegalStateException ise) {
+                // Didn't go to cached in spite of cycle. Good!
+            }
+
+            // Stop the foreground service
+            CommandReceiver.sendCommand(mContext, CommandReceiver
+                            .COMMAND_STOP_FOREGROUND_SERVICE,
+                    PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null);
+
+            // Check that the app's proc state has fallen
+            uid3Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
+            uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
+        } finally {
+            uid1Watcher.finish();
+            uid3Watcher.finish();
+        }
+    }
+
+    private final <T extends Activity> Activity startSubActivity(Class<T> activityClass) {
+        final Instrumentation.ActivityResult result = new Instrumentation.ActivityResult(
+                0, new Intent());
+        final Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
+                activityClass.getName(), result, false);
+        mInstrumentation.addMonitor(monitor);
+        launchActivity(STUB_PACKAGE_NAME, activityClass, null);
+        return monitor.waitForActivity();
+    }
+
+    public void testCycleTop() throws Exception {
+        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 uid1Listener = new UidImportanceListener(mContext,
+                app1Info.uid, ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE,
+                WAITFOR_MSEC);
+        uid1Listener.register();
+
+        UidImportanceListener uid1ServiceListener = new UidImportanceListener(mContext,
+                app1Info.uid, ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
+                WAITFOR_MSEC);
+        uid1ServiceListener.register();
+
+        UidImportanceListener uid2Listener = new UidImportanceListener(mContext,
+                app2Info.uid, ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE,
+                WAITFOR_MSEC);
+        uid2Listener.register();
+
+        UidImportanceListener uid2ServiceListener = new UidImportanceListener(mContext,
+                app2Info.uid, ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
+                WAITFOR_MSEC);
+        uid2ServiceListener.register();
+
+        UidImportanceListener uid3Listener = new UidImportanceListener(mContext,
+                app3Info.uid, ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE,
+                WAITFOR_MSEC);
+        uid3Listener.register();
+
+        Activity activity = null;
+
+        try {
+            // Start an activity
+            activity = startSubActivity(ScreenOnActivity.class);
+
+            // Start a FGS in app2
+            CommandReceiver.sendCommand(mContext,
+                    CommandReceiver.COMMAND_START_FOREGROUND_SERVICE, PACKAGE_NAME_APP2,
+                    PACKAGE_NAME_APP2, 0, null);
+
+            uid2Listener.waitForValue(
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE,
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
+
+            // Bind from TOP to the service in app1
+            CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE,
+                    STUB_PACKAGE_NAME, PACKAGE_NAME_APP1, 0, null);
+
+            uid1Listener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND,
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
+
+            // Bind from app1 to a service in app2
+            CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE,
+                    PACKAGE_NAME_APP1, PACKAGE_NAME_APP2, 0, null);
+
+            // Bind from app2 to a service in app3
+            CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE,
+                    PACKAGE_NAME_APP2, PACKAGE_NAME_APP3, 0, null);
+
+            uid3Listener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND,
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
+
+            // Create a cycle
+            CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE,
+                    PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null);
+
+            try {
+                uid3Listener.waitForValue(ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
+                        ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED);
+                fail("App3 should not be demoted to cached, expecting FGS");
+            } catch (IllegalStateException e) {
+                // Didn't go to cached in spite of cycle. Good!
+            }
+
+            // Unbind from the TOP app
+            CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
+                    STUB_PACKAGE_NAME, PACKAGE_NAME_APP1, 0, null);
+
+            // Check that the apps' proc state is FOREGROUND_SERVICE
+            uid2Listener.waitForValue(
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE,
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
+
+            // Stop the foreground service
+            CommandReceiver.sendCommand(mContext, CommandReceiver
+                            .COMMAND_STOP_FOREGROUND_SERVICE,
+                    PACKAGE_NAME_APP2, PACKAGE_NAME_APP2, 0, null);
+
+            // Check that the apps fall down to cached state
+            uid1ServiceListener.waitForValue(
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED);
+
+            uid2ServiceListener.waitForValue(
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED);
+            uid3Listener.waitForValue(
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED,
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED);
+        } finally {
+            uid1Listener.unregister();
+            uid1ServiceListener.unregister();
+            uid2Listener.unregister();
+            uid2ServiceListener.unregister();
+            uid3Listener.unregister();
+            if (activity != null) {
+                activity.finish();
+            }
+        }
+    }
 }
diff --git a/tests/app/src/android/app/cts/ActivityManagerRecentTaskInfoTest.java b/tests/app/src/android/app/cts/ActivityManagerRecentTaskInfoTest.java
index 652dd400..58a3687 100644
--- a/tests/app/src/android/app/cts/ActivityManagerRecentTaskInfoTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerRecentTaskInfoTest.java
@@ -16,93 +16,65 @@
 package android.app.cts;
 
 import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.content.Intent;
 import android.os.Parcel;
-import android.test.AndroidTestCase;
+import android.support.test.runner.AndroidJUnit4;
 
-public class ActivityManagerRecentTaskInfoTest extends AndroidTestCase {
-    protected ActivityManager.RecentTaskInfo mRecentTaskInfo;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+import static org.junit.Assert.assertEquals;
+
+/**
+ * atest CtsAppTestCases:ActivityManagerRecentTaskInfoTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class ActivityManagerRecentTaskInfoTest extends ActivityManagerBaseTaskInfoTest {
+
+    private ActivityManager.RecentTaskInfo mRecentTaskInfo;
+
+    @Before
+    public void setUp() throws Exception {
         mRecentTaskInfo = new ActivityManager.RecentTaskInfo();
+        mRecentTaskInfo.id = 1;
+        mRecentTaskInfo.persistentId = 1;
     }
 
+    @Test
     public void testConstructor() {
         new ActivityManager.RecentTaskInfo();
     }
 
+    @Test
     public void testDescribeContents() {
         assertEquals(0, mRecentTaskInfo.describeContents());
     }
 
-    public void testWriteToParcel() throws Exception {
-        int id = 1;
-        Intent baseIntent = null;
-        ComponentName origActivity = null;
-        mRecentTaskInfo.id = id;
-        mRecentTaskInfo.baseIntent = baseIntent;
-        mRecentTaskInfo.origActivity = origActivity;
-
+    @Test
+    public void testWriteToParcel() {
+        fillTaskInfo(mRecentTaskInfo);
         Parcel parcel = Parcel.obtain();
         mRecentTaskInfo.writeToParcel(parcel, 0);
         parcel.setDataPosition(0);
-        ActivityManager.RecentTaskInfo values = ActivityManager.RecentTaskInfo.CREATOR
+
+        ActivityManager.RecentTaskInfo info = ActivityManager.RecentTaskInfo.CREATOR
                 .createFromParcel(parcel);
-        assertEquals(id, values.id);
-        assertEquals(null, values.baseIntent);
-        assertEquals(null, values.origActivity);
-        // test baseIntent,origActivity is not null, and id is -1(not running)
-        baseIntent = new Intent();
-        baseIntent.setAction(Intent.ACTION_CALL);
-        origActivity = new ComponentName(mContext, this.getClass());
-        mRecentTaskInfo.id = -1;
-        mRecentTaskInfo.baseIntent = baseIntent;
-        mRecentTaskInfo.origActivity = origActivity;
-        parcel = Parcel.obtain();
-        mRecentTaskInfo.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        values = ActivityManager.RecentTaskInfo.CREATOR
-                .createFromParcel(parcel);
-        assertEquals(-1, values.id);
-        assertNotNull(values.baseIntent);
-        assertEquals(Intent.ACTION_CALL, values.baseIntent.getAction());
-        assertEquals(origActivity, values.origActivity);
+        verifyTaskInfo(info, mRecentTaskInfo);
+        assertEquals(1, info.id);
+        assertEquals(1, info.persistentId);
     }
 
-    public void testReadFromParcel() throws Exception {
-        int id = 1;
-        Intent baseIntent = null;
-        ComponentName origActivity = null;
-        mRecentTaskInfo.id = id;
-        mRecentTaskInfo.baseIntent = baseIntent;
-        mRecentTaskInfo.origActivity = origActivity;
-
+    @Test
+    public void testReadFromParcel() {
+        fillTaskInfo(mRecentTaskInfo);
         Parcel parcel = Parcel.obtain();
         mRecentTaskInfo.writeToParcel(parcel, 0);
         parcel.setDataPosition(0);
-        ActivityManager.RecentTaskInfo values = new ActivityManager.RecentTaskInfo();
-        values.readFromParcel(parcel);
-        assertEquals(id, values.id);
-        assertEquals(null, values.baseIntent);
-        assertEquals(null, values.origActivity);
-        // test baseIntent,origActivity is not null, and id is -1(not running)
-        baseIntent = new Intent();
-        baseIntent.setAction(Intent.ACTION_CALL);
-        origActivity = new ComponentName(mContext, this.getClass());
-        mRecentTaskInfo.id = -1;
-        mRecentTaskInfo.baseIntent = baseIntent;
-        mRecentTaskInfo.origActivity = origActivity;
-        parcel = Parcel.obtain();
-        mRecentTaskInfo.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        values.readFromParcel(parcel);
-        assertEquals(-1, values.id);
-        assertNotNull(values.baseIntent);
-        assertEquals(Intent.ACTION_CALL, values.baseIntent.getAction());
-        assertEquals(origActivity, values.origActivity);
-    }
 
+        ActivityManager.RecentTaskInfo info = new ActivityManager.RecentTaskInfo();
+        info.readFromParcel(parcel);
+        verifyTaskInfo(info, mRecentTaskInfo);
+        assertEquals(1, info.id);
+        assertEquals(1, info.persistentId);
+    }
 }
diff --git a/tests/app/src/android/app/cts/ActivityManagerRunningTaskInfoTest.java b/tests/app/src/android/app/cts/ActivityManagerRunningTaskInfoTest.java
index 4857c03..9889a4b 100644
--- a/tests/app/src/android/app/cts/ActivityManagerRunningTaskInfoTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerRunningTaskInfoTest.java
@@ -16,87 +16,63 @@
 package android.app.cts;
 
 import android.app.ActivityManager;
-import android.graphics.Bitmap;
+import android.os.Debug;
 import android.os.Parcel;
-import android.test.AndroidTestCase;
+import android.support.test.runner.AndroidJUnit4;
 
-public class ActivityManagerRunningTaskInfoTest extends AndroidTestCase {
-    protected ActivityManager.RunningTaskInfo mRunningTaskInfo;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+import static org.junit.Assert.assertEquals;
+
+/**
+ * atest CtsAppTestCases:ActivityManagerRunningTaskInfoTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class ActivityManagerRunningTaskInfoTest extends ActivityManagerBaseTaskInfoTest {
+
+    private ActivityManager.RunningTaskInfo mRunningTaskInfo;
+
+    @Before
+    public void setUp() throws Exception {
         mRunningTaskInfo = new ActivityManager.RunningTaskInfo();
-
         mRunningTaskInfo.id = 1;
-        mRunningTaskInfo.baseActivity = null;
-        mRunningTaskInfo.topActivity = null;
-        mRunningTaskInfo.thumbnail = null;
-        mRunningTaskInfo.numActivities = 1;
-        mRunningTaskInfo.numRunning = 2;
-        mRunningTaskInfo.description = null;
     }
 
+    @Test
     public void testConstructor() {
         new ActivityManager.RunningTaskInfo();
     }
 
+    @Test
     public void testDescribeContents() {
         assertEquals(0, mRunningTaskInfo.describeContents());
     }
 
-    public void testWriteToParcel() throws Exception {
-
+    @Test
+    public void testWriteToParcel() {
+        fillTaskInfo(mRunningTaskInfo);
         Parcel parcel = Parcel.obtain();
         mRunningTaskInfo.writeToParcel(parcel, 0);
         parcel.setDataPosition(0);
-        ActivityManager.RunningTaskInfo values = ActivityManager.RunningTaskInfo.CREATOR
-                .createFromParcel(parcel);
-        assertEquals(1, values.id);
-        assertNull(values.baseActivity);
-        assertNull(values.topActivity);
-        assertNull(values.thumbnail);
-        assertEquals(1, values.numActivities);
-        assertEquals(2, values.numRunning);
-        // test thumbnail is not null
-        mRunningTaskInfo.thumbnail = Bitmap.createBitmap(480, 320,
-                Bitmap.Config.RGB_565);
-        parcel = Parcel.obtain();
-        mRunningTaskInfo.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        values = ActivityManager.RunningTaskInfo.CREATOR
-                .createFromParcel(parcel);
-        assertNotNull(values.thumbnail);
-        assertEquals(320, values.thumbnail.getHeight());
-        assertEquals(480, values.thumbnail.getWidth());
-        assertEquals(Bitmap.Config.RGB_565, values.thumbnail.getConfig());
 
+        ActivityManager.RunningTaskInfo info = ActivityManager.RunningTaskInfo.CREATOR
+                .createFromParcel(parcel);
+        verifyTaskInfo(info, mRunningTaskInfo);
+        assertEquals(1, info.id);
     }
 
-    public void testReadFromParcel() throws Exception {
-
+    @Test
+    public void testReadFromParcel() {
+        fillTaskInfo(mRunningTaskInfo);
         Parcel parcel = Parcel.obtain();
         mRunningTaskInfo.writeToParcel(parcel, 0);
         parcel.setDataPosition(0);
-        ActivityManager.RunningTaskInfo values = new ActivityManager.RunningTaskInfo();
-        values.readFromParcel(parcel);
-        assertEquals(1, values.id);
-        assertNull(values.baseActivity);
-        assertNull(values.topActivity);
-        assertNull(values.thumbnail);
-        assertEquals(1, values.numActivities);
-        assertEquals(2, values.numRunning);
-        // test thumbnail is not null
-        mRunningTaskInfo.thumbnail = Bitmap.createBitmap(480, 320,
-                Bitmap.Config.RGB_565);
-        parcel = Parcel.obtain();
-        mRunningTaskInfo.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        values.readFromParcel(parcel);
-        assertNotNull(values.thumbnail);
-        assertEquals(320, values.thumbnail.getHeight());
-        assertEquals(480, values.thumbnail.getWidth());
-        assertEquals(Bitmap.Config.RGB_565, values.thumbnail.getConfig());
-    }
 
+        ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
+        info.readFromParcel(parcel);
+        verifyTaskInfo(info, mRunningTaskInfo);
+        assertEquals(1, info.id);
+    }
 }
diff --git a/tests/app/src/android/app/cts/AppTaskTests.java b/tests/app/src/android/app/cts/AppTaskTests.java
new file mode 100644
index 0000000..16cb03b
--- /dev/null
+++ b/tests/app/src/android/app/cts/AppTaskTests.java
@@ -0,0 +1,261 @@
+/*
+ * 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.app.cts;
+
+import static android.content.Context.ACTIVITY_SERVICE;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.Instrumentation;
+import android.app.stubs.MockActivity;
+import android.app.stubs.MockListActivity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.runner.lifecycle.ActivityLifecycleCallback;
+import android.support.test.runner.lifecycle.ActivityLifecycleMonitor;
+import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
+import android.support.test.runner.lifecycle.Stage;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BooleanSupplier;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+/**
+ * atest CtsAppTestCases:AppTaskTests
+ */
+@MediumTest
+@FlakyTest(detail = "Can be promoted to pre-submit once confirmed stable.")
+@RunWith(AndroidJUnit4.class)
+public class AppTaskTests {
+
+    private static final long TIME_SLICE_MS = 100;
+    private static final long MAX_WAIT_MS = 1500;
+
+    private Instrumentation mInstrumentation;
+    private ActivityLifecycleMonitor mLifecycleMonitor;
+    private Context mTargetContext;
+
+    @Rule
+    public ActivityTestRule<MockActivity> mActivityRule =
+            new ActivityTestRule<MockActivity>(MockActivity.class) {
+        @Override
+        public Intent getActivityIntent() {
+            Intent intent = new Intent();
+            intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NEW_DOCUMENT
+                    | FLAG_ACTIVITY_MULTIPLE_TASK);
+            return intent;
+        }
+    };
+
+    @Before
+    public void setUp() throws Exception {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mLifecycleMonitor = ActivityLifecycleMonitorRegistry.getInstance();
+        mTargetContext = mInstrumentation.getTargetContext();
+        removeAllAppTasks();
+    }
+
+    /**
+     * Launch an activity and ensure it is in the app task list.
+     */
+    @Test
+    public void testSingleAppTask() throws Exception {
+        final Activity a1 = mActivityRule.launchActivity(null);
+        final List<ActivityManager.AppTask> appTasks = getAppTasks();
+        assertTrue(appTasks.size() == 1);
+        assertTrue(appTasks.get(0).getTaskInfo().topActivity.equals(a1.getComponentName()));
+    }
+
+    /**
+     * Launch a couple tasks and ensure they are in the app tasks list.
+     */
+    @Test
+    public void testMultipleAppTasks() throws Exception {
+        final ArrayList<Activity> activities = new ArrayList<>();
+        for (int i = 0; i < 5; i++) {
+            activities.add(mActivityRule.launchActivity(null));
+        }
+        final List<ActivityManager.AppTask> appTasks = getAppTasks();
+        assertTrue(appTasks.size() == activities.size());
+        for (int i = 0; i < appTasks.size(); i++) {
+            assertTrue(appTasks.get(i).getTaskInfo().topActivity.equals(
+                    activities.get(i).getComponentName()));
+        }
+    }
+
+    /**
+     * Remove an app task and ensure that it is actually removed.
+     */
+    @Test
+    public void testFinishAndRemoveTask() throws Exception {
+        final Activity a1 = mActivityRule.launchActivity(null);
+        waitAndAssertCondition(() -> getAppTasks().size() == 1, "Expected 1 running task");
+        getAppTask(a1).finishAndRemoveTask();
+        waitAndAssertCondition(() -> getAppTasks().isEmpty(), "Expected no running tasks");
+    }
+
+    /**
+     * Ensure that moveToFront will bring the first activity forward.
+     */
+    @Test
+    public void testMoveToFront() throws Exception {
+        final Activity a1 = mActivityRule.launchActivity(null);
+        final Activity a2 = mActivityRule.launchActivity(null);
+        final BooleanValue targetResumed = new BooleanValue();
+        mLifecycleMonitor.addLifecycleCallback(new ActivityLifecycleCallback() {
+            public void onActivityLifecycleChanged(Activity activity, Stage stage) {
+                if (activity == a1 && stage == Stage.RESUMED) {
+                    targetResumed.value = true;
+                }
+            }
+        });
+
+        getAppTask(a1).moveToFront();
+        waitAndAssertCondition(() -> targetResumed.value,
+                "Expected activity brought to front and resumed");
+    }
+
+    /**
+     * Ensure that starting a new activity in the same task results in two activities in the task.
+     */
+    @Test
+    public void testStartActivityInTask_NoNewTask() throws Exception {
+        final Activity a1 = mActivityRule.launchActivity(null);
+        final ActivityManager.AppTask task = getAppTask(a1);
+        final Intent intent = new Intent();
+        intent.setComponent(new ComponentName(mTargetContext, MockListActivity.class));
+        task.startActivity(mTargetContext, intent, null);
+        waitAndAssertCondition(
+                () -> getAppTask(a1) != null && getAppTask(a1).getTaskInfo().numActivities == 2,
+                "Waiting for activity launch");
+
+        final ActivityManager.RecentTaskInfo taskInfo = task.getTaskInfo();
+        assertTrue(taskInfo.numActivities == 2);
+        assertTrue(taskInfo.baseActivity.equals(a1.getComponentName()));
+        assertTrue(taskInfo.topActivity.equals(intent.getComponent()));
+    }
+
+    /**
+     * Ensure that an activity with FLAG_ACTIVITY_NEW_TASK causes the task to be brought forward
+     * and the new activity not being started.
+     */
+    @Test
+    public void testStartActivityInTask_NewTask() throws Exception {
+        final Activity a1 = mActivityRule.launchActivity(null);
+        final ActivityManager.AppTask task = getAppTask(a1);
+        final Intent intent = new Intent();
+        intent.setComponent(new ComponentName(mTargetContext, MockActivity.class));
+        intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
+        task.startActivity(mTargetContext, intent, null);
+
+        final ActivityManager.RecentTaskInfo taskInfo = task.getTaskInfo();
+        assertTrue(taskInfo.numActivities == 1);
+        assertTrue(taskInfo.baseActivity.equals(a1.getComponentName()));
+    }
+
+    /**
+     * Ensure that the activity that is excluded from recents is reflected in the recent task info.
+     */
+    @Test
+    public void testSetExcludeFromRecents() throws Exception {
+        final Activity a1 = mActivityRule.launchActivity(null);
+        final List<ActivityManager.AppTask> appTasks = getAppTasks();
+        final ActivityManager.AppTask t1 = appTasks.get(0);
+        t1.setExcludeFromRecents(true);
+        assertTrue((t1.getTaskInfo().baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+                != 0);
+        t1.setExcludeFromRecents(false);
+        assertTrue((t1.getTaskInfo().baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+                == 0);
+    }
+
+    /**
+     * @return all the {@param ActivityManager.AppTask}s for the current app.
+     */
+    private List<ActivityManager.AppTask> getAppTasks() {
+        ActivityManager am = (ActivityManager) mTargetContext.getSystemService(ACTIVITY_SERVICE);
+        return am.getAppTasks();
+    }
+
+    /**
+     * @return the {@param ActivityManager.AppTask} for the associated activity.
+     */
+    private ActivityManager.AppTask getAppTask(Activity activity) {
+        waitAndAssertCondition(() -> getAppTask(getAppTasks(), activity) != null,
+                "Waiting for app task");
+        return getAppTask(getAppTasks(), activity);
+    }
+
+    private ActivityManager.AppTask getAppTask(List<ActivityManager.AppTask> appTasks,
+            Activity activity) {
+        for (ActivityManager.AppTask task : appTasks) {
+            if (task.getTaskInfo().taskId == activity.getTaskId()) {
+                return task;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Removes all the app tasks the test app.
+     */
+    private void removeAllAppTasks() {
+        final List<ActivityManager.AppTask> appTasks = getAppTasks();
+        for (ActivityManager.AppTask task : appTasks) {
+            task.finishAndRemoveTask();
+        }
+        waitAndAssertCondition(() -> getAppTasks().isEmpty(),
+                "Expected no app tasks after all removed");
+    }
+
+    private void waitAndAssertCondition(BooleanSupplier condition, String failMsgContext) {
+        long startTime = SystemClock.elapsedRealtime();
+        while (true) {
+            if (condition.getAsBoolean()) {
+                // Condition passed
+                return;
+            } else if (SystemClock.elapsedRealtime() > (startTime + MAX_WAIT_MS)) {
+                // Timed out
+                fail("Timed out waiting for: " + failMsgContext);
+            } else {
+                SystemClock.sleep(TIME_SLICE_MS);
+            }
+        }
+    }
+
+    private class BooleanValue {
+        boolean value;
+    }
+}
\ No newline at end of file
diff --git a/tests/app/src/android/app/cts/TaskDescriptionTest.java b/tests/app/src/android/app/cts/TaskDescriptionTest.java
index 408930e..fa35f16 100644
--- a/tests/app/src/android/app/cts/TaskDescriptionTest.java
+++ b/tests/app/src/android/app/cts/TaskDescriptionTest.java
@@ -27,8 +27,10 @@
 import android.app.ActivityManager.TaskDescription;
 import android.graphics.Bitmap;
 import android.graphics.Color;
+import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 import java.util.List;
+import java.util.function.BooleanSupplier;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -42,8 +44,7 @@
 import android.support.test.runner.AndroidJUnit4;
 
 /**
- * Build: mmma -j32 cts/tests/app
- * Run: cts/tests/framework/base/activitymanager/util/run-test CtsAppTestCases android.app.cts.TaskDescriptionTest
+ * Build & Run: atest android.app.cts.TaskDescriptionTest
  */
 @RunWith(AndroidJUnit4.class)
 @Presubmit
@@ -52,6 +53,8 @@
     private static final int TEST_NO_DATA = 0;
     private static final int TEST_RES_DATA = 777;
     private static final int TEST_COLOR = Color.BLACK;
+    private static final int WAIT_TIMEOUT_MS = 1000;
+    private static final int WAIT_RETRIES = 5;
 
     @Rule
     public ActivityTestRule<MockActivity> mTaskDescriptionActivity =
@@ -83,12 +86,17 @@
                 final TaskDescription td = info.taskDescription;
                 assertNotNull(td);
                 if (resId == TEST_NO_DATA) {
-                    assertNotNull(td.getIcon());
-                    assertNotNull(td.getIconFilename());
+                    // TaskPersister at the worst case scenario waits 3 secs (PRE_TASK_DELAY_MS) to
+                    // write the image to disk if its write time has ended
+                    waitFor("TaskDescription's icon is null", () -> td.getIcon() != null);
+                    waitFor("TaskDescription's icon filename is null",
+                            () -> td.getIconFilename() != null);
                 } else {
-                    assertNull(td.getIconFilename());
-                    assertNull(td.getIcon());
+                    waitFor("TaskDescription's icon is not null", () -> td.getIcon() == null);
+                    waitFor("TaskDescription's icon filename is not null",
+                            () -> td.getIconFilename() == null);
                 }
+
                 assertEquals(resId, td.getIconResource());
                 assertEquals(label, td.getLabel());
                 return;
@@ -96,4 +104,14 @@
         }
         fail("Did not find activity (id=" + activity.getTaskId() + ") in recent tasks list");
     }
+
+    private void waitFor(String message, BooleanSupplier waitCondition) {
+        for (int retry = 0; retry < WAIT_RETRIES; retry++) {
+            if (waitCondition.getAsBoolean()) {
+                return;
+            }
+            SystemClock.sleep(WAIT_TIMEOUT_MS);
+        }
+        fail(message);
+    }
 }
diff --git a/tests/autofillservice/AndroidManifest.xml b/tests/autofillservice/AndroidManifest.xml
index e47e6df..69a07af 100644
--- a/tests/autofillservice/AndroidManifest.xml
+++ b/tests/autofillservice/AndroidManifest.xml
@@ -95,10 +95,30 @@
             android:exported="true" />
 
         <activity android:name=".TrampolineForResultActivity" />
+        <activity android:name=".OnCreateServiceStatusVerifierActivity"/>
+        <activity android:name=".UsernameOnlyActivity" >
+            <intent-filter>
+                <!-- This intent filter is not really needed by CTS, but it maks easier to launch
+                     this app during CTS development... -->
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".PasswordOnlyActivity" >
+            <intent-filter>
+                <!-- This intent filter is not really needed by CTS, but it maks easier to launch
+                     this app during CTS development... -->
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
 
         <receiver android:name=".SelfDestructReceiver"
             android:exported="true"
             android:process="android.autofillservice.cts.outside"/>
+        <receiver android:name=".OutOfProcessLoginActivityFinisherReceiver"
+            android:exported="true"
+            android:process="android.autofillservice.cts.outside"/>
 
         <service
             android:name=".InstrumentedAutoFillService"
diff --git a/tests/autofillservice/AndroidTest.xml b/tests/autofillservice/AndroidTest.xml
index a94da63..4fe406f 100644
--- a/tests/autofillservice/AndroidTest.xml
+++ b/tests/autofillservice/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for AutoFill Framework CTS tests.">
   <option name="test-suite-tag" value="cts" />
   <option name="config-descriptor:metadata" key="component" value="autofill" />
+  <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
 
   <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
     <option name="cleanup-apks" value="true" />
diff --git a/tests/autofillservice/assets/login.html b/tests/autofillservice/assets/login.html
index 60e7cf7..4d8effc 100644
--- a/tests/autofillservice/assets/login.html
+++ b/tests/autofillservice/assets/login.html
@@ -17,8 +17,8 @@
 <html>
 <body>
 <form action="login.html" name="FORM AM I">
-    Username: <input type="text" name="username" autocomplete="username" placeholder="There's no place like a holder"/><br/>
-    Password: <input type="password" name="password" autocomplete="current-password" placeholder="Holder it like it cannnot passer a word"/><br/>
+    Username: <input type="text" name="username" autocomplete="username" placeholder="There's no place like a holder" oninput="javascript:JsHandler.onUsernameChanged(this.value)"/><br/>
+    Password: <input type="password" name="password" autocomplete="current-password" placeholder="Holder it like it cannnot passer a word" oninput="javascript:JsHandler.onPasswordChanged(this.value)"/><br/>
     <br/>
     <input type="submit" value="Login"/>
 </form>
diff --git a/tests/autofillservice/res/layout/fragment_container.xml b/tests/autofillservice/res/layout/fragment_container.xml
index 156efad..f5600e6 100644
--- a/tests/autofillservice/res/layout/fragment_container.xml
+++ b/tests/autofillservice/res/layout/fragment_container.xml
@@ -19,4 +19,6 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:focusable="true"
+    android:focusableInTouchMode="true"
     android:id="@+id/rootContainer" />
diff --git a/tests/autofillservice/res/layout/login_with_custom_highlight_activity.xml b/tests/autofillservice/res/layout/login_with_custom_highlight_activity.xml
deleted file mode 100644
index 73926a9..0000000
--- a/tests/autofillservice/res/layout/login_with_custom_highlight_activity.xml
+++ /dev/null
@@ -1,105 +0,0 @@
-<?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"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:focusable="true"
-    android:theme="@style/MyAutofilledHighlight"
-    android:focusableInTouchMode="true"
-    android:orientation="vertical" >
-
-    <LinearLayout
-        android:id="@+id/username_container"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal" >
-
-        <TextView
-            android:id="@+id/username_label"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@+string/username_string" />
-
-        <EditText
-            android:id="@+id/username"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content" />
-    </LinearLayout>
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal" >
-
-        <TextView
-            android:id="@+id/password_label"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@+string/password_string" />
-
-        <EditText
-            android:id="@+id/password"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:inputType="textPassword"/>
-    </LinearLayout>
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal" >
-
-    </LinearLayout>
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal" >
-
-        <Button
-            android:id="@+id/clear"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="Clear" />
-
-        <Button
-            android:id="@+id/save"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="Save" />
-
-        <Button
-            android:id="@+id/login"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="Login" />
-
-        <Button
-            android:id="@+id/cancel"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="Cancel" />
-    </LinearLayout>
-
-    <TextView
-        android:id="@+id/output"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/tests/autofillservice/res/layout/login_with_strings_activity.xml b/tests/autofillservice/res/layout/login_with_strings_activity.xml
index 2f90761..972f53f 100644
--- a/tests/autofillservice/res/layout/login_with_strings_activity.xml
+++ b/tests/autofillservice/res/layout/login_with_strings_activity.xml
@@ -33,7 +33,7 @@
             android:id="@+id/username_label"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:text="@+string/username_string" />
+            android:text="@string/username_string" />
 
         <EditText
             android:id="@+id/username"
@@ -50,7 +50,7 @@
             android:id="@+id/password_label"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:text="@+string/password_string" />
+            android:text="@string/password_string" />
 
         <EditText
             android:id="@+id/password"
diff --git a/tests/autofillservice/res/layout/password_only_activity.xml b/tests/autofillservice/res/layout/password_only_activity.xml
new file mode 100644
index 0000000..dff1d3e
--- /dev/null
+++ b/tests/autofillservice/res/layout/password_only_activity.xml
@@ -0,0 +1,60 @@
+<?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"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:focusable="true"
+    android:focusableInTouchMode="true"
+    android:orientation="vertical" >
+
+    <TextView
+        android:id="@+id/welcome"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <TextView
+            android:id="@+id/password_label"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Password" />
+
+        <EditText
+            android:id="@+id/password"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textPassword" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <Button
+            android:id="@+id/login"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Login" />
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/autofillservice/res/layout/username_only_activity.xml b/tests/autofillservice/res/layout/username_only_activity.xml
new file mode 100644
index 0000000..aacbefd
--- /dev/null
+++ b/tests/autofillservice/res/layout/username_only_activity.xml
@@ -0,0 +1,55 @@
+<?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"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:focusable="true"
+    android:focusableInTouchMode="true"
+    android:orientation="vertical" >
+
+    <LinearLayout
+        android:id="@+id/username_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <TextView
+            android:id="@+id/username_label"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Username" />
+
+        <EditText
+            android:id="@+id/username"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <Button
+            android:id="@+id/next"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Next" />
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/autofillservice/res/layout/view_attribute_test_activity.xml b/tests/autofillservice/res/layout/view_attribute_test_activity.xml
index d03d65c..f2b7c3b 100644
--- a/tests/autofillservice/res/layout/view_attribute_test_activity.xml
+++ b/tests/autofillservice/res/layout/view_attribute_test_activity.xml
@@ -19,6 +19,8 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
+    android:focusable="true"
+    android:focusableInTouchMode="true"
     android:id="@+id/rootContainer">
     <EditText android:id="@+id/editTextNoHint"
         android:layout_width="wrap_content"
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java b/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java
index a8a8091..3ee3e79 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java
@@ -22,10 +22,13 @@
 import android.app.Activity;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.view.PixelCopy;
 import android.view.View;
 import android.view.autofill.AutofillManager;
 
+import androidx.annotation.NonNull;
+
 import com.android.compatibility.common.util.SynchronousPixelCopy;
 
 import java.util.concurrent.CountDownLatch;
@@ -36,6 +39,7 @@
   */
 abstract class AbstractAutoFillActivity extends Activity {
 
+    private final CountDownLatch mDestroyedLatch = new CountDownLatch(1);
     private MyAutofillCallback mCallback;
 
     /**
@@ -121,13 +125,46 @@
         unregisterNonNullCallback();
     }
 
+    /**
+     * Waits until {@link #onDestroy()} is called.
+     */
+    public void waintUntilDestroyed(@NonNull Timeout timeout) throws InterruptedException {
+        if (!mDestroyedLatch.await(timeout.ms(), TimeUnit.MILLISECONDS)) {
+            throw new RetryableException(timeout, "activity %s not destroyed", this);
+        }
+    }
+
     private void unregisterNonNullCallback() {
         getAutofillManager().unregisterCallback(mCallback);
         mCallback = null;
     }
 
     @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        AutofillTestWatcher.registerActivity("onCreate()", this);
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+
+        // Activitiy is typically unregistered at finish(), but we need to unregister here too
+        // for the cases where it's destroyed due to a config change (like device rotation).
+        AutofillTestWatcher.unregisterActivity("onDestroy()", this);
+        mDestroyedLatch.countDown();
+    }
+
+    @Override
     public void finish() {
+        finishOnly();
+        AutofillTestWatcher.unregisterActivity("finish()", this);
+    }
+
+    /**
+     * Finishes the activity, without unregistering it from {@link AutofillTestWatcher}.
+     */
+    void finishOnly() {
         if (mCallback != null) {
             unregisterNonNullCallback();
         }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AbstractGridActivityTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AbstractGridActivityTestCase.java
new file mode 100644
index 0000000..b9d631e
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/AbstractGridActivityTestCase.java
@@ -0,0 +1,70 @@
+/*
+ * 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.autofillservice.cts;
+
+import android.view.accessibility.AccessibilityEvent;
+
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Base class for test cases using {@link GridActivity}.
+ */
+abstract class AbstractGridActivityTestCase
+        extends AutoFillServiceTestCase.AutoActivityLaunch<GridActivity> {
+
+    protected GridActivity mActivity;
+
+    @Override
+    protected AutofillActivityTestRule<GridActivity> getActivityRule() {
+        return new AutofillActivityTestRule<GridActivity>(GridActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+                postActivityLaunched();
+            }
+        };
+    }
+
+    /**
+     * Hook for subclass to customize activity after it's launched.
+     */
+    protected void postActivityLaunched() {
+    }
+
+    /**
+     * Focus to a cell and expect window event
+     */
+    protected void focusCell(int row, int column) throws TimeoutException {
+        mUiBot.waitForWindowChange(() -> mActivity.focusCell(row, column));
+    }
+
+    /**
+     * Focus to a cell and expect no window event.
+     */
+    protected void focusCellNoWindowChange(int row, int column) {
+        final AccessibilityEvent event;
+        try {
+            event = mUiBot.waitForWindowChange(() -> mActivity.focusCell(row, column),
+                    Timeouts.WINDOW_CHANGE_NOT_GENERATED_NAPTIME_MS);
+        } catch (TimeoutException ex) {
+            // no window events! looking good
+            return;
+        }
+        throw new IllegalStateException(String.format("Expect no window event when focusing to"
+                + " column %d row %d, but event happened: %s", row, column, event));
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AbstractLoginActivityTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AbstractLoginActivityTestCase.java
index 8ec9a3e..13c6e4d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AbstractLoginActivityTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AbstractLoginActivityTestCase.java
@@ -17,61 +17,56 @@
 package android.autofillservice.cts;
 
 import android.view.View;
-
-import org.junit.Before;
-import org.junit.Rule;
+import android.view.accessibility.AccessibilityEvent;
 
 import java.util.concurrent.TimeoutException;
 
 /**
  * Base class for test cases using {@link LoginActivity}.
  */
-abstract class AbstractLoginActivityTestCase extends AutoFillServiceTestCase {
-    @Rule
-    public final AutofillActivityTestRule<LoginActivity> mActivityRule =
-            new AutofillActivityTestRule<LoginActivity>(LoginActivity.class);
-
-    @Rule
-    public final AutofillActivityTestRule<CheckoutActivity> mCheckoutActivityRule =
-            new AutofillActivityTestRule<CheckoutActivity>(CheckoutActivity.class, false);
+abstract class AbstractLoginActivityTestCase
+        extends AutoFillServiceTestCase.AutoActivityLaunch<LoginActivity> {
 
     protected LoginActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<LoginActivity> getActivityRule() {
+        return new AutofillActivityTestRule<LoginActivity>(
+                LoginActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     /**
      * Requests focus on username and expect Window event happens.
      */
     protected void requestFocusOnUsername() throws TimeoutException {
-        mUiBot.waitForWindowChange(() -> mActivity.onUsername(View::requestFocus),
-                Timeouts.UI_TIMEOUT.getMaxValue());
+        mUiBot.waitForWindowChange(() -> mActivity.onUsername(View::requestFocus));
     }
 
     /**
      * Requests focus on username and expect no Window event happens.
      */
     protected void requestFocusOnUsernameNoWindowChange() {
+        final AccessibilityEvent event;
         try {
-            // TODO: define a small value in Timeout
-            mUiBot.waitForWindowChange(() -> mActivity.onUsername(View::requestFocus),
-                    Timeouts.UI_TIMEOUT.ms());
+            event = mUiBot.waitForWindowChange(() -> mActivity.onUsername(View::requestFocus),
+                    Timeouts.WINDOW_CHANGE_NOT_GENERATED_NAPTIME_MS);
         } catch (TimeoutException ex) {
             // no window events! looking good
             return;
         }
         throw new IllegalStateException("Expect no window event when focusing to"
-                + " username, but event happened");
+                + " username, but event happened: " + event);
     }
 
     /**
      * Requests focus on password and expect Window event happens.
      */
     protected void requestFocusOnPassword() throws TimeoutException {
-        mUiBot.waitForWindowChange(() -> mActivity.onPassword(View::requestFocus),
-                Timeouts.UI_TIMEOUT.getMaxValue());
+        mUiBot.waitForWindowChange(() -> mActivity.onPassword(View::requestFocus));
     }
-
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AttachedContextActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/AttachedContextActivityTest.java
index 50526dd..97ed220 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AttachedContextActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AttachedContextActivityTest.java
@@ -18,24 +18,25 @@
 
 import android.autofillservice.cts.AttachedContextActivity.FillExpectation;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 /**
  * Makes sure activity with attached context can be autofilled.
  */
-public class AttachedContextActivityTest extends AutoFillServiceTestCase {
-
-    @Rule
-    public final AutofillActivityTestRule<AttachedContextActivity> mActivityRule =
-            new AutofillActivityTestRule<>(AttachedContextActivity.class);
+public class AttachedContextActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<AttachedContextActivity> {
 
     private AttachedContextActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<AttachedContextActivity> getActivityRule() {
+        return new AutofillActivityTestRule<AttachedContextActivity>(
+                AttachedContextActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     @Test
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
index 577975f..b2a6caa 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
@@ -17,15 +17,13 @@
 package android.autofillservice.cts;
 
 import static android.autofillservice.cts.Helper.getContext;
-import static android.autofillservice.cts.Helper.getLoggingLevel;
-import static android.autofillservice.cts.Helper.hasAutofillFeature;
-import static android.autofillservice.cts.Helper.setLoggingLevel;
 import static android.autofillservice.cts.InstrumentedAutoFillService.SERVICE_NAME;
 import static android.autofillservice.cts.common.ShellHelper.runShellCommand;
 
 import android.autofillservice.cts.InstrumentedAutoFillService.Replier;
 import android.autofillservice.cts.common.SettingsStateKeeperRule;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.provider.Settings;
 import android.support.test.InstrumentationRegistry;
@@ -33,179 +31,275 @@
 import android.util.Log;
 import android.widget.RemoteViews;
 
+import androidx.annotation.NonNull;
+
 import com.android.compatibility.common.util.RequiredFeatureRule;
 
-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.rules.TestRule;
 import org.junit.rules.TestWatcher;
 import org.junit.runner.Description;
 import org.junit.runner.RunWith;
+import org.junit.runners.model.Statement;
 
 /**
- * Base class for all other tests.
+ * Placeholder for the base class for all integration tests:
+ *
+ * <ul>
+ *   <li>{@link AutoActivityLaunch}
+ *   <li>{@link ManualActivityLaunch}
+ * </ul>
+ *
+ * <p>These classes provide the common infrastructure such as:
+ *
+ * <ul>
+ *   <li>Preserving the autofill service settings.
+ *   <li>Cleaning up test state.
+ *   <li>Wrapping the test under autofill-specific test rules.
+ *   <li>Launching the activity used by the test.
+ * </ul>
  */
-@RunWith(AndroidJUnit4.class)
-// NOTE: @ClassRule requires it to be public
-public abstract class AutoFillServiceTestCase {
-    private static final String TAG = "AutoFillServiceTestCase";
+final class AutoFillServiceTestCase {
 
-    static final UiBot sDefaultUiBot = new UiBot();
+    /**
+     * Base class for all test cases that use an {@link AutofillActivityTestRule} to
+     * launch the activity.
+     */
+    // Must be public becaue of @ClassRule
+    public abstract static class AutoActivityLaunch<A extends AbstractAutoFillActivity>
+            extends BaseTestCase {
 
-    protected static final Replier sReplier = InstrumentedAutoFillService.getReplier();
+        @ClassRule
+        public static final SettingsStateKeeperRule sPublicServiceSettingsKeeper =
+                sTheRealServiceSettingsKeeper;
 
-    private static final Context sContext = InstrumentationRegistry.getTargetContext();
-
-    @ClassRule
-    public static final SettingsStateKeeperRule mServiceSettingsKeeper =
-            new SettingsStateKeeperRule(sContext, Settings.Secure.AUTOFILL_SERVICE);
-
-    @Rule
-    public final TestWatcher watcher = new TestWatcher() {
-        @Override
-        protected void starting(Description description) {
-            JUnitHelper.setCurrentTestName(description.getDisplayName());
+        protected AutoActivityLaunch() {
+            super(sDefaultUiBot);
         }
 
         @Override
-        protected void finished(Description description) {
-            JUnitHelper.setCurrentTestName(null);
+        protected TestRule getMainTestRule() {
+            return getActivityRule();
         }
-    };
 
-    @Rule
-    public final RetryRule mRetryRule = new RetryRule(2);
+        /**
+         * Gets the rule to launch the main activity for this test.
+         *
+         * <p><b>Note: </b>the rule must be either lazily generated or a static singleton, otherwise
+         * this method could return {@code null} when the rule chain that uses it is constructed.
+         *
+         */
+        protected abstract @NonNull AutofillActivityTestRule<A> getActivityRule();
 
-    @Rule
-    public final AutofillLoggingTestRule mLoggingRule = new AutofillLoggingTestRule(TAG);
-
-    @Rule
-    public final RequiredFeatureRule mRequiredFeatureRule =
-            new RequiredFeatureRule(PackageManager.FEATURE_AUTOFILL);
-
-    @Rule
-    public final SafeCleanerRule mSafeCleanerRule = new SafeCleanerRule()
-            .setDumper(mLoggingRule)
-            .run(() -> sReplier.assertNoUnhandledFillRequests())
-            .run(() -> sReplier.assertNoUnhandledSaveRequests())
-            .add(() -> { return sReplier.getExceptions(); });
-
-    protected final Context mContext = sContext;
-    protected final String mPackageName;
-    protected final UiBot mUiBot;
-
-    /**
-     * Stores the previous logging level so it's restored after the test.
-     */
-    private String mLoggingLevel;
-
-    protected AutoFillServiceTestCase() {
-        this(sDefaultUiBot);
-    }
-
-    protected AutoFillServiceTestCase(UiBot uiBot) {
-        mPackageName = mContext.getPackageName();
-        mUiBot = uiBot;
-        mUiBot.reset();
-    }
-
-    @BeforeClass
-    public static void prepareScreen() throws Exception {
-        if (!hasAutofillFeature()) return;
-
-        // Unlock screen.
-        runShellCommand("input keyevent KEYCODE_WAKEUP");
-
-        // Collapse notifications.
-        runShellCommand("cmd statusbar collapse");
-
-        // Set orientation as portrait, otherwise some tests might fail due to elements not fitting
-        // in, IME orientation, etc...
-        sDefaultUiBot.setScreenOrientation(UiBot.PORTRAIT);
-    }
-
-    @Before
-    public void cleanupStaticState() {
-        Helper.preTestCleanup();
-        sReplier.reset();
-    }
-
-    @Before
-    public void setVerboseLogging() {
-        try {
-            mLoggingLevel = getLoggingLevel();
-        } catch (Exception e) {
-            Log.w(TAG, "Could not get previous logging level: " + e);
-            mLoggingLevel = "debug";
+        protected @NonNull A launchActivity(@NonNull Intent intent) {
+            return getActivityRule().launchActivity(intent);
         }
-        try {
-            setLoggingLevel("verbose");
-        } catch (Exception e) {
-            Log.w(TAG, "Could not change logging level to verbose: " + e);
+
+        protected @NonNull A getActivity() {
+            return getActivityRule().getActivity();
         }
     }
 
     /**
-     * Cleans up activities that might have been left over.
+     * Base class for all test cases that don't require an {@link AutofillActivityTestRule}.
      */
-    @Before
-    @After
-    public void finishActivities() {
-        WelcomeActivity.finishIt(mUiBot);
-    }
+    // Must be public becaue of @ClassRule
+    public abstract static class ManualActivityLaunch extends BaseTestCase {
 
-    @After
-    public void resetVerboseLogging() {
-        try {
-            setLoggingLevel(mLoggingLevel);
-        } catch (Exception e) {
-            Log.w(TAG, "Could not restore logging level to " + mLoggingLevel + ": " + e);
+        @ClassRule
+        public static final SettingsStateKeeperRule sPublicServiceSettingsKeeper =
+                sTheRealServiceSettingsKeeper;
+
+        protected ManualActivityLaunch() {
+            this(sDefaultUiBot);
+        }
+
+        protected ManualActivityLaunch(@NonNull UiBot uiBot) {
+            super(uiBot);
+        }
+
+        @Override
+        protected TestRule getMainTestRule() {
+            return new TestRule() {
+
+                @Override
+                public Statement apply(Statement base, Description description) {
+                    // Returns a no-op statements
+                    return new Statement() {
+                        @Override
+                        public void evaluate() throws Throwable {
+                            base.evaluate();
+                        }
+                    };
+                }
+            };
         }
     }
 
-    @After
-    public void ignoreFurtherRequests() {
-        InstrumentedAutoFillService.setIgnoreUnexpectedRequests(true);
+    @RunWith(AndroidJUnit4.class)
+    private abstract static class BaseTestCase {
+
+        private static final String TAG = "AutoFillServiceTestCase";
+
+        protected static final Replier sReplier = InstrumentedAutoFillService.getReplier();
+
+        protected static final Context sContext = InstrumentationRegistry.getTargetContext();
+
+        // Hack because JUnit requires that @ClassRule instance belong to a public class.
+        protected static final SettingsStateKeeperRule sTheRealServiceSettingsKeeper =
+                new SettingsStateKeeperRule(sContext, Settings.Secure.AUTOFILL_SERVICE) {
+            @Override
+            protected void preEvaluate(Description description) {
+                JUnitHelper.setCurrentTestClass(description.getClassName());
+            }
+
+            @Override
+            protected void postEvaluate(Description description) {
+                JUnitHelper.setCurrentTestClass(null);
+            }
+        };
+
+        private final TestWatcher mTestWatcher = new AutofillTestWatcher();
+
+        private final RetryRule mRetryRule = new RetryRule(1);
+
+        private final AutofillLoggingTestRule mLoggingRule = new AutofillLoggingTestRule(TAG);
+
+        private final RequiredFeatureRule mRequiredFeatureRule =
+                new RequiredFeatureRule(PackageManager.FEATURE_AUTOFILL);
+
+        protected final SafeCleanerRule mSafeCleanerRule = new SafeCleanerRule()
+                .setDumper(mLoggingRule)
+                .run(() -> sReplier.assertNoUnhandledFillRequests())
+                .run(() -> sReplier.assertNoUnhandledSaveRequests())
+                .add(() -> { return sReplier.getExceptions(); });
+
+        @Rule
+        public final RuleChain mLookAllTheseRules = RuleChain
+                //
+                // mRequiredFeatureRule should be first so the test can be skipped right away
+                .outerRule(mRequiredFeatureRule)
+                //
+                // mTestWatcher should always be one the first rules, as it defines the name of the
+                // test being ran and finishes dangling activities at the end
+                .around(mTestWatcher)
+                //
+                // mLoggingRule wraps the test but doesn't interfere with it
+                .around(mLoggingRule)
+                //
+                // mSafeCleanerRule will catch errors
+                .around(mSafeCleanerRule)
+                //
+                // mRetryRule should be closest to the main test as possible
+                .around(mRetryRule)
+                //
+                //
+                // Finally, let subclasses add their own rules (like ActivityTestRule)
+                .around(getMainTestRule());
+
+
+        protected final Context mContext = sContext;
+        protected final String mPackageName;
+        protected final UiBot mUiBot;
+
+        private BaseTestCase(@NonNull UiBot uiBot) {
+            mPackageName = mContext.getPackageName();
+            mUiBot = uiBot;
+            mUiBot.reset();
+        }
+
+        /**
+         * Gets the test-specific {@link Rule @Rule}.
+         *
+         * <p>Sub-class <b>MUST</b> override this method instead of annotation their own rules,
+         * so the order is preserved.
+         *
+         */
+        @NonNull
+        protected abstract TestRule getMainTestRule();
+
+        @Before
+        public void prepareDevice() throws Exception {
+            Log.v(TAG, "@Before: prepareDevice()");
+
+            // Unlock screen.
+            runShellCommand("input keyevent KEYCODE_WAKEUP");
+
+            // Dismiss keyguard, in case it's set as "Swipe to unlock".
+            runShellCommand("wm dismiss-keyguard");
+
+            // Collapse notifications.
+            runShellCommand("cmd statusbar collapse");
+
+            // Set orientation as portrait, otherwise some tests might fail due to elements not
+            // fitting in, IME orientation, etc...
+            mUiBot.setScreenOrientation(UiBot.PORTRAIT);
+
+            // Wait until device is idle to avoid flakiness
+            mUiBot.waitForIdle();
+        }
+
+        @Before
+        public void preTestCleanup() {
+            Log.v(TAG, "@Before: preTestCleanup()");
+
+            prepareServicePreTest();
+
+            InstrumentedAutoFillService.resetStaticState();
+            AuthenticationActivity.resetStaticState();
+            sReplier.reset();
+        }
+
+        /**
+         * Prepares the service before each test - by default, disables it
+         */
+        protected void prepareServicePreTest() {
+            Log.v(TAG, "prepareServicePreTest(): calling disableService()");
+            disableService();
+        }
+
+        /**
+         * Enables the {@link InstrumentedAutoFillService} for autofill for the current user.
+         */
+        protected void enableService() {
+            Helper.enableAutofillService(getContext(), SERVICE_NAME);
+        }
+
+        /**
+         * Disables the {@link InstrumentedAutoFillService} for autofill for the current user.
+         */
+        protected void disableService() {
+            Helper.disableAutofillService(getContext());
+        }
+
+        /**
+         * Asserts that the {@link InstrumentedAutoFillService} is enabled for the default user.
+         */
+        protected void assertServiceEnabled() {
+            Helper.assertAutofillServiceStatus(SERVICE_NAME, true);
+        }
+
+        /**
+         * Asserts that the {@link InstrumentedAutoFillService} is disabled for the default user.
+         */
+        protected void assertServiceDisabled() {
+            Helper.assertAutofillServiceStatus(SERVICE_NAME, false);
+        }
+
+        protected RemoteViews createPresentation(String message) {
+            final RemoteViews presentation = new RemoteViews(getContext()
+                    .getPackageName(), R.layout.list_item);
+            presentation.setTextViewText(R.id.text1, message);
+            return presentation;
+        }
     }
 
-    /**
-     * Enables the {@link InstrumentedAutoFillService} for autofill for the current user.
-     */
-    protected void enableService() {
-        Helper.enableAutofillService(getContext(), SERVICE_NAME);
-        InstrumentedAutoFillService.setIgnoreUnexpectedRequests(false);
-    }
+    protected static final UiBot sDefaultUiBot = new UiBot();
 
-    /**
-     * Disables the {@link InstrumentedAutoFillService} for autofill for the current user.
-     */
-    protected void disableService() {
-        if (!hasAutofillFeature()) return;
-
-        Helper.disableAutofillService(getContext(), SERVICE_NAME);
-        InstrumentedAutoFillService.setIgnoreUnexpectedRequests(true);
-    }
-
-    /**
-     * Asserts that the {@link InstrumentedAutoFillService} is enabled for the default user.
-     */
-    protected void assertServiceEnabled() {
-        Helper.assertAutofillServiceStatus(SERVICE_NAME, true);
-    }
-
-    /**
-     * Asserts that the {@link InstrumentedAutoFillService} is disabled for the default user.
-     */
-    protected void assertServiceDisabled() {
-        Helper.assertAutofillServiceStatus(SERVICE_NAME, false);
-    }
-
-    protected RemoteViews createPresentation(String message) {
-        final RemoteViews presentation = new RemoteViews(getContext()
-                .getPackageName(), R.layout.list_item);
-        presentation.setTextViewText(R.id.text1, message);
-        return presentation;
+    private AutoFillServiceTestCase() {
+        throw new UnsupportedOperationException("Contain static stuff only");
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFinishSessionTest.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFinishSessionTest.java
index 5fae8bf..5f2d476 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFinishSessionTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFinishSessionTest.java
@@ -27,13 +27,12 @@
 import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
 import android.content.Intent;
 import android.service.autofill.SaveInfo;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import android.view.ViewGroup;
 import android.widget.EditText;
 
-import org.junit.Before;
-import org.junit.Rule;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import org.junit.Test;
 
 import java.util.concurrent.atomic.AtomicReference;
@@ -41,22 +40,30 @@
 /**
  * Tests that the session finishes when the views and fragments go away
  */
-public class AutoFinishSessionTest extends AutoFillServiceTestCase {
+public class AutoFinishSessionTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<FragmentContainerActivity> {
 
     private static final String ID_BUTTON = "button";
 
-    @Rule
-    public final AutofillActivityTestRule<FragmentContainerActivity> mActivityRule =
-            new AutofillActivityTestRule<>(FragmentContainerActivity.class);
     private FragmentContainerActivity mActivity;
     private EditText mEditText1;
     private EditText mEditText2;
     private Fragment mFragment;
     private ViewGroup mParent;
 
-    @Before
-    public void initViews() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<FragmentContainerActivity> getActivityRule() {
+        return new AutofillActivityTestRule<FragmentContainerActivity>(
+                FragmentContainerActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                initViews(getActivity());
+            }
+        };
+    }
+
+    private void initViews(FragmentContainerActivity activitiy) {
+        mActivity = activitiy;
         mEditText1 = mActivity.findViewById(R.id.editText1);
         mEditText2 = mActivity.findViewById(R.id.editText2);
         mFragment = mActivity.getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
@@ -75,16 +82,15 @@
                 .setSaveInfoFlags(SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE)
                 .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, viewsToSave).build());
 
-        // Trigger autofill
-        mActivity.syncRunOnUiThread(() -> {
-            mEditText2.requestFocus();
-            mEditText1.requestFocus();
-        });
-
+        // Trigger autofill on editText2
+        mActivity.syncRunOnUiThread(() -> mEditText2.requestFocus());
         sReplier.getNextFillRequest();
-
         mUiBot.assertNoDatasetsEver();
 
+        // Now it's safe to focus on editText1 without triggering a new partition due to race
+        // conditions
+        mActivity.syncRunOnUiThread(() -> mEditText1.requestFocus());
+
         // remove first set of views
         mActivity.syncRunOnUiThread(() -> {
             mEditText1.setText("editText1-filled");
@@ -205,16 +211,16 @@
                 .setSaveInfoFlags(SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE)
                 .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, "editText1").build());
 
-        // Trigger autofill
-        mActivity.syncRunOnUiThread(() -> {
-            mEditText2.requestFocus();
-            mEditText1.requestFocus();
-        });
 
+        // Trigger autofill on editText2
+        mActivity.syncRunOnUiThread(() -> mEditText2.requestFocus());
         sReplier.getNextFillRequest();
-
         mUiBot.assertNoDatasetsEver();
 
+        // Now it's safe to focus on editText1 without triggering a new partition due to race
+        // conditions
+        mActivity.syncRunOnUiThread(() -> mEditText1.requestFocus());
+
         mActivity.syncRunOnUiThread(() -> {
             mEditText1.setText("editText1-filled");
             mEditText2.setText("editText2-filled");
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutofillActivityTestRule.java b/tests/autofillservice/src/android/autofillservice/cts/AutofillActivityTestRule.java
index 7e365932..1985ba6 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutofillActivityTestRule.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutofillActivityTestRule.java
@@ -15,13 +15,13 @@
  */
 package android.autofillservice.cts;
 
-import android.app.Activity;
 import android.support.test.rule.ActivityTestRule;
 
 /**
  * Custom {@link ActivityTestRule}.
  */
-public class AutofillActivityTestRule<T extends Activity> extends ActivityTestRule<T> {
+public class AutofillActivityTestRule<T extends AbstractAutoFillActivity>
+        extends ActivityTestRule<T> {
 
     public AutofillActivityTestRule(Class<T> activityClass) {
         super(activityClass);
@@ -30,4 +30,12 @@
     public AutofillActivityTestRule(Class<T> activityClass, boolean launchActivity) {
         super(activityClass, false, launchActivity);
     }
+
+    @Override
+    protected void afterActivityFinished() {
+        // AutofillTestWatcher does not need to watch for this activity as the ActivityTestRule
+        // will take care of finishing it...
+        AutofillTestWatcher.unregisterActivity("AutofillActivityTestRule.afterActivityFinished()",
+                getActivity());
+    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java b/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java
index 7c5f02e..aacd200 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java
@@ -18,9 +18,10 @@
 
 import static android.autofillservice.cts.common.ShellHelper.runShellCommand;
 
-import androidx.annotation.NonNull;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+
 import org.junit.AssumptionViolatedException;
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
@@ -52,7 +53,6 @@
             @Override
             public void evaluate() throws Throwable {
                 final String testName = description.getDisplayName();
-                Log.v(TAG, "@Before " + testName);
                 final String levelBefore = runShellCommand("cmd autofill get log_level");
                 if (!levelBefore.equals("verbose")) {
                     runShellCommand("cmd autofill set log_level verbose");
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutofillTestWatcher.java b/tests/autofillservice/src/android/autofillservice/cts/AutofillTestWatcher.java
new file mode 100644
index 0000000..2cca253
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutofillTestWatcher.java
@@ -0,0 +1,154 @@
+/*
+ * 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.autofillservice.cts;
+
+import android.util.ArraySet;
+import android.util.Log;
+
+import androidx.annotation.GuardedBy;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+import java.util.Set;
+
+/**
+ * Custom {@link TestWatcher} that's the outer rule of all {@link AutoFillServiceTestCase} tests.
+ *
+ * <p>This class is not thread safe, but should be fine...
+ */
+public final class AutofillTestWatcher extends TestWatcher {
+
+    private static final String TAG = "AutofillTestWatcher";
+
+    @GuardedBy("sUnfinishedBusiness")
+    private static final Set<AbstractAutoFillActivity> sUnfinishedBusiness = new ArraySet<>();
+
+    @GuardedBy("sAllActivities")
+    private static final Set<AbstractAutoFillActivity> sAllActivities = new ArraySet<>();
+
+    @Override
+    protected void starting(Description description) {
+        resetStaticState();
+        final String testName = description.getDisplayName();
+        Log.i(TAG, "Starting " + testName);
+        JUnitHelper.setCurrentTestName(testName);
+    }
+
+    @Override
+    protected void finished(Description description) {
+        final String testName = description.getDisplayName();
+        try {
+            finishActivities();
+            waitUntilAllDestroyed();
+        } finally {
+            resetStaticState();
+        }
+        Log.i(TAG, "Finished " + testName);
+        JUnitHelper.setCurrentTestName(null);
+    }
+
+    private void resetStaticState() {
+        synchronized (sUnfinishedBusiness) {
+            sUnfinishedBusiness.clear();
+        }
+        synchronized (sAllActivities) {
+            sAllActivities.clear();
+        }
+    }
+
+    /**
+     * Registers an activity so it's automatically finished (if necessary) after the test.
+     */
+    public static void registerActivity(@NonNull String where,
+            @NonNull AbstractAutoFillActivity activity) {
+        synchronized (sUnfinishedBusiness) {
+            if (sUnfinishedBusiness.contains(activity)) {
+                throw new IllegalStateException("Already registered " + activity);
+            }
+            Log.v(TAG, "registering activity on " + where + ": " + activity);
+            sUnfinishedBusiness.add(activity);
+            sAllActivities.add(activity);
+        }
+        synchronized (sAllActivities) {
+            sAllActivities.add(activity);
+
+        }
+    }
+
+    /**
+     * Unregisters an activity so it's not automatically finished after the test.
+     */
+    public static void unregisterActivity(@NonNull String where,
+            @NonNull AbstractAutoFillActivity activity) {
+        synchronized (sUnfinishedBusiness) {
+            final boolean unregistered = sUnfinishedBusiness.remove(activity);
+            if (unregistered) {
+                Log.d(TAG, "unregistered activity on " + where + ": " + activity);
+            } else {
+                Log.v(TAG, "ignoring already unregistered activity on " + where + ": " + activity);
+            }
+        }
+    }
+
+    /**
+     * Gets the instance of a previously registered activity.
+     */
+    @Nullable
+    public static <A extends AbstractAutoFillActivity> A getActivity(@NonNull Class<A> clazz) {
+        @SuppressWarnings("unchecked")
+        final A activity = (A) sAllActivities.stream().filter(a -> a.getClass().equals(clazz))
+                .findFirst()
+                .get();
+        return activity;
+    }
+
+    private void finishActivities() {
+        synchronized (sUnfinishedBusiness) {
+            if (sUnfinishedBusiness.isEmpty()) {
+                return;
+            }
+            Log.d(TAG, "Manually finishing " + sUnfinishedBusiness.size() + " activities");
+            for (AbstractAutoFillActivity activity : sUnfinishedBusiness) {
+                if (activity.isFinishing()) {
+                    Log.v(TAG, "Ignoring activity that isFinishing(): " + activity);
+                } else {
+                    Log.d(TAG, "Finishing activity: " + activity);
+                    activity.finishOnly();
+                }
+            }
+        }
+    }
+
+    private void waitUntilAllDestroyed() {
+        synchronized (sAllActivities) {
+            if (sAllActivities.isEmpty()) return;
+
+            Log.d(TAG, "Waiting until " + sAllActivities.size() + " activities are destroyed");
+            for (AbstractAutoFillActivity activity : sAllActivities) {
+                Log.d(TAG, "Waiting for " + activity);
+                try {
+                    activity.waintUntilDestroyed(Timeouts.ACTIVITY_RESURRECTION);
+                } catch (InterruptedException e) {
+                    Log.e(TAG, "interrupted waiting for " + activity + " to be destroyed");
+                    Thread.currentThread().interrupt();
+                }
+            }
+        }
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java b/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java
index a63afe4..3a0c294 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java
@@ -35,8 +35,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 /*
@@ -54,10 +52,8 @@
  *  CheckoutActivityTest.
  */
 @AppModeFull // Unit test
-public class AutofillValueTest extends AutoFillServiceTestCase {
-    @Rule
-    public final AutofillActivityTestRule<AllAutofillableViewsActivity> mActivityRule =
-            new AutofillActivityTestRule<>(AllAutofillableViewsActivity.class);
+public class AutofillValueTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<AllAutofillableViewsActivity> {
 
     private AllAutofillableViewsActivity mActivity;
     private EditText mEditText;
@@ -69,9 +65,8 @@
     private DatePicker mDatePicker;
     private TimePicker mTimePicker;
 
-    @Before
-    public void setFields() {
-        mActivity = mActivityRule.getActivity();
+    private void setFields(AllAutofillableViewsActivity activity) {
+        mActivity = activity;
 
         mEditText = (EditText) mActivity.findViewById(R.id.editText);
         mCompoundButton = (CompoundButton) mActivity.findViewById(R.id.compoundButton);
@@ -83,6 +78,17 @@
         mTimePicker = (TimePicker) mActivity.findViewById(R.id.timePicker);
     }
 
+    @Override
+    protected AutofillActivityTestRule<AllAutofillableViewsActivity> getActivityRule() {
+        return new AutofillActivityTestRule<AllAutofillableViewsActivity>(
+                AllAutofillableViewsActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                setFields(getActivity());
+            }
+        };
+    }
+
     @Test
     public void createTextValue() throws Exception {
         assertThat(AutofillValue.forText(null)).isNull();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java b/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
index 33aa025..90d74561 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
@@ -175,7 +175,9 @@
             if (mSaveDescription != null) {
                 saveInfo.setDescription(mSaveDescription);
             }
-            saveInfo.setNegativeAction(mNegativeActionStyle, mNegativeActionListener);
+            if (mNegativeActionListener != null) {
+                saveInfo.setNegativeAction(mNegativeActionStyle, mNegativeActionListener);
+            }
 
             if (mCustomDescription != null) {
                 saveInfo.setCustomDescription(mCustomDescription);
@@ -189,6 +191,8 @@
                 saveInfo.setTriggerId(mSaveTriggerId);
             }
             builder.setSaveInfo(saveInfo.build());
+        } else if (mSaveInfoFlags != 0) {
+            builder.setSaveInfo(new SaveInfo.Builder(mSaveType).setFlags(mSaveInfoFlags).build());
         }
         if (mIgnoredIds != null) {
             builder.setIgnoredIds(getAutofillIds(nodeResolver, mIgnoredIds));
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivity.java b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivity.java
index 141b8d0..f5e7f87 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivity.java
@@ -21,7 +21,6 @@
 
 import android.content.Intent;
 import android.os.Bundle;
-import android.util.Log;
 import android.widget.ArrayAdapter;
 import android.widget.Button;
 import android.widget.CheckBox;
@@ -46,7 +45,6 @@
  * </ul>
  */
 public class CheckoutActivity extends AbstractAutoFillActivity {
-    private static final String TAG = "CheckoutActivity";
     private static final long BUY_TIMEOUT_MS = 1000;
 
     static final String ID_CC_NUMBER = "cc_number";
@@ -76,12 +74,6 @@
     private FillExpectation mExpectation;
     private CountDownLatch mBuyLatch;
 
-    private static CheckoutActivity sInstance;
-
-    public CheckoutActivity() {
-        sInstance = this;
-    }
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -106,21 +98,6 @@
         mClearButton.setOnClickListener((v) -> resetFields());
     }
 
-    @Override
-    public void finish() {
-        super.finish();
-
-        sInstance = null;
-    }
-
-    static void finishIt(UiBot uiBot) {
-        if (sInstance != null) {
-            Log.d(TAG, "So long and thanks for all the fish!");
-            sInstance.finish();
-            uiBot.assertGoneByRelativeId(ID_CC_NUMBER, Timeouts.ACTIVITY_RESURRECTION);
-        }
-    }
-
     protected int getContentView() {
         return R.layout.checkout_activity;
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
index 6739a73..ac181ec 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
@@ -52,8 +52,6 @@
 import android.widget.RemoteViews;
 import android.widget.Spinner;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.Arrays;
@@ -62,17 +60,19 @@
 /**
  * Test case for an activity containing non-TextField views.
  */
-public class CheckoutActivityTest extends AutoFillServiceTestCase {
-
-    @Rule
-    public final AutofillActivityTestRule<CheckoutActivity> mActivityRule =
-        new AutofillActivityTestRule<CheckoutActivity>(CheckoutActivity.class);
+public class CheckoutActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<CheckoutActivity> {
 
     private CheckoutActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<CheckoutActivity> getActivityRule() {
+        return new AutofillActivityTestRule<CheckoutActivity>(CheckoutActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     @Test
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionDateTest.java b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionDateTest.java
index 7858278..49e772d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionDateTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionDateTest.java
@@ -33,24 +33,25 @@
 import android.view.autofill.AutofillId;
 import android.widget.RemoteViews;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.Calendar;
 
 @AppModeFull // Service-specific test
-public class CustomDescriptionDateTest extends AutoFillServiceTestCase {
-
-    @Rule
-    public final AutofillActivityTestRule<DatePickerSpinnerActivity> mActivityRule = new
-            AutofillActivityTestRule<DatePickerSpinnerActivity>(DatePickerSpinnerActivity.class);
+public class CustomDescriptionDateTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<DatePickerSpinnerActivity> {
 
     private DatePickerSpinnerActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<DatePickerSpinnerActivity> getActivityRule() {
+        return new AutofillActivityTestRule<DatePickerSpinnerActivity>(
+                DatePickerSpinnerActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     @Test
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionTest.java b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionTest.java
index 08050e0..9827fc4 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionTest.java
@@ -40,25 +40,13 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.function.BiFunction;
 import java.util.regex.Pattern;
 
 @AppModeFull // Service-specific test
-public class CustomDescriptionTest extends AutoFillServiceTestCase {
-    @Rule
-    public final AutofillActivityTestRule<LoginActivity> mActivityRule =
-            new AutofillActivityTestRule<>(LoginActivity.class);
-
-    private LoginActivity mActivity;
-
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
-    }
+public class CustomDescriptionTest extends AbstractLoginActivityTestCase {
 
     /**
      * Base test
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java
index 642e372..b87a04a 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java
@@ -25,9 +25,10 @@
 import android.service.autofill.CustomDescription;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiObject2;
-import android.util.Log;
 import android.widget.RemoteViews;
 
+import androidx.annotation.NonNull;
+
 import static org.junit.Assume.assumeTrue;
 
 import org.junit.Test;
@@ -46,11 +47,32 @@
  * <p>The overall behavior should be the same in both cases, although the implementation of the
  * tests per se will be sligthly different.
  */
-abstract class CustomDescriptionWithLinkTestCase extends AutoFillServiceTestCase {
+abstract class CustomDescriptionWithLinkTestCase<A extends AbstractAutoFillActivity> extends
+        AutoFillServiceTestCase.AutoActivityLaunch<A> {
 
-    private static final String TAG = "CustomDescriptionWithLinkTestCase";
     private static final String ID_LINK = "link";
 
+    private final Class<A> mActivityClass;
+
+    protected A mActivity;
+
+    protected CustomDescriptionWithLinkTestCase(@NonNull Class<A> activityClass) {
+        mActivityClass = activityClass;
+    }
+
+    protected void startActivity() {
+        startActivity(false);
+    }
+
+    protected void startActivity(boolean remainOnRecents) {
+        final Intent intent = new Intent(mContext, mActivityClass);
+        if (remainOnRecents) {
+            intent.setFlags(
+                    Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS | Intent.FLAG_ACTIVITY_NEW_TASK);
+        }
+        mActivity = launchActivity(intent);
+    }
+
     /**
      * Tests scenarios when user taps a link in the custom description and then taps back:
      * the Save UI should have been restored.
@@ -69,13 +91,7 @@
     public final void testTapLink_changeOrientationThenTapBack() throws Exception {
         assumeTrue("Rotation is supported", Helper.isRotationSupported(mContext));
 
-        final int width = mUiBot.getDevice().getDisplayWidth();
-        final int heigth = mUiBot.getDevice().getDisplayHeight();
-        final int min = Math.min(width, heigth);
-
-        assumeTrue("Screen size is too small (" + width + "x" + heigth + ")", min >= 500);
-        Log.d(TAG, "testTapLink_changeOrientationThenTapBack(): screen size is "
-                + width + "x" + heigth);
+        mUiBot.setScreenResolution();
 
         mUiBot.setScreenOrientation(UiBot.PORTRAIT);
         try {
@@ -84,12 +100,13 @@
             saveUiRestoredAfterTappingLinkTest(
                     PostSaveLinkTappedAction.ROTATE_THEN_TAP_BACK_BUTTON);
         } finally {
-            mUiBot.setScreenOrientation(UiBot.PORTRAIT);
             try {
+                mUiBot.setScreenOrientation(UiBot.PORTRAIT);
                 cleanUpAfterScreenOrientationIsBackToPortrait();
+            } catch (Exception e) {
+                mSafeCleanerRule.add(e);
             } finally {
-                runShellCommand("wm density reset");
-                runShellCommand("wm size reset");
+                mUiBot.resetScreenResolution();
             }
         }
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DatePickerCalendarActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/DatePickerCalendarActivityTest.java
index f0c3e04..35f3b73 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DatePickerCalendarActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DatePickerCalendarActivityTest.java
@@ -13,21 +13,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.autofillservice.cts;
 
 import android.platform.test.annotations.AppModeFull;
 
-import org.junit.Rule;
-
 @AppModeFull // Unit test
 public class DatePickerCalendarActivityTest extends DatePickerTestCase<DatePickerCalendarActivity> {
 
-    @Rule
-    public final AutofillActivityTestRule<DatePickerCalendarActivity> mActivityRule =
-        new AutofillActivityTestRule<DatePickerCalendarActivity>(DatePickerCalendarActivity.class);
-
     @Override
-    protected DatePickerCalendarActivity getDatePickerActivity() {
-        return mActivityRule.getActivity();
+    protected AutofillActivityTestRule<DatePickerCalendarActivity> getActivityRule() {
+        return new AutofillActivityTestRule<DatePickerCalendarActivity>(
+                DatePickerCalendarActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DatePickerSpinnerActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/DatePickerSpinnerActivityTest.java
index 0fb026a..291d5aa 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DatePickerSpinnerActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DatePickerSpinnerActivityTest.java
@@ -13,21 +13,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.autofillservice.cts;
 
 import android.platform.test.annotations.AppModeFull;
 
-import org.junit.Rule;
-
 @AppModeFull // Unit test
 public class DatePickerSpinnerActivityTest extends DatePickerTestCase<DatePickerSpinnerActivity> {
 
-    @Rule
-    public final AutofillActivityTestRule<DatePickerSpinnerActivity> mActivityRule =
-        new AutofillActivityTestRule<DatePickerSpinnerActivity>(DatePickerSpinnerActivity.class);
-
     @Override
-    protected DatePickerSpinnerActivity getDatePickerActivity() {
-        return mActivityRule.getActivity();
+    protected AutofillActivityTestRule<DatePickerSpinnerActivity> getActivityRule() {
+        return new AutofillActivityTestRule<DatePickerSpinnerActivity>(
+                DatePickerSpinnerActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java
index c9afacc..07df4b3 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java
@@ -36,14 +36,14 @@
 /**
  * Base class for {@link AbstractDatePickerActivity} tests.
  */
-abstract class DatePickerTestCase<T extends AbstractDatePickerActivity>
-        extends AutoFillServiceTestCase {
+abstract class DatePickerTestCase<A extends AbstractDatePickerActivity>
+        extends AutoFillServiceTestCase.AutoActivityLaunch<A> {
 
-    protected abstract T getDatePickerActivity();
+    protected A mActivity;
 
     @Test
     public void testAutoFillAndSave() throws Exception {
-        final T activity = getDatePickerActivity();
+        assertWithMessage("subclass did not set mActivity").that(mActivity).isNotNull();
 
         // Set service.
         enableService();
@@ -62,10 +62,10 @@
                     .build())
                 .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_OUTPUT, ID_DATE_PICKER)
                 .build());
-        activity.expectAutoFill("2012/11/20", 2012, Calendar.DECEMBER, 20);
+        mActivity.expectAutoFill("2012/11/20", 2012, Calendar.DECEMBER, 20);
 
         // Trigger auto-fill.
-        activity.onOutput((v) -> v.requestFocus());
+        mActivity.onOutput((v) -> v.requestFocus());
         final FillRequest fillRequest = sReplier.getNextFillRequest();
 
         // Assert properties of DatePicker field.
@@ -76,11 +76,11 @@
         mUiBot.selectDataset("The end of the world");
 
         // Check the results.
-        activity.assertAutoFilled();
+        mActivity.assertAutoFilled();
 
         // Trigger save.
-        activity.setDate(2010, Calendar.DECEMBER, 12);
-        activity.tapOk();
+        mActivity.setDate(2010, Calendar.DECEMBER, 12);
+        mActivity.tapOk();
 
         mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DialogLauncherActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/DialogLauncherActivityTest.java
index 1e9fcf2..175d0cb 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DialogLauncherActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DialogLauncherActivityTest.java
@@ -24,21 +24,21 @@
 import android.support.test.uiautomator.UiObject2;
 import android.view.View;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
-public class DialogLauncherActivityTest extends AutoFillServiceTestCase {
-
-    @Rule
-    public final AutofillActivityTestRule<DialogLauncherActivity> mActivityRule =
-            new AutofillActivityTestRule<DialogLauncherActivity>(DialogLauncherActivity.class);
+public class DialogLauncherActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<DialogLauncherActivity> {
 
     private DialogLauncherActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<DialogLauncherActivity> getActivityRule() {
+        return new AutofillActivityTestRule<DialogLauncherActivity>(DialogLauncherActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     @Test
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DisableAutofillTest.java b/tests/autofillservice/src/android/autofillservice/cts/DisableAutofillTest.java
index 2f4de15..d4d1ff4 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DisableAutofillTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DisableAutofillTest.java
@@ -16,6 +16,9 @@
 
 package android.autofillservice.cts;
 
+import static android.autofillservice.cts.Timeouts.ACTIVITY_RESURRECTION;
+import static android.autofillservice.cts.Timeouts.CALLBACK_NOT_CALLED_TIMEOUT_MS;
+
 import android.autofillservice.cts.CannedFillResponse.CannedDataset;
 import android.content.Intent;
 import android.os.SystemClock;
@@ -28,7 +31,7 @@
 /**
  * Tests for the {@link android.service.autofill.FillResponse.Builder#disableAutofill(long)} API.
  */
-public class DisableAutofillTest extends AutoFillServiceTestCase {
+public class DisableAutofillTest extends AutoFillServiceTestCase.ManualActivityLaunch {
 
     private static final String TAG = "DisableAutofillTest";
 
@@ -81,7 +84,10 @@
         ASSERT_ENABLED_AND_AUTOFILL
     }
 
-    private void launchSimpleSaveActivity(PostLaunchAction action) throws Exception {
+    /**
+     * Launches and finishes {@link SimpleSaveActivity}, returning how long it took.
+     */
+    private long launchSimpleSaveActivity(PostLaunchAction action) throws Exception {
         Log.v(TAG, "launchPreSimpleSaveActivity(): " + action);
         sReplier.assertNoUnhandledFillRequests();
 
@@ -96,6 +102,7 @@
 
         }
 
+        final long before = SystemClock.elapsedRealtime();
         final SimpleSaveActivity activity = startSimpleSaveActivity();
         final MyAutofillCallback callback = activity.registerCallback();
 
@@ -128,9 +135,13 @@
         } finally {
             activity.finish();
         }
+        return SystemClock.elapsedRealtime() - before;
     }
 
-    private void launchPreSimpleSaveActivity(PostLaunchAction action) throws Exception {
+    /**
+     * Launches and finishes {@link PreSimpleSaveActivity}, returning how long it took.
+     */
+    private long launchPreSimpleSaveActivity(PostLaunchAction action) throws Exception {
         Log.v(TAG, "launchPreSimpleSaveActivity(): " + action);
         sReplier.assertNoUnhandledFillRequests();
 
@@ -143,6 +154,7 @@
                     .build());
         }
 
+        final long before = SystemClock.elapsedRealtime();
         final PreSimpleSaveActivity activity = startPreSimpleSaveActivity();
         final MyAutofillCallback callback = activity.registerCallback();
 
@@ -170,6 +182,7 @@
         } finally {
             activity.finish();
         }
+        return SystemClock.elapsedRealtime() - before;
     }
 
     @Test
@@ -198,19 +211,19 @@
         enableService();
 
         // Need to wait the equivalent of launching 2 activities, plus some extra legging room
-        final long duration = 2 * Timeouts.ACTIVITY_RESURRECTION.ms() + 500;
+        final long duration = 2 * ACTIVITY_RESURRECTION.ms() + 500;
 
         // Set expectations.
         sReplier.addResponse(new CannedFillResponse.Builder().disableAutofill(duration).build());
 
         // Trigger autofill for the first time.
-        launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
+        long passedTime = launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
 
         // Launch activity again.
-        launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
+        passedTime += launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
 
         // Wait for the timeout, then try again, autofilling it this time.
-        SystemClock.sleep(duration + 1);
+        sleep(passedTime, duration);
         launchSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
 
         // Also try it on another activity.
@@ -268,7 +281,7 @@
         enableService();
 
         // Need to wait the equivalent of launching 2 activities, plus some extra legging room
-        final long duration = 2 * Timeouts.ACTIVITY_RESURRECTION.ms() + 500;
+        final long duration = 2 * ACTIVITY_RESURRECTION.ms() + 500;
 
         // Set expectations.
         sReplier.addResponse(new CannedFillResponse.Builder()
@@ -277,16 +290,16 @@
                 .build());
 
         // Trigger autofill for the first time.
-        launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
+        long passedTime = launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
 
         // Launch activity again.
-        launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
+        passedTime += launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
 
         // Make sure other app is working.
-        launchPreSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
+        passedTime += launchPreSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
 
         // Wait for the timeout, then try again, autofilling it this time.
-        SystemClock.sleep(duration + 1);
+        sleep(passedTime, duration);
         launchSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
     }
 
@@ -317,11 +330,26 @@
 
     private void assertAutofillEnabled(AbstractAutoFillActivity activity, boolean expected)
             throws Exception {
-        Timeouts.ACTIVITY_RESURRECTION.run(
+        ACTIVITY_RESURRECTION.run(
                 "assertAutofillEnabled(" + activity.getComponentName().flattenToShortString() + ")",
                 () -> {
                     return activity.getAutofillManager().isEnabled() == expected
                             ? Boolean.TRUE : null;
                 });
     }
+
+    private void sleep(long passedTime, long disableDuration) {
+        final long napTime = disableDuration - passedTime + 500;
+        if (napTime <= 0) {
+            // Throw an exception so ACTIVITY_RESURRECTION is increased
+            throw new RetryableException("took longer than expcted to launch activities: "
+                            + "passedTime=" + passedTime + "ms, disableDuration=" + disableDuration
+                            + ", ACTIVITY_RESURRECTION=" + ACTIVITY_RESURRECTION
+                            + ", CALLBACK_NOT_CALLED_TIMEOUT_MS=" + CALLBACK_NOT_CALLED_TIMEOUT_MS);
+        }
+        Log.v(TAG, "Sleeping for " + napTime + "ms (duration=" + disableDuration + "ms, passedTime="
+                + passedTime + ")");
+
+        SystemClock.sleep(napTime);
+    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java
index 846dcc4..53ff280 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java
@@ -30,29 +30,26 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
-public class DuplicateIdActivityTest extends AutoFillServiceTestCase {
-    private static final String LOG_TAG = DuplicateIdActivityTest.class.getSimpleName();
-    @Rule
-    public final AutofillActivityTestRule<DuplicateIdActivity> mActivityRule = new AutofillActivityTestRule<>(
-            DuplicateIdActivity.class);
+public class DuplicateIdActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<DuplicateIdActivity> {
 
-    private DuplicateIdActivity mActivity;
+    private static final String TAG = "DuplicateIdActivityTest";
+
+    @Override
+    protected AutofillActivityTestRule<DuplicateIdActivity> getActivityRule() {
+        return new AutofillActivityTestRule<DuplicateIdActivity>(DuplicateIdActivity.class);
+    }
 
     @Before
     public void setup() throws Exception {
         Helper.disableAutoRotation(mUiBot);
         mUiBot.setScreenOrientation(0);
-
-        mActivity = mActivityRule.getActivity();
     }
 
     @After
     public void teardown() {
-        mActivity.finish();
-
         Helper.allowAutoRotation();
     }
 
@@ -90,7 +87,7 @@
         runShellCommand("input keyevent KEYCODE_TAB");
 
         final InstrumentedAutoFillService.FillRequest request1 = sReplier.getNextFillRequest();
-        Log.v(LOG_TAG, "request1: " + request1);
+        Log.v(TAG, "request1: " + request1);
 
         final AssistStructure.ViewNode[] views1 = findViews(request1);
         final AssistStructure.ViewNode view1 = views1[0];
@@ -98,8 +95,8 @@
         final AutofillId id1 = view1.getAutofillId();
         final AutofillId id2 = view2.getAutofillId();
 
-        Log.i(LOG_TAG, "view1=" + id1);
-        Log.i(LOG_TAG, "view2=" + id2);
+        Log.v(TAG, "view1=" + id1);
+        Log.v(TAG, "view2=" + id2);
 
         // Both checkboxes use the same id
         assertThat(view1.getId()).isEqualTo(view2.getId());
@@ -115,7 +112,7 @@
         mUiBot.assertShownByRelativeId(DUPLICATE_ID);
         // Ignore 2nd request.
         final InstrumentedAutoFillService.FillRequest request2 = sReplier.getNextFillRequest();
-        Log.v(LOG_TAG, "request2: " + request2);
+        Log.v(TAG, "request2: " + request2);
 
         // Select other field to trigger new partition (because server didn't return 2nd field
         // on 1st response)
@@ -123,15 +120,15 @@
         runShellCommand("input keyevent KEYCODE_TAB");
 
         final InstrumentedAutoFillService.FillRequest request3 = sReplier.getNextFillRequest();
-        Log.v(LOG_TAG, "request3: " + request3);
+        Log.v(TAG, "request3: " + request3);
         final AssistStructure.ViewNode[] views2 = findViews(request3);
         final AssistStructure.ViewNode recreatedView1 = views2[0];
         final AssistStructure.ViewNode recreatedView2 = views2[1];
         final AutofillId recreatedId1 = recreatedView1.getAutofillId();
         final AutofillId recreatedId2 = recreatedView2.getAutofillId();
 
-        Log.i(LOG_TAG, "restored view1=" + recreatedId1);
-        Log.i(LOG_TAG, "restored view2=" + recreatedId2);
+        Log.v(TAG, "restored view1=" + recreatedId1);
+        Log.v(TAG, "restored view2=" + recreatedId2);
 
         // For the restoring logic the two views are the same. Hence it might happen that the first
         // view is restored with the autofill id of the second view or the other way round.
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FatActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/FatActivityTest.java
index 28ecdf7..456f5cf 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/FatActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/FatActivityTest.java
@@ -48,25 +48,24 @@
 import android.app.assist.AssistStructure.ViewNode;
 import android.autofillservice.cts.InstrumentedAutoFillService.FillRequest;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 /**
  * Test case for an activity containing useless auto-fill data that should be optimized out.
  */
-public class FatActivityTest extends AutoFillServiceTestCase {
+public class FatActivityTest extends AutoFillServiceTestCase.AutoActivityLaunch<FatActivity> {
 
-    @Rule
-    public final AutofillActivityTestRule<FatActivity> mActivityRule =
-        new AutofillActivityTestRule<FatActivity>(FatActivity.class);
-
-    private FatActivity mFatActivity;
+    private FatActivity mActivity;
     private ViewNode mRoot;
 
-    @Before
-    public void setActivity() {
-        mFatActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<FatActivity> getActivityRule() {
+        return new AutofillActivityTestRule<FatActivity>(FatActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     @Test
@@ -78,7 +77,7 @@
         sReplier.addResponse(NO_RESPONSE);
 
         // Trigger auto-fill.
-        mFatActivity.onInput((v) -> v.requestFocus());
+        mActivity.onInput((v) -> v.requestFocus());
         final FillRequest fillRequest = sReplier.getNextFillRequest();
         mUiBot.assertNoDatasetsEver();
 
@@ -154,7 +153,7 @@
         sReplier.addResponse(NO_RESPONSE);
 
         // Trigger autofill.
-        mFatActivity.onInput((v) -> mFatActivity.getAutofillManager().requestAutofill(v));
+        mActivity.onInput((v) -> mActivity.getAutofillManager().requestAutofill(v));
         final FillRequest fillRequest = sReplier.getNextFillRequest();
         mUiBot.assertNoDatasetsEver();
 
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java b/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
index 1ba93c4..96e1fa9 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
@@ -28,26 +28,20 @@
 
 import android.autofillservice.cts.Helper.FieldClassificationResult;
 import android.autofillservice.cts.common.SettingsStateChangerRule;
-import android.content.Context;
 import android.platform.test.annotations.AppModeFull;
 import android.service.autofill.FillEventHistory.Event;
 import android.service.autofill.UserData;
-import android.support.test.InstrumentationRegistry;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 import android.widget.EditText;
 
-import org.junit.Before;
 import org.junit.ClassRule;
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.List;
 
 @AppModeFull // Service-specific test
-public class FieldsClassificationTest extends AutoFillServiceTestCase {
-
-    private static final Context sContext = InstrumentationRegistry.getContext();
+public class FieldsClassificationTest extends AbstractGridActivityTestCase {
 
     @ClassRule
     public static final SettingsStateChangerRule sFeatureEnabler =
@@ -74,17 +68,10 @@
     public static final SettingsStateChangerRule sUserDataMaxCategoryChanger =
             new SettingsStateChangerRule(sContext, AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT, "42");
 
-    @Rule
-    public final AutofillActivityTestRule<GridActivity> mActivityRule =
-            new AutofillActivityTestRule<GridActivity>(GridActivity.class);
-
-
-    private GridActivity mActivity;
     private AutofillManager mAfm;
 
-    @Before
-    public void setFixtures() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected void postActivityLaunched() {
         mAfm = mActivity.getAutofillManager();
     }
 
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java b/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java
index 1eab92d..2e1ae0d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java
@@ -53,8 +53,6 @@
 
 import com.google.common.collect.ImmutableMap;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.List;
@@ -66,18 +64,7 @@
  * Test that uses {@link LoginActivity} to test {@link FillEventHistory}.
  */
 @AppModeFull // Service-specific test
-public class FillEventHistoryTest extends AutoFillServiceTestCase {
-
-    @Rule
-    public final AutofillActivityTestRule<LoginActivity> mActivityRule =
-            new AutofillActivityTestRule<LoginActivity>(LoginActivity.class);
-
-    private LoginActivity mActivity;
-
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
-    }
+public class FillEventHistoryTest extends AbstractLoginActivityTestCase {
 
     @Test
     public void testDatasetAuthenticationSelected() throws Exception {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FragmentContainerActivity.java b/tests/autofillservice/src/android/autofillservice/cts/FragmentContainerActivity.java
index d33f142..b95fec6 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/FragmentContainerActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/FragmentContainerActivity.java
@@ -17,6 +17,8 @@
 package android.autofillservice.cts;
 
 import android.os.Bundle;
+import android.widget.FrameLayout;
+
 import androidx.annotation.Nullable;
 
 import java.util.concurrent.CountDownLatch;
@@ -30,6 +32,7 @@
             FragmentContainerActivity.class.getName() + "#FRAGMENT_TAG";
     private CountDownLatch mResumed = new CountDownLatch(1);
     private CountDownLatch mStopped = new CountDownLatch(0);
+    private FrameLayout mRootContainer;
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -37,6 +40,8 @@
 
         setContentView(R.layout.fragment_container);
 
+        mRootContainer = findViewById(R.id.rootContainer);
+
         // have to manually add fragment as we cannot remove it otherwise
         getFragmentManager().beginTransaction().add(R.id.rootContainer,
                 new FragmentWithEditText(), FRAGMENT_TAG).commitNow();
@@ -70,6 +75,17 @@
         mStopped.countDown();
     }
 
+    /**
+     * Sets whether the root container is focusable or not.
+     *
+     * <p>It's initially set as {@code trye} in the XML layout so autofill is not automatically
+     * triggered in the edit text before the service is prepared to handle it.
+     */
+    public void setRootContainerFocusable(boolean focusable) {
+        mRootContainer.setFocusable(focusable);
+        mRootContainer.setFocusableInTouchMode(focusable);
+    }
+
     public boolean waitUntilResumed() throws InterruptedException {
         return mResumed.await(Timeouts.UI_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/GridActivity.java b/tests/autofillservice/src/android/autofillservice/cts/GridActivity.java
index 65deff56..e1379c3 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/GridActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/GridActivity.java
@@ -134,11 +134,12 @@
     }
 
     public String getText(int row, int column) throws InterruptedException {
+        final long timeoutMs = 100;
         final BlockingQueue<String> queue = new LinkedBlockingQueue<>(1);
-        onCell(row, column, (c) -> queue.offer(c.getText().toString()));
-        final String text = queue.poll(100, TimeUnit.MILLISECONDS);
+        onCell(row, column, (c) -> Helper.offer(queue, c.getText().toString(), timeoutMs));
+        final String text = queue.poll(timeoutMs, TimeUnit.MILLISECONDS);
         if (text == null) {
-            throw new RetryableException("text not set in 100ms");
+            throw new RetryableException("text not set in " + timeoutMs + "ms");
         }
         return text;
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
index 4e5418b..8af976e 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
@@ -16,7 +16,6 @@
 
 package android.autofillservice.cts;
 
-import static android.autofillservice.cts.InstrumentedAutoFillService.SERVICE_NAME;
 import static android.autofillservice.cts.UiBot.PORTRAIT;
 import static android.autofillservice.cts.common.ShellHelper.runShellCommand;
 import static android.provider.Settings.Secure.AUTOFILL_SERVICE;
@@ -62,13 +61,14 @@
 import androidx.annotation.Nullable;
 
 import com.android.compatibility.common.util.BitmapUtils;
-import com.android.compatibility.common.util.RequiredFeatureRule;
 
 import java.io.File;
 import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
 import java.util.function.Function;
 
 /**
@@ -90,15 +90,19 @@
     static final String ID_OUTPUT = "output";
     static final String ID_STATIC_TEXT = "static_text";
 
-    public static final String NULL_DATASET_ID = null;
+    static final String NULL_DATASET_ID = null;
+
+    static final char LARGE_STRING_CHAR = '6';
+    // NOTE: cannot be much large as it could ANR and fail the test.
+    static final int LARGE_STRING_SIZE = 100_000;
+    static final String LARGE_STRING = com.android.compatibility.common.util.TextUtils
+            .repeat(LARGE_STRING_CHAR, LARGE_STRING_SIZE);
 
     /**
      * Can be used in cases where the autofill values is required by irrelevant (like adding a
      * value to an authenticated dataset).
      */
-    public static final String UNUSED_AUTOFILL_VALUE = null;
-
-    private static final String CMD_LIST_SESSIONS = "cmd autofill list sessions";
+    static final String UNUSED_AUTOFILL_VALUE = null;
 
     private static final String ACCELLEROMETER_CHANGE =
             "content insert --uri content://settings/system --bind name:s:accelerometer_rotation "
@@ -785,13 +789,6 @@
     }
 
     /**
-     * Checks if device supports the Autofill feature.
-     */
-    public static boolean hasAutofillFeature() {
-        return RequiredFeatureRule.hasFeature(PackageManager.FEATURE_AUTOFILL);
-    }
-
-    /**
      * Checks if autofill window is fullscreen, see com.android.server.autofill.ui.FillUi.
      */
     public static boolean isAutofillWindowFullScreen(Context context) {
@@ -831,43 +828,45 @@
     }
 
     /**
-     * Uses Settings to disable the given autofill service for the default user, and checks the
-     * value was properly check, throwing an exception if it was not.
+     * Uses Settings to disable the given autofill service for the default user, and waits until
+     * the setting is deleted.
      */
-    public static void disableAutofillService(@NonNull Context context,
-            @NonNull String serviceName) {
-        if (!isAutofillServiceEnabled(serviceName)) return;
-
+    public static void disableAutofillService(@NonNull Context context) {
+        final String currentService = SettingsHelper.get(AUTOFILL_SERVICE);
+        if (currentService == null) {
+            Log.v(TAG, "disableAutofillService(): already disabled");
+            return;
+        }
+        Log.v(TAG, "Disabling " + currentService);
         SettingsHelper.syncDelete(context, AUTOFILL_SERVICE);
     }
 
     /**
      * Checks whether the given service is set as the autofill service for the default user.
      */
-    private static boolean isAutofillServiceEnabled(@NonNull String serviceName) {
-        final String actualName = SettingsHelper.get(AUTOFILL_SERVICE);
+    public static boolean isAutofillServiceEnabled(@NonNull String serviceName) {
+        final String actualName = getAutofillServiceName();
         return serviceName.equals(actualName);
     }
 
     /**
+     * Gets then name of the autofill service for the default user.
+     */
+    public static String getAutofillServiceName() {
+        return SettingsHelper.get(AUTOFILL_SERVICE);
+    }
+
+    /**
      * Asserts whether the given service is enabled as the autofill service for the default user.
      */
     public static void assertAutofillServiceStatus(@NonNull String serviceName, boolean enabled) {
         final String actual = SettingsHelper.get(AUTOFILL_SERVICE);
-        final String expected = enabled ? serviceName : "null";
+        final String expected = enabled ? serviceName : null;
         assertWithMessage("Invalid value for secure setting %s", AUTOFILL_SERVICE)
                 .that(actual).isEqualTo(expected);
     }
 
     /**
-     * Asserts that there is a pending session for the given package.
-     */
-    public static void assertHasSessions(String packageName) {
-        final String result = runShellCommand(CMD_LIST_SESSIONS);
-        assertThat(result).contains(packageName);
-    }
-
-    /**
      * Gets the instrumentation context.
      */
     public static Context getContext() {
@@ -875,21 +874,6 @@
     }
 
     /**
-     * Cleans up the autofill state; should be called before pretty much any test.
-     */
-    public static void preTestCleanup() {
-        if (!hasAutofillFeature()) return;
-
-        Log.d(TAG, "preTestCleanup()");
-
-        disableAutofillService(getContext(), SERVICE_NAME);
-        InstrumentedAutoFillService.setIgnoreUnexpectedRequests(true);
-
-        InstrumentedAutoFillService.resetStaticState();
-        AuthenticationActivity.resetStaticState();
-    }
-
-    /**
      * Asserts the node has an {@code HTMLInfo} property, with the given tag.
      */
     public static HtmlInfo assertHasHtmlTag(ViewNode node, String expectedTag) {
@@ -1218,10 +1202,8 @@
             return;
         }
 
-        final File dir = new File(LOCAL_DIRECTORY);
-        dir.mkdirs();
-        if (!dir.exists()) {
-            Log.e(TAG, "Could not create directory " + dir);
+        final File dir = getLocalDirectory();
+        if (dir == null) {
             throw new AssertionError("bitmap comparison failed for " + filename
                     + ", and bitmaps could not be dumped on " + dir);
         }
@@ -1232,21 +1214,94 @@
     }
 
     @Nullable
-    private static File dumpBitmap(@NonNull Bitmap bitmap, @NonNull File dir,
-            @NonNull String filename) throws IOException {
+    private static File getLocalDirectory() {
+        final File dir = new File(LOCAL_DIRECTORY);
+        dir.mkdirs();
+        if (!dir.exists()) {
+            Log.e(TAG, "Could not create directory " + dir);
+            return null;
+        }
+        return dir;
+    }
+
+    @Nullable
+    private static File createFile(@NonNull File dir, @NonNull String filename) throws IOException {
         final File file = new File(dir, filename);
         if (file.exists()) {
+            Log.v(TAG, "Deleting file " + file);
             file.delete();
         }
         if (!file.createNewFile()) {
             Log.e(TAG, "Could not create file " + file);
             return null;
         }
-        Log.d(TAG, "Dumping bitmap at " + file);
+        return file;
+    }
+
+    @Nullable
+    private static File dumpBitmap(@NonNull Bitmap bitmap, @NonNull File dir,
+            @NonNull String filename) throws IOException {
+        final File file = createFile(dir, filename);
+        if (file != null) {
+            dumpBitmap(bitmap, file);
+
+        }
+        return file;
+    }
+
+    @Nullable
+    public static File dumpBitmap(@NonNull Bitmap bitmap, @NonNull File file) throws IOException {
+        Log.i(TAG, "Dumping bitmap at " + file);
         BitmapUtils.saveBitmap(bitmap, file.getParent(), file.getName());
         return file;
     }
 
+    /**
+     * Creates a file in the device, using the name of the current test as a prefix.
+     */
+    @Nullable
+    public static File createTestFile(@NonNull String name) throws IOException {
+        final File dir = getLocalDirectory();
+        if (dir == null) return null;
+
+        final String prefix = JUnitHelper.getCurrentTestName().replaceAll("\\.|\\(|\\/", "_")
+                .replaceAll("\\)", "");
+        final String filename = prefix + "-" + name;
+
+        return createFile(dir, filename);
+    }
+
+    /**
+     * Offers an object to a queue or times out.
+     *
+     * @return {@code true} if the offer was accepted, {$code false} if it timed out or was
+     * interrupted.
+     */
+    public static <T> boolean offer(BlockingQueue<T> queue, T obj, long timeoutMs) {
+        boolean offered = false;
+        try {
+            offered = queue.offer(obj, timeoutMs, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            Log.w(TAG, "interrupted offering", e);
+            Thread.currentThread().interrupt();
+        }
+        if (!offered) {
+            Log.e(TAG, "could not offer " + obj + " in " + timeoutMs + "ms");
+        }
+        return offered;
+    }
+
+    /**
+     * Calls this method to assert given {@code string} is equal to {@link #LARGE_STRING}, as
+     * comparing its value using standard assertions might ANR.
+     */
+    public static void assertEqualsToLargeString(@NonNull String string) {
+        assertThat(string).isNotNull();
+        assertThat(string).hasLength(LARGE_STRING_SIZE);
+        assertThat(string.charAt(0)).isEqualTo(LARGE_STRING_CHAR);
+        assertThat(string.charAt(LARGE_STRING_SIZE - 1)).isEqualTo(LARGE_STRING_CHAR);
+    }
+
     private Helper() {
         throw new UnsupportedOperationException("contain static methods only");
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/InitializedCheckoutActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/InitializedCheckoutActivityTest.java
index c96bbbc..974f0cc 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/InitializedCheckoutActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/InitializedCheckoutActivityTest.java
@@ -30,25 +30,27 @@
 import android.autofillservice.cts.InstrumentedAutoFillService.FillRequest;
 import android.platform.test.annotations.AppModeFull;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 /**
  * Test case for an activity containing non-TextField views with initial values set on XML.
  */
 @AppModeFull // CheckoutActivityTest() is enough to test ephemeral apps support
-public class InitializedCheckoutActivityTest extends AutoFillServiceTestCase {
-
-    @Rule
-    public final AutofillActivityTestRule<InitializedCheckoutActivity> mActivityRule =
-        new AutofillActivityTestRule<InitializedCheckoutActivity>(InitializedCheckoutActivity.class);
+public class InitializedCheckoutActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<InitializedCheckoutActivity> {
 
     private InitializedCheckoutActivity mCheckoutActivity;
 
-    @Before
-    public void setActivity() {
-        mCheckoutActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<InitializedCheckoutActivity> getActivityRule() {
+        return new AutofillActivityTestRule<InitializedCheckoutActivity>(
+                InitializedCheckoutActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mCheckoutActivity = getActivity();
+            }
+        };
+
     }
 
     @Test
diff --git a/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
index 6b47c9f..9e7c800 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
@@ -34,6 +34,8 @@
 import android.content.IntentSender;
 import android.os.Bundle;
 import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.SystemClock;
 import android.service.autofill.AutofillService;
 import android.service.autofill.Dataset;
@@ -52,6 +54,7 @@
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -73,19 +76,24 @@
             new AtomicReference<>();
     private static final Replier sReplier = new Replier();
 
-    private static final Object sLock = new Object();
-
-    // @GuardedBy("sLock") // NOTE: not using annotation because of dependencies
-    private static boolean sIgnoreUnexpectedRequests = false;
-
-    // @GuardedBy("sLock") // NOTE: not using annotation because of dependencies
-    private static boolean sConnected;
+    private static AtomicBoolean sConnected = new AtomicBoolean(false);
 
     protected static String sServiceLabel = SERVICE_CLASS;
 
+    // We must handle all requests in a separate thread as the service's main thread is the also
+    // the UI thread of the test process and we don't want to hose it in case of failures here
+    private static final HandlerThread sMyThread = new HandlerThread("MyServiceThread");
+    private final Handler mHandler;
+
+    static {
+        Log.i(TAG, "Starting thread " + sMyThread);
+        sMyThread.start();
+    }
+
     public InstrumentedAutoFillService() {
         sInstance.set(this);
         sServiceLabel = SERVICE_CLASS;
+        mHandler = Handler.createAsync(sMyThread.getLooper());
     }
 
     private static InstrumentedAutoFillService peekInstance() {
@@ -158,59 +166,76 @@
         return sServiceLabel;
     }
 
+    private void handleConnected(boolean connected) {
+        Log.v(TAG, "handleConnected(): from " + sConnected.get() + " to " + connected);
+        sConnected.set(connected);
+    }
+
     @Override
     public void onConnected() {
-        synchronized (sLock) {
-            Log.v(TAG, "onConnected(): connected=" + sConnected);
-            sConnected = true;
-        }
+        mHandler.post(()->handleConnected(true));
     }
 
     @Override
     public void onDisconnected() {
-        synchronized (sLock) {
-            Log.v(TAG, "onDisconnected(): connected=" + sConnected);
-            sConnected = false;
-        }
+        mHandler.post(()->handleConnected(false));
     }
 
     @Override
     public void onFillRequest(android.service.autofill.FillRequest request,
             CancellationSignal cancellationSignal, FillCallback callback) {
-        if (DUMP_FILL_REQUESTS) dumpStructure("onFillRequest()", request.getFillContexts());
-        synchronized (sLock) {
-            if (sIgnoreUnexpectedRequests || !fromSamePackage(request.getFillContexts()))  {
-                Log.w(TAG, "Ignoring onFillRequest()");
-                return;
-            }
+
+        final ComponentName component = getLastActivityComponent(request.getFillContexts());
+        if (!JUnitHelper.isRunningTest()) {
+            Log.e(TAG, "onFillRequest(" + component + ") called after tests finished");
+            return;
         }
-        sReplier.onFillRequest(request.getFillContexts(), request.getClientState(),
-                cancellationSignal, callback, request.getFlags());
+        if (!fromSamePackage(component))  {
+            Log.w(TAG, "Ignoring onFillRequest() from different package: " + component);
+            return;
+        }
+        if (DUMP_FILL_REQUESTS) {
+            dumpStructure("onFillRequest()", request.getFillContexts());
+        } else {
+            Log.i(TAG, "onFillRequest() for " + component.toShortString());
+        }
+        mHandler.post(
+                () -> sReplier.onFillRequest(request.getFillContexts(), request.getClientState(),
+                        cancellationSignal, callback, request.getFlags()));
     }
 
     @Override
     public void onSaveRequest(android.service.autofill.SaveRequest request,
             SaveCallback callback) {
-        if (DUMP_SAVE_REQUESTS) dumpStructure("onSaveRequest()", request.getFillContexts());
-        synchronized (sLock) {
-            if (sIgnoreUnexpectedRequests || !fromSamePackage(request.getFillContexts())) {
-                Log.w(TAG, "Ignoring onSaveRequest()");
-                return;
-            }
+        mHandler.post(()->handleSaveRequest(request, callback));
+    }
+
+    private void handleSaveRequest(android.service.autofill.SaveRequest request,
+            SaveCallback callback) {
+        final ComponentName component = getLastActivityComponent(request.getFillContexts());
+        if (!JUnitHelper.isRunningTest()) {
+            Log.e(TAG, "onSaveRequest(" + component + ") called after tests finished");
+            return;
         }
-        sReplier.onSaveRequest(request.getFillContexts(), request.getClientState(), callback,
-                request.getDatasetIds());
+        if (!fromSamePackage(component)) {
+            Log.w(TAG, "Ignoring onSaveRequest() from different package: " + component);
+            return;
+        }
+        if (DUMP_SAVE_REQUESTS) {
+            dumpStructure("onSaveRequest()", request.getFillContexts());
+        } else {
+            Log.i(TAG, "onSaveRequest() for " + component.toShortString());
+        }
+        mHandler.post(() -> sReplier.onSaveRequest(request.getFillContexts(),
+                request.getClientState(), callback,
+                request.getDatasetIds()));
     }
 
     private static boolean isConnected() {
-        synchronized (sLock) {
-            return sConnected;
-        }
+        return sConnected.get();
     }
 
-    private boolean fromSamePackage(List<FillContext> contexts) {
-        final ComponentName component = contexts.get(contexts.size() - 1).getStructure()
-                .getActivityComponent();
+    private boolean fromSamePackage(ComponentName component) {
         final String actualPackage = component.getPackageName();
         if (!actualPackage.equals(getPackageName())
                 && !actualPackage.equals(sReplier.mAcceptedPackageName)) {
@@ -220,15 +245,8 @@
         return true;
     }
 
-    /**
-     * Sets whether unexpected calls to
-     * {@link #onFillRequest(android.service.autofill.FillRequest, CancellationSignal, FillCallback)}
-     * should throw an exception.
-     */
-    public static void setIgnoreUnexpectedRequests(boolean ignore) {
-        synchronized (sLock) {
-            sIgnoreUnexpectedRequests = ignore;
-        }
+    private ComponentName getLastActivityComponent(List<FillContext> contexts) {
+        return contexts.get(contexts.size() - 1).getStructure().getActivityComponent();
     }
 
     /**
@@ -269,7 +287,7 @@
 
     static void resetStaticState() {
         sInstance.set(null);
-        sConnected = false;
+        sConnected.set(false);
         sServiceLabel = SERVICE_CLASS;
     }
 
@@ -407,8 +425,9 @@
          * Sets the {@link IntentSender} that is passed to
          * {@link SaveCallback#onSuccess(IntentSender)}.
          */
-        void setOnSave(IntentSender intentSender) {
+        Replier setOnSave(IntentSender intentSender) {
             mOnSaveIntentSender = intentSender;
+            return this;
         }
 
         /**
@@ -585,19 +604,24 @@
             } catch (Throwable t) {
                 addException(t);
             } finally {
-                mFillRequests.offer(new FillRequest(contexts, data, cancellationSignal, callback,
-                        flags));
+                Helper.offer(mFillRequests, new FillRequest(contexts, data, cancellationSignal,
+                        callback, flags), CONNECTION_TIMEOUT.ms());
             }
         }
 
         private void onSaveRequest(List<FillContext> contexts, Bundle data, SaveCallback callback,
                 List<String> datasetIds) {
             Log.d(TAG, "onSaveRequest(): sender=" + mOnSaveIntentSender);
-            mSaveRequests.offer(new SaveRequest(contexts, data, callback, datasetIds));
-            if (mOnSaveIntentSender != null) {
-                callback.onSuccess(mOnSaveIntentSender);
-            } else {
-                callback.onSuccess();
+
+            try {
+                if (mOnSaveIntentSender != null) {
+                    callback.onSuccess(mOnSaveIntentSender);
+                } else {
+                    callback.onSuccess();
+                }
+            } finally {
+                Helper.offer(mSaveRequests, new SaveRequest(contexts, data, callback, datasetIds),
+                        CONNECTION_TIMEOUT.ms());
             }
         }
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/JUnitHelper.java b/tests/autofillservice/src/android/autofillservice/cts/JUnitHelper.java
index 28e1fde..2920500 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/JUnitHelper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/JUnitHelper.java
@@ -17,21 +17,33 @@
 package android.autofillservice.cts;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 /**
  * Generic helper for JUnit needs.
  */
 public final class JUnitHelper {
 
-    private static String sCurrentTestNamer;
+    private static String sCurrentTestName;
+    private static String sCurrentTestClass;
 
     @NonNull
-    static String getCurrentTestName() {
-        return sCurrentTestNamer != null ? sCurrentTestNamer : "N/A";
+    public static String getCurrentTestName() {
+        if (sCurrentTestName != null) return sCurrentTestName;
+        if (sCurrentTestClass != null) return sCurrentTestClass;
+        return "(Unknown test)";
     }
 
-    public static void setCurrentTestName(String name) {
-        sCurrentTestNamer = name;
+    public static void setCurrentTestName(@Nullable String name) {
+        sCurrentTestName = name;
+    }
+
+    public static void setCurrentTestClass(@Nullable String testClass) {
+        sCurrentTestClass = testClass;
+    }
+
+    public static boolean isRunningTest() {
+        return sCurrentTestName != null;
     }
 
     private JUnitHelper() {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
index c476c7c..183ca34 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -86,6 +86,7 @@
 import android.view.autofill.AutofillManager;
 import android.widget.RemoteViews;
 
+import org.junit.Ignore;
 import org.junit.Test;
 
 import java.util.concurrent.CountDownLatch;
@@ -700,8 +701,7 @@
         mUiBot.assertDatasets("The Dude");
 
         // tapping outside autofill window should close it and raise ui hidden event
-        mUiBot.waitForWindowChange(() -> tap(mActivity.getUsernameLabel()),
-                Timeouts.UI_TIMEOUT.getMaxValue());
+        mUiBot.waitForWindowChange(() -> tap(mActivity.getUsernameLabel()));
         callback.assertUiHiddenEvent(username);
 
         mUiBot.assertNoDatasets();
@@ -972,13 +972,17 @@
             final String extraValue = saveRequest.data.getString("numbers");
             assertWithMessage("extras not passed on save").that(extraValue).isEqualTo("4815162342");
         } finally {
-            // Make sure we can no longer add overlays
-            runShellCommand("appops set %s SYSTEM_ALERT_WINDOW ignore", mPackageName);
-            // Make sure the overlay is removed
-            mActivity.runOnUiThread(() -> {
-                WindowManager windowManager = mContext.getSystemService(WindowManager.class);
-                windowManager.removeView(overlay[0]);
-            });
+            try {
+                // Make sure we can no longer add overlays
+                runShellCommand("appops set %s SYSTEM_ALERT_WINDOW ignore", mPackageName);
+                // Make sure the overlay is removed
+                mActivity.runOnUiThread(() -> {
+                    WindowManager windowManager = mContext.getSystemService(WindowManager.class);
+                    windowManager.removeView(overlay[0]);
+                });
+            } catch (Exception e) {
+                mSafeCleanerRule.add(e);
+            }
         }
     }
 
@@ -2154,6 +2158,7 @@
         mActivity.assertAutoFilled();
     }
 
+    @Ignore("blocked on b/4222506") // STOPSHIP: must fix and remove @Ignore before Q is launched
     @Test
     public void testCommitMultipleTimes() throws Throwable {
         // Set service.
@@ -2163,7 +2168,7 @@
                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
                 .build();
 
-        for (int i = 1; i <= 3; i++) {
+        for (int i = 1; i <= 10; i++) {
             Log.i(TAG, "testCommitMultipleTimes(): step " + i);
             final String username = "user-" + i;
             final String password = "pass-" + i;
@@ -2219,7 +2224,7 @@
         // Set service.
         enableService();
 
-        for (int i = 1; i <= 3; i++) {
+        for (int i = 1; i <= 10; i++) {
             Log.i(TAG, "testCancelMultipleTimes(): step " + i);
             final String username = "user-" + i;
             final String password = "pass-" + i;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginWithCustomHighlightActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginWithCustomHighlightActivityTest.java
index 3193cc3..cefc58c 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginWithCustomHighlightActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginWithCustomHighlightActivityTest.java
@@ -25,24 +25,24 @@
 import android.support.test.uiautomator.UiObject2;
 import android.view.View;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.concurrent.TimeoutException;
 
-public class LoginWithCustomHighlightActivityTest extends AutoFillServiceTestCase {
-
-    @Rule
-    public final AutofillActivityTestRule<LoginWithCustomHighlightActivity> mActivityRule =
-            new AutofillActivityTestRule<LoginWithCustomHighlightActivity>(
-            LoginWithCustomHighlightActivity.class);
+public class LoginWithCustomHighlightActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<LoginWithCustomHighlightActivity> {
 
     private LoginWithCustomHighlightActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<LoginWithCustomHighlightActivity> getActivityRule() {
+        return new AutofillActivityTestRule<LoginWithCustomHighlightActivity>(
+                LoginWithCustomHighlightActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     @Test
@@ -84,7 +84,6 @@
      * Requests focus on username and expect Window event happens.
      */
     protected void requestFocusOnUsername() throws TimeoutException {
-        mUiBot.waitForWindowChange(() -> mActivity.onUsername(View::requestFocus),
-                Timeouts.UI_TIMEOUT.getMaxValue());
+        mUiBot.waitForWindowChange(() -> mActivity.onUsername(View::requestFocus));
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginWithStringsActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginWithStringsActivityTest.java
index 8474d28d..131b40f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginWithStringsActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginWithStringsActivityTest.java
@@ -40,22 +40,24 @@
 import android.platform.test.annotations.AppModeFull;
 import android.view.View;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 @AppModeFull // LoginActivityTest is enough to test ephemeral apps support
-public class LoginWithStringsActivityTest extends AutoFillServiceTestCase {
-
-    @Rule
-    public final AutofillActivityTestRule<LoginWithStringsActivity> mActivityRule =
-            new AutofillActivityTestRule<LoginWithStringsActivity>(LoginWithStringsActivity.class);
+public class LoginWithStringsActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<LoginWithStringsActivity> {
 
     private LoginWithStringsActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+
+    @Override
+    protected AutofillActivityTestRule<LoginWithStringsActivity> getActivityRule() {
+        return new AutofillActivityTestRule<LoginWithStringsActivity>(
+                LoginWithStringsActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     @Test
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MultiScreenLoginTest.java b/tests/autofillservice/src/android/autofillservice/cts/MultiScreenLoginTest.java
new file mode 100644
index 0000000..b41d185
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/MultiScreenLoginTest.java
@@ -0,0 +1,184 @@
+/*
+ * 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.autofillservice.cts;
+
+import static android.autofillservice.cts.Helper.ID_PASSWORD;
+import static android.autofillservice.cts.Helper.ID_USERNAME;
+import static android.autofillservice.cts.Helper.assertTextAndValue;
+import static android.autofillservice.cts.Helper.findNodeByResourceId;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_USERNAME;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.assist.AssistStructure;
+import android.autofillservice.cts.InstrumentedAutoFillService.FillRequest;
+import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
+import android.content.ComponentName;
+import android.service.autofill.SaveInfo;
+
+import org.junit.Test;
+
+/**
+ * Test case for the senario where a login screen is split in multiple activities.
+ */
+public class MultiScreenLoginTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<UsernameOnlyActivity> {
+
+    private UsernameOnlyActivity mActivity;
+
+    @Override
+    protected AutofillActivityTestRule<UsernameOnlyActivity> getActivityRule() {
+        return new AutofillActivityTestRule<UsernameOnlyActivity>(UsernameOnlyActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
+    }
+
+    /**
+     * Tests the "traditional" scenario where the service must save each field (username and
+     * password) separately.
+     */
+    @Test
+    public void testSaveEachFieldSeparately() throws Exception {
+        // Set service
+        enableService();
+
+        // First handle username...
+
+        // Set expectations.
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setRequiredSavableIds(SAVE_DATA_TYPE_USERNAME, ID_USERNAME)
+                .build());
+
+        // Trigger autofill
+        mActivity.focusOnUsername();
+        sReplier.getNextFillRequest();
+        mUiBot.assertNoDatasetsEver();
+
+        // Trigger save...
+        mActivity.setUsername("dude");
+        mActivity.next();
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_USERNAME);
+
+        // ..and assert results
+        final SaveRequest saveRequest1 = sReplier.getNextSaveRequest();
+        assertTextAndValue(findNodeByResourceId(saveRequest1.structure, ID_USERNAME), "dude");
+
+        // ...now rinse and repeat for password
+
+        // Get the activity
+        final PasswordOnlyActivity activity2 = AutofillTestWatcher
+                .getActivity(PasswordOnlyActivity.class);
+
+        // Set expectations.
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_PASSWORD)
+                .build());
+
+        // Trigger autofill
+        activity2.focusOnPassword();
+        sReplier.getNextFillRequest();
+        mUiBot.assertNoDatasetsEver();
+
+        // Trigger save...
+        activity2.setPassword("sweet");
+        activity2.login();
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+
+        // ..and assert results
+        final SaveRequest saveRequest2 = sReplier.getNextSaveRequest();
+        assertTextAndValue(findNodeByResourceId(saveRequest2.structure, ID_PASSWORD), "sweet");
+    }
+
+    /**
+     * Tests the new scenario introudced on Q where the service can set a multi-screen session.
+     */
+    @Test
+    public void testSaveBothFieldsAtOnce() throws Exception {
+        // Set service
+        enableService();
+
+        // First handle username...
+
+        // Set expectations.
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setSaveInfoFlags(SaveInfo.FLAG_DELAY_SAVE).build());
+
+        // Trigger autofill
+        mActivity.focusOnUsername();
+        final FillRequest fillRequest1 = sReplier.getNextFillRequest();
+        final ComponentName component1 = fillRequest1.structure.getActivityComponent();
+        assertThat(component1).isEqualTo(mActivity.getComponentName());
+        mUiBot.assertNoDatasetsEver();
+
+        // Trigger what would be save...
+        mActivity.setUsername("dude");
+        mActivity.next();
+        mUiBot.assertSaveNotShowing();
+
+        // ...now rinse and repeat for password
+
+        // Get the activity
+        final PasswordOnlyActivity passwordActivity = AutofillTestWatcher
+                .getActivity(PasswordOnlyActivity.class);
+
+        // Set expectations.
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setRequiredSavableIds(SAVE_DATA_TYPE_USERNAME | SAVE_DATA_TYPE_PASSWORD,
+                        ID_PASSWORD)
+                .build());
+
+        // Trigger autofill
+        passwordActivity.focusOnPassword();
+        final FillRequest fillRequest2 = sReplier.getNextFillRequest();
+        final ComponentName component2 = fillRequest2.structure.getActivityComponent();
+        assertThat(component2).isEqualTo(passwordActivity.getComponentName());
+        mUiBot.assertNoDatasetsEver();
+
+        // Trigger save...
+        passwordActivity.setPassword("sweet");
+        passwordActivity.login();
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_USERNAME, SAVE_DATA_TYPE_PASSWORD);
+
+        // ..and assert results
+        final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+        assertThat(saveRequest.contexts.size()).isEqualTo(2);
+
+        // Username is set in the 1st context
+        final AssistStructure previousStructure = saveRequest.contexts.get(0).getStructure();
+        assertWithMessage("no structure for 1st activity").that(previousStructure).isNotNull();
+        assertTextAndValue(findNodeByResourceId(previousStructure, ID_USERNAME), "dude");
+        final ComponentName componentPrevious = previousStructure.getActivityComponent();
+        assertThat(componentPrevious).isEqualTo(mActivity.getComponentName());
+
+        // Password is set in the 2nd context
+        final AssistStructure currentStructure = saveRequest.contexts.get(1).getStructure();
+        assertWithMessage("no structure for 2nd activity").that(currentStructure).isNotNull();
+        assertTextAndValue(findNodeByResourceId(currentStructure, ID_PASSWORD), "sweet");
+        final ComponentName componentCurrent = currentStructure.getActivityComponent();
+        assertThat(componentCurrent).isEqualTo(passwordActivity.getComponentName());
+    }
+
+    // TODO(b/112051762): add test cases for more scenarios such as:
+    // - make sure that activity not marked with keepAlive is not sent in the 2nd request
+    // - somehow verify that the first activity's session is gone
+    // - test client state
+    // - WebView
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MultiWindowEmptyActivity.java b/tests/autofillservice/src/android/autofillservice/cts/MultiWindowEmptyActivity.java
index c6cd06a..5aad499 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MultiWindowEmptyActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MultiWindowEmptyActivity.java
@@ -49,8 +49,11 @@
     }
 
     public static MultiWindowEmptyActivity waitNewInstance() throws InterruptedException {
-        sLastInstanceLatch.await(Timeouts.ACTIVITY_RESURRECTION.getMaxValue(),
-                TimeUnit.MILLISECONDS);
+        if (!sLastInstanceLatch.await(Timeouts.ACTIVITY_RESURRECTION.getMaxValue(),
+                TimeUnit.MILLISECONDS)) {
+            throw new RetryableException("New MultiWindowLoginActivity didn't start",
+                    Timeouts.ACTIVITY_RESURRECTION);
+        }
         sLastInstanceLatch = null;
         return sLastInstance;
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivity.java b/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivity.java
index e0b3109..02460050 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivity.java
@@ -49,8 +49,11 @@
     }
 
     public static MultiWindowLoginActivity waitNewInstance() throws InterruptedException {
-        sLastInstanceLatch.await(Timeouts.ACTIVITY_RESURRECTION.getMaxValue(),
-                TimeUnit.MILLISECONDS);
+        if (!sLastInstanceLatch.await(Timeouts.ACTIVITY_RESURRECTION.getMaxValue(),
+                TimeUnit.MILLISECONDS)) {
+            throw new RetryableException("New MultiWindowLoginActivity didn't start",
+                    Timeouts.ACTIVITY_RESURRECTION);
+        }
         sLastInstanceLatch = null;
         return sLastInstance;
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivityTest.java
index 0796028..68d1556 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivityTest.java
@@ -15,7 +15,7 @@
  */
 package android.autofillservice.cts;
 
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.autofillservice.cts.Helper.ID_PASSWORD;
 import static android.autofillservice.cts.Helper.ID_USERNAME;
 import static android.autofillservice.cts.common.ShellHelper.runShellCommand;
@@ -27,46 +27,50 @@
 
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.content.Intent;
 import android.platform.test.annotations.AppModeFull;
 import android.view.View;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.concurrent.TimeoutException;
 
 @AppModeFull // This test requires android.permission.MANAGE_ACTIVITY_STACKS
-public class MultiWindowLoginActivityTest extends AutoFillServiceTestCase {
-
-    @Rule
-    public final AutofillActivityTestRule<MultiWindowLoginActivity> mActivityRule =
-            new AutofillActivityTestRule<MultiWindowLoginActivity>(MultiWindowLoginActivity.class);
+public class MultiWindowLoginActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<MultiWindowLoginActivity> {
 
     private LoginActivity mActivity;
-    protected ActivityManager mAm;
+    private ActivityManager mAm;
+    private ActivityTaskManager mAtm;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<MultiWindowLoginActivity> getActivityRule() {
+        return new AutofillActivityTestRule<MultiWindowLoginActivity>(
+                MultiWindowLoginActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+                mAm = mContext.getSystemService(ActivityManager.class);
+                mAtm = mContext.getSystemService(ActivityTaskManager.class);
+            }
+        };
     }
 
     @Before
     public void setup() {
         assumeTrue("Skipping test: no split multi-window support",
-                ActivityManager.supportsSplitScreenMultiWindow(mContext));
-        mAm = mContext.getSystemService(ActivityManager.class);
+                ActivityTaskManager.supportsSplitScreenMultiWindow(mContext));
     }
 
     /**
      * Touch a view and exepct autofill window change
      */
     protected void tapViewAndExpectWindowEvent(View view) throws TimeoutException {
-        mUiBot.waitForWindowChange(() -> tap(view), Timeouts.UI_TIMEOUT.getMaxValue());
+        mUiBot.waitForWindowChange(() -> tap(view));
     }
 
-
     protected String runAmStartActivity(Class<? extends Activity> activityClass, int flags) {
         return runAmStartActivity(activityClass.getName(), flags);
     }
@@ -80,7 +84,7 @@
      * Put activity1 in TOP, will be followed by amStartActivity()
      */
     protected void splitWindow(Activity activity1) {
-        mAm.setTaskWindowingModeSplitScreenPrimary(activity1.getTaskId(),
+        mAtm.setTaskWindowingModeSplitScreenPrimary(activity1.getTaskId(),
                 SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, true, false, null, true);
 
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MultipleFragmentLoginTest.java b/tests/autofillservice/src/android/autofillservice/cts/MultipleFragmentLoginTest.java
index 8da4add..ad08fd3 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MultipleFragmentLoginTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MultipleFragmentLoginTest.java
@@ -30,24 +30,28 @@
 import android.view.autofill.AutofillValue;
 import android.widget.EditText;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
-public class MultipleFragmentLoginTest extends AutoFillServiceTestCase {
-    private static final String LOG_TAG = MultipleFragmentLoginTest.class.getSimpleName();
-    @Rule
-    public final AutofillActivityTestRule<FragmentContainerActivity> mActivityRule =
-            new AutofillActivityTestRule<>(FragmentContainerActivity.class);
+public class MultipleFragmentLoginTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<FragmentContainerActivity> {
+
+    private static final String LOG_TAG = "MultipleFragmentLoginTest";
+
     private FragmentContainerActivity mActivity;
     private EditText mEditText1;
     private EditText mEditText2;
 
-    @Before
-    public void init() {
-        mActivity = mActivityRule.getActivity();
-        mEditText1 = mActivity.findViewById(R.id.editText1);
-        mEditText2 = mActivity.findViewById(R.id.editText2);
+    @Override
+    protected AutofillActivityTestRule<FragmentContainerActivity> getActivityRule() {
+        return new AutofillActivityTestRule<FragmentContainerActivity>(
+                FragmentContainerActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+                mEditText1 = mActivity.findViewById(R.id.editText1);
+                mEditText2 = mActivity.findViewById(R.id.editText2);
+            }
+        };
     }
 
     @Test
@@ -64,21 +68,18 @@
                 .setExtras(clientState)
                 .build());
 
-        final InstrumentedAutoFillService.FillRequest[] fillRequest =
-                new InstrumentedAutoFillService.FillRequest[1];
+        // Trigger autofill on editText2
+        mActivity.syncRunOnUiThread(() -> mEditText2.requestFocus());
 
-        // Trigger autofill
-        mActivity.syncRunOnUiThread(() -> {
-            mEditText2.requestFocus();
-            mEditText1.requestFocus();
-        });
+        final InstrumentedAutoFillService.FillRequest fillRequest1 = sReplier.getNextFillRequest();
+        assertThat(fillRequest1.data).isNull();
 
-        fillRequest[0] = sReplier.getNextFillRequest();
+        mUiBot.assertNoDatasetsEver(); // UI is only shown on editText1
 
-        assertThat(fillRequest[0].data).isNull();
+        mActivity.setRootContainerFocusable(false);
 
-        AssistStructure structure = fillRequest[0].contexts.get(0).getStructure();
-        assertThat(fillRequest[0].contexts.size()).isEqualTo(1);
+        final AssistStructure structure = fillRequest1.contexts.get(0).getStructure();
+        assertThat(fillRequest1.contexts.size()).isEqualTo(1);
         assertThat(findNodeByResourceId(structure, "editText1")).isNotNull();
         assertThat(findNodeByResourceId(structure, "editText2")).isNotNull();
         assertThat(findNodeByResourceId(structure, "editText3")).isNull();
@@ -86,6 +87,7 @@
         assertThat(findNodeByResourceId(structure, "editText5")).isNull();
 
         // Wait until autofill has been applied
+        mActivity.syncRunOnUiThread(() -> mEditText1.requestFocus());
         mUiBot.selectDataset("dataset1");
         mUiBot.assertShownByText("editText1-autofilled");
 
@@ -111,15 +113,15 @@
                         R.id.rootContainer, new FragmentWithMoreEditTexts(),
                         FRAGMENT_TAG).commitNow());
         EditText editText5 = mActivity.findViewById(R.id.editText5);
-        fillRequest[0] = sReplier.getNextFillRequest();
+        final InstrumentedAutoFillService.FillRequest fillRequest2 = sReplier.getNextFillRequest();
 
         // The fillRequest should have a fillContext for each partition. The first partition
         // should be filled in
-        assertThat(fillRequest[0].contexts.size()).isEqualTo(2);
+        assertThat(fillRequest2.contexts.size()).isEqualTo(2);
 
-        assertThat(fillRequest[0].data.getString("key")).isEqualTo("value1");
+        assertThat(fillRequest2.data.getString("key")).isEqualTo("value1");
 
-        AssistStructure structure1 = fillRequest[0].contexts.get(0).getStructure();
+        final AssistStructure structure1 = fillRequest2.contexts.get(0).getStructure();
         ViewNode editText1Node = findNodeByResourceId(structure1, "editText1");
         // The actual value in the structure is not updated in FillRequest-contexts, but the
         // autofill value is. For text views in SaveRequest both are updated, but this is the
@@ -136,7 +138,7 @@
         assertThat(findNodeByResourceId(structure1, "editText4")).isNull();
         assertThat(findNodeByResourceId(structure1, "editText5")).isNull();
 
-        AssistStructure structure2 = fillRequest[0].contexts.get(1).getStructure();
+        final AssistStructure structure2 = fillRequest2.contexts.get(1).getStructure();
 
         assertThat(findNodeByResourceId(structure2, "editText1")).isNull();
         assertThat(findNodeByResourceId(structure2, "editText2")).isNull();
@@ -157,33 +159,33 @@
         mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
 
         // The saveRequest should have a fillContext for each partition with all the data
-        InstrumentedAutoFillService.SaveRequest saveRequest = sReplier.getNextSaveRequest();
+        final InstrumentedAutoFillService.SaveRequest saveRequest = sReplier.getNextSaveRequest();
         assertThat(saveRequest.contexts.size()).isEqualTo(2);
 
         assertThat(saveRequest.data.getString("key")).isEqualTo("value2");
 
-        structure1 = saveRequest.contexts.get(0).getStructure();
-        editText1Node = findNodeByResourceId(structure1, "editText1");
+        final AssistStructure saveStructure1 = saveRequest.contexts.get(0).getStructure();
+        editText1Node = findNodeByResourceId(saveStructure1, "editText1");
         assertThat(editText1Node.getText().toString()).isEqualTo("editText1-autofilled");
 
-        editText2Node = findNodeByResourceId(structure1, "editText2");
+        editText2Node = findNodeByResourceId(saveStructure1, "editText2");
         assertThat(editText2Node.getText().toString()).isEqualTo("editText2-manually-filled");
 
-        assertThat(findNodeByResourceId(structure1, "editText3")).isNull();
-        assertThat(findNodeByResourceId(structure1, "editText4")).isNull();
-        assertThat(findNodeByResourceId(structure1, "editText5")).isNull();
+        assertThat(findNodeByResourceId(saveStructure1, "editText3")).isNull();
+        assertThat(findNodeByResourceId(saveStructure1, "editText4")).isNull();
+        assertThat(findNodeByResourceId(saveStructure1, "editText5")).isNull();
 
-        structure2 = saveRequest.contexts.get(1).getStructure();
-        assertThat(findNodeByResourceId(structure2, "editText1")).isNull();
-        assertThat(findNodeByResourceId(structure2, "editText2")).isNull();
+        final AssistStructure saveStructure2 = saveRequest.contexts.get(1).getStructure();
+        assertThat(findNodeByResourceId(saveStructure2, "editText1")).isNull();
+        assertThat(findNodeByResourceId(saveStructure2, "editText2")).isNull();
 
-        ViewNode editText3Node = findNodeByResourceId(structure2, "editText3");
+        ViewNode editText3Node = findNodeByResourceId(saveStructure2, "editText3");
         assertThat(editText3Node.getText().toString()).isEqualTo("editText3-autofilled");
 
-        ViewNode editText4Node = findNodeByResourceId(structure2, "editText4");
+        ViewNode editText4Node = findNodeByResourceId(saveStructure2, "editText4");
         assertThat(editText4Node.getText().toString()).isEqualTo("editText4-autofilled");
 
-        ViewNode editText5Node = findNodeByResourceId(structure2, "editText5");
+        ViewNode editText5Node = findNodeByResourceId(saveStructure2, "editText5");
         assertThat(editText5Node.getText().toString()).isEqualTo("editText5-manually-filled");
     }
 
@@ -213,14 +215,15 @@
 
         sReplier.addResponse(response.build());
 
-        // Trigger autofill
-        mActivity.syncRunOnUiThread(() -> {
-            mEditText2.requestFocus();
-            mEditText1.requestFocus();
-        });
+        // Trigger autofill on editText2
+        mActivity.syncRunOnUiThread(() -> mEditText2.requestFocus());
+        sReplier.getNextFillRequest();
+        mUiBot.assertNoDatasetsEver(); // UI is only shown on editText1
+
+        mActivity.setRootContainerFocusable(false);
 
         // Check UI is shown, but don't select it.
-        sReplier.getNextFillRequest();
+        mActivity.syncRunOnUiThread(() -> mEditText1.requestFocus());
         mUiBot.assertDatasets("dataset1");
 
         // Switch fragments
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MutableAutofillIdTest.java b/tests/autofillservice/src/android/autofillservice/cts/MutableAutofillIdTest.java
index c04685d..f9b57f1 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MutableAutofillIdTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MutableAutofillIdTest.java
@@ -38,31 +38,17 @@
 import android.view.autofill.AutofillId;
 import android.widget.EditText;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.List;
-import java.util.concurrent.TimeoutException;
 
 /**
  * Test cases for the cases where the autofill id of a view is changed by the app.
  */
-public class MutableAutofillIdTest extends AutoFillServiceTestCase {
+public class MutableAutofillIdTest extends AbstractGridActivityTestCase {
 
     private static final String TAG = "MutableAutofillIdTest";
 
-    @Rule
-    public final AutofillActivityTestRule<GridActivity> mActivityRule =
-            new AutofillActivityTestRule<GridActivity>(GridActivity.class);
-
-    private GridActivity mActivity;
-
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
-    }
-
     @Test
     public void testDatasetPickerIsNotShownAfterViewIsSwappedOut() throws Exception {
         enableService();
@@ -303,12 +289,4 @@
         assertThat(node2Context2.getIdEntry()).isEqualTo(ID_L1C2);
         assertThat(node2Context2.getText().toString()).isEqualTo("NOD2");
     }
-
-    /**
-     * Focus to a cell and expect window event
-     */
-    void focusCell(int row, int column) throws TimeoutException {
-        mUiBot.waitForWindowChange(() -> mActivity.focusCell(row, column),
-                Timeouts.UI_TIMEOUT.getMaxValue());
-    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java b/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
index 2108a4b..ff3eef9 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
@@ -17,10 +17,13 @@
 package android.autofillservice.cts;
 
 import static android.autofillservice.cts.Helper.callbackEventAsString;
+import static android.autofillservice.cts.Timeouts.CALLBACK_NOT_CALLED_TIMEOUT_MS;
 import static android.autofillservice.cts.Timeouts.CONNECTION_TIMEOUT;
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.util.Log;
 import android.view.View;
 import android.view.autofill.AutofillManager.AutofillCallback;
@@ -39,17 +42,33 @@
 
     public static final Timeout MY_TIMEOUT = CONNECTION_TIMEOUT;
 
+    // We must handle all requests in a separate thread as the service's main thread is the also
+    // the UI thread of the test process and we don't want to hose it in case of failures here
+    private static final HandlerThread sMyThread = new HandlerThread("MyCallbackThread");
+    private final Handler mHandler;
+
+    static {
+        Log.i(TAG, "Starting thread " + sMyThread);
+        sMyThread.start();
+    }
+
+    MyAutofillCallback() {
+        mHandler = Handler.createAsync(sMyThread.getLooper());
+    }
+
     @Override
     public void onAutofillEvent(View view, int event) {
-        Log.v(TAG, "onAutofillEvent: view=" + view + ", event=" + callbackEventAsString(event));
-        mEvents.offer(new MyEvent(view, event));
+        mHandler.post(() -> offer(new MyEvent(view, event)));
     }
 
     @Override
     public void onAutofillEvent(View view, int childId, int event) {
-        Log.v(TAG, "onAutofillEvent: view=" + view + ", child=" + childId
-                + ", event=" + callbackEventAsString(event));
-        mEvents.offer(new MyEvent(view, childId, event));
+        mHandler.post(() -> offer(new MyEvent(view, childId, event)));
+    }
+
+    private void offer(MyEvent event) {
+        Log.v(TAG, "offer: " + event);
+        Helper.offer(mEvents, event, MY_TIMEOUT.ms());
     }
 
     /**
@@ -67,7 +86,7 @@
      * Assert no more events were received.
      */
     void assertNotCalled() throws InterruptedException {
-        final MyEvent event = mEvents.poll(CONNECTION_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
+        final MyEvent event = mEvents.poll(CALLBACK_NOT_CALLED_TIMEOUT_MS, TimeUnit.MILLISECONDS);
         if (event != null) {
             // Not retryable.
             throw new IllegalStateException("should not have received " + event);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MyWebView.java b/tests/autofillservice/src/android/autofillservice/cts/MyWebView.java
index 754d5e9..5ccfb4a 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MyWebView.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MyWebView.java
@@ -21,8 +21,9 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
-import android.util.SparseArray;
-import android.view.autofill.AutofillValue;
+import android.util.Log;
+import android.view.autofill.AutofillManager;
+import android.webkit.JavascriptInterface;
 import android.webkit.WebView;
 
 import java.util.concurrent.CountDownLatch;
@@ -33,14 +34,20 @@
  */
 public class MyWebView extends WebView {
 
+    private static final String TAG = "MyWebView";
+
     private FillExpectation mExpectation;
 
     public MyWebView(Context context) {
         super(context);
+        setJsHandler();
+        Log.d(TAG, "isAutofillEnabled() on constructor? " + isAutofillEnabled());
     }
 
     public MyWebView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        setJsHandler();
+        Log.d(TAG, "isAutofillEnabled() on constructor? " + isAutofillEnabled());
     }
 
     public void expectAutofill(String username, String password) {
@@ -49,63 +56,77 @@
 
     public void assertAutofilled() throws Exception {
         assertWithMessage("expectAutofill() not called").that(mExpectation).isNotNull();
-        final boolean set = mExpectation.mLatch.await(FILL_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
-        if (mExpectation.mException != null) {
-            throw mExpectation.mException;
-        }
-        assertWithMessage("Timeout (%s ms) expecting autofill()", FILL_TIMEOUT.ms())
-                .that(set).isTrue();
-        assertWithMessage("Wrong value for username").that(mExpectation.mActualUsername)
-                .isEqualTo(mExpectation.mExpectedUsername);
-        assertWithMessage("Wrong value for password").that(mExpectation.mActualPassword)
-                .isEqualTo(mExpectation.mExpectedPassword);
+        mExpectation.assertUsernameCalled();
+        mExpectation.assertPasswordCalled();
     }
 
-    @Override
-    public void autofill(SparseArray<AutofillValue> values) {
-        super.autofill(values);
+    private void setJsHandler() {
+        getSettings().setJavaScriptEnabled(true);
+        addJavascriptInterface(new JavascriptHandler(), "JsHandler");
+    }
 
-        if (mExpectation == null) return;
-
-        try {
-            if (values == null || values.size() != 2) {
-                mExpectation.mException =
-                        new IllegalArgumentException("Invalid values on autofill(): " + values);
-            } else {
-                try {
-                    // We don't know the order of the values in the array. As we're just expecting
-                    // 2, it's easy to just check them individually; if we had more, than we would
-                    // need to override onProvideAutofillVirtualStructure() to keep track of the
-                    // nodes added by WebView so we could save their AutofillIds and reuse here.
-                    final String value1 = values.valueAt(0).getTextValue().toString();
-                    final String value2 = values.valueAt(1).getTextValue().toString();
-                    if (mExpectation.mExpectedUsername.equals(value1)) {
-                        mExpectation.mActualUsername = value1;
-                        mExpectation.mActualPassword = value2;
-                    } else {
-                        mExpectation.mActualUsername = value2;
-                        mExpectation.mActualPassword = value1;
-                    }
-                } catch (Exception e) {
-                    mExpectation.mException = e;
-                }
-            }
-        } finally {
-            mExpectation.mLatch.countDown();
-        }
+    boolean isAutofillEnabled() {
+        return getContext().getSystemService(AutofillManager.class).isEnabled();
     }
 
     private class FillExpectation {
-        private final CountDownLatch mLatch = new CountDownLatch(1);
+        private final CountDownLatch mUsernameLatch = new CountDownLatch(1);
+        private final CountDownLatch mPasswordLatch = new CountDownLatch(1);
         private final String mExpectedUsername;
         private final String mExpectedPassword;
         private String mActualUsername;
         private String mActualPassword;
-        private Exception mException;
 
         FillExpectation(String username, String password) {
             this.mExpectedUsername = username;
             this.mExpectedPassword = password;
         }
+
+        void setUsername(String username) {
+            mActualUsername = username;
+            mUsernameLatch.countDown();
+        }
+
+        void setPassword(String password) {
+            mActualPassword = password;
+            mPasswordLatch.countDown();
+        }
+
+        void assertUsernameCalled() throws Exception {
+            assertCalled(mUsernameLatch, "username");
+            assertWithMessage("Wrong value for username").that(mExpectation.mActualUsername)
+                .isEqualTo(mExpectation.mExpectedUsername);
+        }
+
+        void assertPasswordCalled() throws Exception {
+            assertCalled(mPasswordLatch, "password");
+            assertWithMessage("Wrong value for password").that(mExpectation.mActualPassword)
+                    .isEqualTo(mExpectation.mExpectedPassword);
+        }
+
+        private void assertCalled(CountDownLatch latch, String field) throws Exception {
+            if (!latch.await(FILL_TIMEOUT.ms(), TimeUnit.MILLISECONDS)) {
+                throw new RetryableException(FILL_TIMEOUT, "%s not called", field);
+            }
+        }
+    }
+
+    private class JavascriptHandler {
+
+        @JavascriptInterface
+        public void onUsernameChanged(String username) {
+            Log.d(TAG, "onUsernameChanged():" + username);
+            if (mExpectation != null) {
+                mExpectation.setUsername(username);
+            }
+        }
+
+        @JavascriptInterface
+        public void onPasswordChanged(String password) {
+            Log.d(TAG, "onPasswordChanged():" + password);
+            if (mExpectation != null) {
+                mExpectation.setPassword(password);
+            }
+        }
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/OnCreateServiceStatusVerifierActivity.java b/tests/autofillservice/src/android/autofillservice/cts/OnCreateServiceStatusVerifierActivity.java
new file mode 100644
index 0000000..69bd868
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/OnCreateServiceStatusVerifierActivity.java
@@ -0,0 +1,61 @@
+/*
+ * 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.autofillservice.cts;
+
+import static android.autofillservice.cts.Helper.getAutofillServiceName;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * Activity used to verify whether the service is enable or not when it's launched.
+ */
+public class OnCreateServiceStatusVerifierActivity extends AbstractAutoFillActivity {
+
+    private static final String TAG = "OnCreateServiceStatusVerifierActivity";
+
+    static final String SERVICE_NAME = android.autofillservice.cts.NoOpAutofillService.SERVICE_NAME;
+
+    private String mSettingsOnCreate;
+    private boolean mEnabledOnCreate;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.simple_save_activity);
+
+        mSettingsOnCreate = getAutofillServiceName();
+        mEnabledOnCreate = getAutofillManager().isEnabled();
+        Log.i(TAG, "On create: settings=" + mSettingsOnCreate + ", enabled=" + mEnabledOnCreate);
+    }
+
+    void assertServiceStatusOnCreate(boolean enabled) {
+        if (enabled) {
+            assertWithMessage("Wrong settings").that(mSettingsOnCreate)
+                .isEqualTo(SERVICE_NAME);
+            assertWithMessage("AutofillManager.isEnabled() is wrong").that(mEnabledOnCreate)
+                .isTrue();
+
+        } else {
+            assertWithMessage("Wrong settings").that(mSettingsOnCreate).isNull();
+            assertWithMessage("AutofillManager.isEnabled() is wrong").that(mEnabledOnCreate)
+                .isFalse();
+        }
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/OptionalSaveActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/OptionalSaveActivityTest.java
index 5aaedde..ac48f28 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/OptionalSaveActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/OptionalSaveActivityTest.java
@@ -33,8 +33,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 /**
@@ -49,20 +47,22 @@
  * </ul>
  */
 @AppModeFull // Service-specific test
-public class OptionalSaveActivityTest extends AutoFillServiceTestCase {
+public class OptionalSaveActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<OptionalSaveActivity> {
 
     private static final boolean EXPECT_NO_SAVE_UI = false;
     private static final boolean EXPECT_SAVE_UI = false;
 
-    @Rule
-    public final AutofillActivityTestRule<OptionalSaveActivity> mActivityRule =
-        new AutofillActivityTestRule<OptionalSaveActivity>(OptionalSaveActivity.class);
-
     private OptionalSaveActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<OptionalSaveActivity> getActivityRule() {
+        return new AutofillActivityTestRule<OptionalSaveActivity>(OptionalSaveActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     /**
diff --git a/tests/autofillservice/src/android/autofillservice/cts/OutOfProcessLoginActivity.java b/tests/autofillservice/src/android/autofillservice/cts/OutOfProcessLoginActivity.java
index dadb3c9..a9974f1 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/OutOfProcessLoginActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/OutOfProcessLoginActivity.java
@@ -19,9 +19,10 @@
 import android.app.Activity;
 import android.content.Context;
 import android.os.Bundle;
+import android.util.Log;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import android.util.Log;
 
 import java.io.File;
 import java.io.IOException;
@@ -30,46 +31,50 @@
  * Simple activity showing R.layout.login_activity. Started outside of the test process.
  */
 public class OutOfProcessLoginActivity extends Activity {
-    private static final String LOG_TAG = OutOfProcessLoginActivity.class.getSimpleName();
+    private static final String TAG = "OutOfProcessLoginActivity";
+
+    private static OutOfProcessLoginActivity sInstance;
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
-        Log.i(LOG_TAG, "onCreate(" + savedInstanceState + ")");
+        Log.i(TAG, "onCreate(" + savedInstanceState + ")");
         super.onCreate(savedInstanceState);
 
         setContentView(R.layout.login_activity);
 
         findViewById(R.id.login).setOnClickListener((v) -> finish());
+
+        sInstance = this;
     }
 
     @Override
     protected void onStart() {
-        Log.i(LOG_TAG, "onStart()");
+        Log.i(TAG, "onStart()");
         super.onStart();
         try {
             if (!getStartedMarker(this).createNewFile()) {
-                Log.e(LOG_TAG, "cannot write started file");
+                Log.e(TAG, "cannot write started file");
             }
         } catch (IOException e) {
-            Log.e(LOG_TAG, "cannot write started file");
+            Log.e(TAG, "cannot write started file: " + e);
         }
     }
 
     @Override
     protected void onStop() {
-        Log.i(LOG_TAG, "onStop()");
+        Log.i(TAG, "onStop()");
         super.onStop();
 
         try {
             getStoppedMarker(this).createNewFile();
         } catch (IOException e) {
-            Log.e(LOG_TAG, "cannot write stopped filed");
+            Log.e(TAG, "cannot write stopped file: " + e);
         }
     }
 
     @Override
     protected void onDestroy() {
-        Log.i(LOG_TAG, "onDestroy()");
+        Log.i(TAG, "onDestroy()");
         super.onDestroy();
     }
 
@@ -92,4 +97,11 @@
     @NonNull public static File getStartedMarker(@NonNull Context context) {
         return new File(context.getFilesDir(), "started");
     }
+
+    public static void finishIt() {
+        Log.v(TAG, "Finishing " + sInstance);
+        if (sInstance != null) {
+            sInstance.finish();
+        }
+    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/OutOfProcessLoginActivityFinisherReceiver.java b/tests/autofillservice/src/android/autofillservice/cts/OutOfProcessLoginActivityFinisherReceiver.java
new file mode 100644
index 0000000..b75785e
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/OutOfProcessLoginActivityFinisherReceiver.java
@@ -0,0 +1,35 @@
+/*
+ * 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.autofillservice.cts;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+/**
+ * A {@link BroadcastReceiver} that finishes {@link OutOfProcessLoginActivity}.
+ */
+public class OutOfProcessLoginActivityFinisherReceiver extends BroadcastReceiver {
+
+    private static final String TAG = "OutOfProcessLoginActivityFinisherReceiver";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Log.i(TAG, "Goodbye, unfinished business!");
+        OutOfProcessLoginActivity.finishIt();
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
index d37bd16..767c56d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
@@ -50,52 +50,13 @@
 import android.platform.test.annotations.AppModeFull;
 import android.service.autofill.FillResponse;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
-import java.util.concurrent.TimeoutException;
-
 /**
  * Test case for an activity containing multiple partitions.
  */
 @AppModeFull // Service-specific test
-public class PartitionedActivityTest extends AutoFillServiceTestCase {
-
-    @Rule
-    public final AutofillActivityTestRule<GridActivity> mActivityRule =
-        new AutofillActivityTestRule<GridActivity>(GridActivity.class);
-
-    private GridActivity mActivity;
-
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
-    }
-
-    /**
-     * Focus to a cell and expect window event
-     */
-    void focusCell(int row, int column) throws TimeoutException {
-        mUiBot.waitForWindowChange(() -> mActivity.focusCell(row, column),
-                Timeouts.UI_TIMEOUT.getMaxValue());
-    }
-
-    /**
-     * Focus to a cell and expect no window event.
-     */
-    void focusCellNoWindowChange(int row, int column) {
-        try {
-            // TODO: define a small value in Timeout
-            mUiBot.waitForWindowChange(() -> mActivity.focusCell(row, column),
-                    Timeouts.UI_TIMEOUT.ms());
-        } catch (TimeoutException ex) {
-            // no window events! looking good
-            return;
-        }
-        throw new IllegalStateException(String.format("Expect no window event when focusing to"
-                + " column %d row %d, but event happened", row, column));
-    }
+public class PartitionedActivityTest extends AbstractGridActivityTestCase {
 
     @Test
     public void testAutofillTwoPartitionsSkipFirst() throws Exception {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PasswordOnlyActivity.java b/tests/autofillservice/src/android/autofillservice/cts/PasswordOnlyActivity.java
new file mode 100644
index 0000000..7a1d3d2
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/PasswordOnlyActivity.java
@@ -0,0 +1,68 @@
+/*
+ * 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.autofillservice.cts;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+
+public final class PasswordOnlyActivity extends AbstractAutoFillActivity {
+
+    private static final String TAG = "PasswordOnlyActivity";
+
+    static final String EXTRA_USERNAME = "username";
+
+    private TextView mWelcomeLabel;
+    private EditText mPasswordEditText;
+    private Button mLoginButton;
+    private String mUsername;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(getContentView());
+
+        mWelcomeLabel = findViewById(R.id.welcome);
+        mPasswordEditText = findViewById(R.id.password);
+        mLoginButton = findViewById(R.id.login);
+        mLoginButton.setOnClickListener((v) -> login());
+
+        mUsername = getIntent().getStringExtra(EXTRA_USERNAME);
+        final String welcomeMsg = "Welcome to the jungle, " + mUsername;
+        Log.v(TAG, welcomeMsg);
+        mWelcomeLabel.setText(welcomeMsg);
+    }
+
+    protected int getContentView() {
+        return R.layout.password_only_activity;
+    }
+
+    public void focusOnPassword() {
+        syncRunOnUiThread(() -> mPasswordEditText.requestFocus());
+    }
+
+    void setPassword(String password) {
+        syncRunOnUiThread(() -> mPasswordEditText.setText(password));
+    }
+
+    void login() {
+        final String password = mPasswordEditText.getText().toString();
+        Log.i(TAG, "Login as " + mUsername + "/" + password);
+        finish();
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java
index 7ca496b..c476722 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java
@@ -30,25 +30,25 @@
 import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
 import android.platform.test.annotations.AppModeFull;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 /**
  * Covers scenarios where the behavior is different because some fields were pre-filled.
  */
 @AppModeFull // LoginActivityTest is enough to test ephemeral apps support
-public class PreFilledLoginActivityTest extends AutoFillServiceTestCase {
-
-    @Rule
-    public final AutofillActivityTestRule<PreFilledLoginActivity> mActivityRule =
-            new AutofillActivityTestRule<PreFilledLoginActivity>(PreFilledLoginActivity.class);
+public class PreFilledLoginActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<PreFilledLoginActivity> {
 
     private PreFilledLoginActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<PreFilledLoginActivity> getActivityRule() {
+        return new AutofillActivityTestRule<PreFilledLoginActivity>(PreFilledLoginActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     @Test
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java
index b3257f0..244d72f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java
@@ -28,7 +28,6 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
-import android.content.Intent;
 import android.service.autofill.BatchUpdates;
 import android.service.autofill.CustomDescription;
 import android.service.autofill.RegexValidator;
@@ -38,31 +37,21 @@
 import android.view.View;
 import android.widget.RemoteViews;
 
-import org.junit.After;
-import org.junit.Rule;
-
 import java.util.regex.Pattern;
 
-public class PreSimpleSaveActivityTest extends CustomDescriptionWithLinkTestCase {
+public class PreSimpleSaveActivityTest
+        extends CustomDescriptionWithLinkTestCase<PreSimpleSaveActivity> {
 
-    @Rule
-    public final AutofillActivityTestRule<PreSimpleSaveActivity> mActivityRule =
+    private static final AutofillActivityTestRule<PreSimpleSaveActivity> sActivityRule =
             new AutofillActivityTestRule<PreSimpleSaveActivity>(PreSimpleSaveActivity.class, false);
 
-    private PreSimpleSaveActivity mActivity;
-
-    private void startActivity(boolean remainOnRecents) {
-        final Intent intent = new Intent(mContext, PreSimpleSaveActivity.class);
-        if (remainOnRecents) {
-            intent.setFlags(
-                    Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS | Intent.FLAG_ACTIVITY_NEW_TASK);
-        }
-        mActivity = mActivityRule.launchActivity(intent);
+    public PreSimpleSaveActivityTest() {
+        super(PreSimpleSaveActivity.class);
     }
 
-    @After
-    public void finishSimpleSaveActivity() {
-        SimpleSaveActivity.finishIt(mUiBot);
+    @Override
+    protected AutofillActivityTestRule<PreSimpleSaveActivity> getActivityRule() {
+        return sActivityRule;
     }
 
     @Override
@@ -81,7 +70,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mPreInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -110,7 +98,7 @@
                 break;
             case FINISH_ACTIVITY:
                 // ..then finishes it.
-                WelcomeActivity.finishIt(mUiBot);
+                WelcomeActivity.finishIt();
                 break;
             default:
                 throw new IllegalArgumentException("invalid type: " + type);
@@ -140,7 +128,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mPreInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -200,7 +187,6 @@
         }
 
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         newActivty.syncRunOnUiThread(() -> {
@@ -234,7 +220,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mPreInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -292,7 +277,6 @@
         // Trigger autofill.
         mActivity.getAutofillManager().requestAutofill(mActivity.mPreInput);
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -327,7 +311,6 @@
         newActivty.getAutofillManager().requestAutofill(newActivty.mInput);
 
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         newActivty.syncRunOnUiThread(() -> {
@@ -373,7 +356,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mPreInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/RetryRule.java b/tests/autofillservice/src/android/autofillservice/cts/RetryRule.java
index 1dbafeb..e190cda 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/RetryRule.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/RetryRule.java
@@ -30,12 +30,18 @@
     private static final String TAG = "RetryRule";
     private final int mMaxAttempts;
 
-    public RetryRule(int maxAttempts) {
-        if (maxAttempts < 2) {
-            throw new IllegalArgumentException(
-                    "Must retry at least once; otherwise, what's the point?");
+    /**
+     * 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 = maxAttempts;
+        mMaxAttempts = retries + 1;
     }
 
     @Override
@@ -44,6 +50,13 @@
 
             @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++) {
@@ -53,7 +66,7 @@
                             Log.v(TAG, "Good News, Everyone! " + name + " passed right away");
                         } else {
                             Log.d(TAG,
-                                    "Better late than never: " + name + "passed at attempt #" + i);
+                                    "Better late than never: " + name + " passed at attempt #" + i);
                         }
                         return;
                     } catch (RetryableException e) {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/RetryRuleTest.java b/tests/autofillservice/src/android/autofillservice/cts/RetryRuleTest.java
index 3d182ed..0c23f9f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/RetryRuleTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/RetryRuleTest.java
@@ -18,15 +18,22 @@
 
 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 android.platform.test.annotations.AppModeFull;
-import android.support.test.runner.AndroidJUnit4;
 
 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(AndroidJUnit4.class)
+@RunWith(MockitoJUnitRunner.class)
 @AppModeFull // Unit test
 public class RetryRuleTest {
 
@@ -59,9 +66,16 @@
         }
     }
 
+    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(2);
+        final RetryRule rule = new RetryRule(1);
         rule.apply(new RetryableStatement<RetryableException>(1, sRetryableException), mDescription)
                 .evaluate();
     }
@@ -70,22 +84,58 @@
     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(2);
+
+        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(2);
-        try {
-            rule.apply(new RetryableStatement<RetryableException>(2, sRetryableException),
-                    mDescription).evaluate();
-            throw new AssertionError("2ND CALL, Y U NO FAIL?");
-        } catch (RetryableException e) {
-            assertThat(e).isSameAs(sRetryableException);
-        }
+        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/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRule.java b/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRule.java
index bf36138..aefc0de 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRule.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRule.java
@@ -16,9 +16,10 @@
 
 package android.autofillservice.cts;
 
-import androidx.annotation.NonNull;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
@@ -55,7 +56,7 @@
     /**
      * Adds exceptions directly.
      *
-     * <p>Typically used when exceptions were caught asychronously during the test execution.
+     * <p>Typically used for exceptions caught asychronously during the test execution.
      */
     public SafeCleanerRule add(@NonNull Callable<List<Throwable>> exceptions) {
         mExtraThrowables.add(exceptions);
@@ -63,6 +64,17 @@
     }
 
     /**
+     * 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) {
@@ -79,8 +91,8 @@
                 try {
                     base.evaluate();
                 } catch (Throwable t) {
-                    Log.w(TAG, "Adding exception from main test");
-                    mThrowables.add(t);
+                    Log.w(TAG, "Adding exception from main test at index 0: " + t);
+                    mThrowables.add(0, t);
                 }
 
                 // Then the cleanup runners
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRuleTest.java b/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRuleTest.java
index f41b001..28af015 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRuleTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRuleTest.java
@@ -149,6 +149,21 @@
     }
 
     @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)
@@ -193,6 +208,7 @@
         final SafeCleanerRule rule = new SafeCleanerRule()
                 .run(mGoodGuyRunner1)
                 .add(mGoodGuyExtraExceptions1)
+                .add(mRuntimeException)
                 .add(() -> {
                     return ImmutableList.of(extra1, extra2);
                 })
@@ -212,7 +228,8 @@
                 SafeCleanerRule.MultipleExceptions.class,
                 () -> rule.apply(new FailureStatement(testException), mDescription).evaluate());
         assertThat(actualException.getThrowables())
-                .containsExactly(testException, error1, error2, extra1, extra2, extra3)
+                .containsExactly(testException, mRuntimeException, error1, error2, extra1, extra2,
+                        extra3)
                 .inOrder();
         verify(mGoodGuyRunner1).run();
         verify(mGoodGuyRunner2).run();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SaveInfoTest.java b/tests/autofillservice/src/android/autofillservice/cts/SaveInfoTest.java
index a13f4a0..1c009e9 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SaveInfoTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SaveInfoTest.java
@@ -16,6 +16,12 @@
 
 package android.autofillservice.cts;
 
+import static android.service.autofill.SaveInfo.FLAG_DELAY_SAVE;
+import static android.service.autofill.SaveInfo.FLAG_DONT_SAVE_ON_FINISH;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_GENERIC;
+
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Mockito.mock;
 import static org.testng.Assert.assertThrows;
 
@@ -33,61 +39,62 @@
 @AppModeFull // Unit test
 public class SaveInfoTest {
 
-    private  final AutofillId mId = new AutofillId(42);
+    private final AutofillId mId = new AutofillId(42);
+    private final AutofillId[] mIdArray = { mId };
     private final InternalSanitizer mSanitizer = mock(InternalSanitizer.class);
 
     @Test
     public void testRequiredIdsBuilder_null() {
         assertThrows(IllegalArgumentException.class,
-                () -> new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC, null));
+                () -> new SaveInfo.Builder(SAVE_DATA_TYPE_GENERIC, null));
     }
 
     @Test
     public void testRequiredIdsBuilder_empty() {
         assertThrows(IllegalArgumentException.class,
-                () -> new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC, new AutofillId[] {}));
+                () -> new SaveInfo.Builder(SAVE_DATA_TYPE_GENERIC, new AutofillId[] {}));
     }
 
     @Test
     public void testRequiredIdsBuilder_nullEntry() {
         assertThrows(IllegalArgumentException.class,
-                () -> new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC,
+                () -> new SaveInfo.Builder(SAVE_DATA_TYPE_GENERIC,
                         new AutofillId[] { null }));
     }
 
     @Test
     public void testBuild_noOptionalIds() {
-        final SaveInfo.Builder builder = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC);
+        final SaveInfo.Builder builder = new SaveInfo.Builder(SAVE_DATA_TYPE_GENERIC);
         assertThrows(IllegalStateException.class, ()-> builder.build());
     }
 
     @Test
     public void testSetOptionalIds_null() {
-        final SaveInfo.Builder builder = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC,
-                new AutofillId[] { mId });
+        final SaveInfo.Builder builder = new SaveInfo.Builder(SAVE_DATA_TYPE_GENERIC,
+                mIdArray);
         assertThrows(IllegalArgumentException.class, ()-> builder.setOptionalIds(null));
     }
 
     @Test
     public void testSetOptional_empty() {
-        final SaveInfo.Builder builder = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC,
-                new AutofillId[] { mId });
+        final SaveInfo.Builder builder = new SaveInfo.Builder(SAVE_DATA_TYPE_GENERIC,
+                mIdArray);
         assertThrows(IllegalArgumentException.class,
                 () -> builder.setOptionalIds(new AutofillId[] {}));
     }
 
     @Test
     public void testSetOptional_nullEntry() {
-        final SaveInfo.Builder builder = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC,
-                new AutofillId[] { mId });
+        final SaveInfo.Builder builder = new SaveInfo.Builder(SAVE_DATA_TYPE_GENERIC,
+                mIdArray);
         assertThrows(IllegalArgumentException.class,
                 () -> builder.setOptionalIds(new AutofillId[] { null }));
     }
 
     @Test
     public void testAddSanitizer_illegalArgs() {
-        final SaveInfo.Builder builder = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC,
-                new AutofillId[] { mId });
+        final SaveInfo.Builder builder = new SaveInfo.Builder(SAVE_DATA_TYPE_GENERIC,
+                mIdArray);
         // Null sanitizer
         assertThrows(IllegalArgumentException.class,
                 () -> builder.addSanitizer(null, mId));
@@ -107,10 +114,35 @@
 
     @Test
     public void testAddSanitizer_sameIdOnDifferentCalls() {
-        final SaveInfo.Builder builder = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC,
-                new AutofillId[] { mId });
+        final SaveInfo.Builder builder = new SaveInfo.Builder(SAVE_DATA_TYPE_GENERIC,
+                mIdArray);
         builder.addSanitizer(mSanitizer, mId);
         assertThrows(IllegalArgumentException.class, () -> builder.addSanitizer(mSanitizer, mId));
     }
 
+    @Test
+    public void testBuild_invalid() {
+        // No nothing
+        assertThrows(IllegalStateException.class, () -> new SaveInfo.Builder(SAVE_DATA_TYPE_GENERIC)
+                .build());
+        // Flag only, but invalid flag
+        assertThrows(IllegalStateException.class, () -> new SaveInfo.Builder(SAVE_DATA_TYPE_GENERIC)
+                .setFlags(FLAG_DONT_SAVE_ON_FINISH).build());
+    }
+
+    @Test
+    public void testBuild_valid() {
+        // Required ids
+        assertThat(new SaveInfo.Builder(SAVE_DATA_TYPE_GENERIC, mIdArray)
+                .build()).isNotNull();
+
+        // Optional ids
+        assertThat(new SaveInfo.Builder(SAVE_DATA_TYPE_GENERIC).setOptionalIds(mIdArray)
+                .build()).isNotNull();
+
+        // Delayed save
+        assertThat(new SaveInfo.Builder(SAVE_DATA_TYPE_GENERIC).setFlags(FLAG_DELAY_SAVE)
+                .build()).isNotNull();
+    }
+
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ServiceDisabledForSureTest.java b/tests/autofillservice/src/android/autofillservice/cts/ServiceDisabledForSureTest.java
new file mode 100644
index 0000000..a27a5bd
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/ServiceDisabledForSureTest.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 android.autofillservice.cts;
+
+import static android.autofillservice.cts.Helper.disableAutofillService;
+import static android.autofillservice.cts.Helper.enableAutofillService;
+import static android.autofillservice.cts.OnCreateServiceStatusVerifierActivity.SERVICE_NAME;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.util.Log;
+import android.view.autofill.AutofillManager;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Test case that guarantee the service is disabled before the activity launches.
+ */
+public class ServiceDisabledForSureTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<OnCreateServiceStatusVerifierActivity> {
+
+    private static final String TAG = "ServiceDisabledForSureTest";
+
+    private OnCreateServiceStatusVerifierActivity mActivity;
+
+    @BeforeClass
+    public static void resetService() {
+        disableAutofillService(sContext);
+    }
+
+    @Override
+    protected AutofillActivityTestRule<OnCreateServiceStatusVerifierActivity> getActivityRule() {
+        return new AutofillActivityTestRule<OnCreateServiceStatusVerifierActivity>(
+                OnCreateServiceStatusVerifierActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
+    }
+
+    @Override
+    protected void prepareServicePreTest() {
+        // Doesn't need to prepare the service - that was already taken care of in a @BeforeClass -
+        // but to guarantee the test finishes in the proper state
+        Log.v(TAG, "prepareServicePreTest(): not doing anything");
+        mSafeCleanerRule.run(() ->assertThat(mActivity.getAutofillManager().isEnabled()).isFalse());
+    }
+
+    @Test
+    public void testIsAutofillEnabled() throws Exception {
+        mActivity.assertServiceStatusOnCreate(false);
+
+        final AutofillManager afm = mActivity.getAutofillManager();
+        assertThat(afm.isEnabled()).isFalse();
+
+        enableAutofillService(mContext, SERVICE_NAME);
+        assertThat(afm.isEnabled()).isTrue();
+
+        disableAutofillService(mContext);
+        assertThat(afm.isEnabled()).isFalse();
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ServiceEnabledForSureTest.java b/tests/autofillservice/src/android/autofillservice/cts/ServiceEnabledForSureTest.java
new file mode 100644
index 0000000..24c2be0
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/ServiceEnabledForSureTest.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 android.autofillservice.cts;
+
+import static android.autofillservice.cts.Helper.disableAutofillService;
+import static android.autofillservice.cts.Helper.enableAutofillService;
+import static android.autofillservice.cts.OnCreateServiceStatusVerifierActivity.SERVICE_NAME;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.util.Log;
+import android.view.autofill.AutofillManager;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Test case that guarantee the service is enabled before the activity launches.
+ */
+public class ServiceEnabledForSureTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<OnCreateServiceStatusVerifierActivity> {
+
+    private static final String TAG = "ServiceEnabledForSureTest";
+
+    private OnCreateServiceStatusVerifierActivity mActivity;
+
+    @BeforeClass
+    public static void resetService() {
+        enableAutofillService(sContext, SERVICE_NAME);
+    }
+
+    @Override
+    protected AutofillActivityTestRule<OnCreateServiceStatusVerifierActivity> getActivityRule() {
+        return new AutofillActivityTestRule<OnCreateServiceStatusVerifierActivity>(
+                OnCreateServiceStatusVerifierActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
+    }
+
+    @Override
+    protected void prepareServicePreTest() {
+        // Doesn't need to prepare the service - that was already taken care of in a @BeforeClass -
+        // but to guarantee the test finishes in the proper state
+        Log.v(TAG, "prepareServicePreTest(): not doing anything");
+        mSafeCleanerRule.run(() ->assertThat(mActivity.getAutofillManager().isEnabled()).isTrue());
+    }
+
+    @Test
+    public void testIsAutofillEnabled() throws Exception {
+        mActivity.assertServiceStatusOnCreate(true);
+
+        final AutofillManager afm = mActivity.getAutofillManager();
+        assertThat(afm.isEnabled()).isTrue();
+
+        disableAutofillService(mContext);
+        assertThat(afm.isEnabled()).isFalse();
+
+        enableAutofillService(mContext, SERVICE_NAME);
+        assertThat(afm.isEnabled()).isTrue();
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java b/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
index 9b03d0a..6138cd7 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
@@ -55,7 +55,7 @@
  * Test the lifecycle of a autofill session
  */
 @AppModeFull // This test requires android.permission.WRITE_EXTERNAL_STORAGE
-public class SessionLifecycleTest extends AutoFillServiceTestCase {
+public class SessionLifecycleTest extends AutoFillServiceTestCase.ManualActivityLaunch {
     private static final String ID_BUTTON = "button";
     private static final String ID_CANCEL = "cancel";
 
@@ -66,7 +66,7 @@
     private static final long WAIT_ACTIVITY_MS = 1000;
 
     private static final Timeout SESSION_LIFECYCLE_TIMEOUT = new Timeout(
-            "SESSION_LIFECYCLE_TIMEOUT", 2000, 2F, 5000);
+            "SESSION_LIFECYCLE_TIMEOUT", 5000, 2F, 5000);
 
     /**
      * Runs an {@code assertion}, retrying until {@code timeout} is reached.
@@ -96,6 +96,13 @@
         Helper.allowAutoRotation();
     }
 
+    @After
+    public void finishLoginActivityOnAnotherProcess() throws Exception {
+        runShellCommand("am broadcast --receiver-foreground "
+                + "-n android.autofillservice.cts/.OutOfProcessLoginActivityFinisherReceiver");
+        mUiBot.assertGoneByRelativeId(ID_USERNAME, Timeouts.ACTIVITY_RESURRECTION);
+    }
+
     private void killOfProcessLoginActivityProcess() throws Exception {
         // Waiting for activity to stop (stop marker appears)
         eventually("getStoppedMarker()", () -> {
@@ -132,111 +139,118 @@
         // Set service.
         enableService();
 
-        // Start activity that is autofilled in a separate process so it can be killed
-        startAndWaitExternalActivity();
+        mUiBot.setScreenResolution();
 
-        // Set expectations.
-        final Bundle extras = new Bundle();
-        extras.putString("numbers", "4815162342");
+        try {
 
-        // Create the authentication intent (launching a full screen activity)
-        IntentSender authentication = PendingIntent.getActivity(getContext(), 0,
-                new Intent(getContext(), ManualAuthenticationActivity.class),
-                0).getIntentSender();
+            // Start activity that is autofilled in a separate process so it can be killed
+            startAndWaitExternalActivity();
 
-        // Prepare the authenticated response
-        ManualAuthenticationActivity.setResponse(new CannedFillResponse.Builder()
-                .addDataset(new CannedFillResponse.CannedDataset.Builder()
-                        .setField(ID_USERNAME, AutofillValue.forText("autofilled username"))
-                        .setPresentation(createPresentation("dataset")).build())
-                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
-                .setExtras(extras).build());
+            // Set expectations.
+            final Bundle extras = new Bundle();
+            extras.putString("numbers", "4815162342");
 
-        CannedFillResponse response = new CannedFillResponse.Builder()
-                .setAuthentication(authentication, ID_USERNAME, ID_PASSWORD)
-                .setPresentation(createPresentation("authenticate"))
-                .build();
-        sReplier.addResponse(response);
+            // Create the authentication intent (launching a full screen activity)
+            IntentSender authentication = PendingIntent.getActivity(getContext(), 0,
+                    new Intent(getContext(), ManualAuthenticationActivity.class),
+                    0).getIntentSender();
 
-        // Trigger autofill on username
-        mUiBot.selectByRelativeId(ID_USERNAME);
+            // Prepare the authenticated response
+            ManualAuthenticationActivity.setResponse(new CannedFillResponse.Builder()
+                    .addDataset(new CannedFillResponse.CannedDataset.Builder()
+                            .setField(ID_USERNAME, AutofillValue.forText("autofilled username"))
+                            .setPresentation(createPresentation("dataset")).build())
+                    .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
+                    .setExtras(extras).build());
 
-        // Wait for fill request to be processed
-        sReplier.getNextFillRequest();
+            CannedFillResponse response = new CannedFillResponse.Builder()
+                    .setAuthentication(authentication, ID_USERNAME, ID_PASSWORD)
+                    .setPresentation(createPresentation("authenticate"))
+                    .build();
+            sReplier.addResponse(response);
 
-        // Wait until authentication is shown
-        mUiBot.assertDatasets("authenticate");
+            // Trigger autofill on username
+            mUiBot.selectByRelativeId(ID_USERNAME);
 
-        // Change orientation which triggers a destroy -> create in the app as the activity
-        // cannot deal with such situations
-        mUiBot.setScreenOrientation(LANDSCAPE);
-        mUiBot.setScreenOrientation(PORTRAIT);
+            // Wait for fill request to be processed
+            sReplier.getNextFillRequest();
 
-        // Wait context and Views being recreated in rotation
-        mUiBot.assertShownByRelativeId(ID_USERNAME);
+            // Wait until authentication is shown
+            mUiBot.assertDatasets("authenticate");
 
-        // Delete stopped marker
-        getStoppedMarker(getContext()).delete();
+            // Change orientation which triggers a destroy -> create in the app as the activity
+            // cannot deal with such situations
+            mUiBot.setScreenOrientation(LANDSCAPE);
+            mUiBot.setScreenOrientation(PORTRAIT);
 
-        // Authenticate
-        mUiBot.selectDataset("authenticate");
+            // Wait context and Views being recreated in rotation
+            mUiBot.assertShownByRelativeId(ID_USERNAME);
 
-        // Kill activity that is in the background
-        killOfProcessLoginActivityProcess();
+            // Delete stopped marker
+            getStoppedMarker(getContext()).delete();
 
-        // Change orientation which triggers a destroy -> create in the app as the activity
-        // cannot deal with such situations
-        mUiBot.setScreenOrientation(PORTRAIT);
+            // Authenticate
+            mUiBot.selectDataset("authenticate");
 
-        // Approve authentication
-        mUiBot.selectByRelativeId(ID_BUTTON);
+            // Kill activity that is in the background
+            killOfProcessLoginActivityProcess();
 
-        // Wait for dataset to be shown
-        mUiBot.assertDatasets("dataset");
+            // Change orientation which triggers a destroy -> create in the app as the activity
+            // cannot deal with such situations
+            mUiBot.setScreenOrientation(PORTRAIT);
 
-        // Change orientation which triggers a destroy -> create in the app as the activity
-        // cannot deal with such situations
-        mUiBot.setScreenOrientation(LANDSCAPE);
+            // Approve authentication
+            mUiBot.selectByRelativeId(ID_BUTTON);
 
-        // Select dataset
-        mUiBot.selectDataset("dataset");
+            // Wait for dataset to be shown
+            mUiBot.assertDatasets("dataset");
 
-        // Check the results.
-        eventually("getTextById(" + ID_USERNAME + ")", () -> {
-            return mUiBot.getTextByRelativeId(ID_USERNAME).equals("autofilled username");
-        });
+            // Change orientation which triggers a destroy -> create in the app as the activity
+            // cannot deal with such situations
+            mUiBot.setScreenOrientation(LANDSCAPE);
 
-        // Set password
-        mUiBot.setTextByRelativeId(ID_PASSWORD, "new password");
+            // Select dataset
+            mUiBot.selectDataset("dataset");
 
-        // Login
-        mUiBot.selectByRelativeId(ID_LOGIN);
+            // Check the results.
+            eventually("getTextById(" + ID_USERNAME + ")", () -> {
+                return mUiBot.getTextByRelativeId(ID_USERNAME).equals("autofilled username");
+            });
 
-        // Wait for save UI to be shown
-        mUiBot.assertSaveShowing(SAVE_DATA_TYPE_PASSWORD);
+            // Set password
+            mUiBot.setTextByRelativeId(ID_PASSWORD, "new password");
 
-        // Change orientation to make sure save UI can handle this
-        mUiBot.setScreenOrientation(PORTRAIT);
+            // Login
+            mUiBot.selectByRelativeId(ID_LOGIN);
 
-        // Tap "Save".
-        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+            // Wait for save UI to be shown
+            mUiBot.assertSaveShowing(SAVE_DATA_TYPE_PASSWORD);
 
-        // Get save request
-        InstrumentedAutoFillService.SaveRequest saveRequest = sReplier.getNextSaveRequest();
-        assertWithMessage("onSave() not called").that(saveRequest).isNotNull();
+            // Change orientation to make sure save UI can handle this
+            mUiBot.setScreenOrientation(PORTRAIT);
 
-        // Make sure data is correctly saved
-        final AssistStructure.ViewNode username = findNodeByResourceId(saveRequest.structure,
-                ID_USERNAME);
-        assertTextAndValue(username, "autofilled username");
-        final AssistStructure.ViewNode password = findNodeByResourceId(saveRequest.structure,
-                ID_PASSWORD);
-        assertTextAndValue(password, "new password");
+            // Tap "Save".
+            mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
 
-        // Make sure extras were passed back on onSave()
-        assertThat(saveRequest.data).isNotNull();
-        final String extraValue = saveRequest.data.getString("numbers");
-        assertWithMessage("extras not passed on save").that(extraValue).isEqualTo("4815162342");
+            // Get save request
+            InstrumentedAutoFillService.SaveRequest saveRequest = sReplier.getNextSaveRequest();
+            assertWithMessage("onSave() not called").that(saveRequest).isNotNull();
+
+            // Make sure data is correctly saved
+            final AssistStructure.ViewNode username = findNodeByResourceId(saveRequest.structure,
+                    ID_USERNAME);
+            assertTextAndValue(username, "autofilled username");
+            final AssistStructure.ViewNode password = findNodeByResourceId(saveRequest.structure,
+                    ID_PASSWORD);
+            assertTextAndValue(password, "new password");
+
+            // Make sure extras were passed back on onSave()
+            assertThat(saveRequest.data).isNotNull();
+            final String extraValue = saveRequest.data.getString("numbers");
+            assertWithMessage("extras not passed on save").that(extraValue).isEqualTo("4815162342");
+        } finally {
+            mUiBot.resetScreenResolution();
+        }
     }
 
     @Test
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SettingsIntentTest.java b/tests/autofillservice/src/android/autofillservice/cts/SettingsIntentTest.java
index af582f6..9e471e1 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SettingsIntentTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SettingsIntentTest.java
@@ -27,25 +27,26 @@
 import android.support.test.uiautomator.UiObject2;
 
 import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 @AppModeFull // Service-specific test
-public class SettingsIntentTest extends AutoFillServiceTestCase {
+public class SettingsIntentTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<TrampolineForResultActivity> {
 
     private static final int MY_REQUEST_CODE = 42;
 
-    @Rule
-    public final AutofillActivityTestRule<TrampolineForResultActivity> mActivityRule =
-            new AutofillActivityTestRule<TrampolineForResultActivity>(
-                    TrampolineForResultActivity.class);
 
     protected TrampolineForResultActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<TrampolineForResultActivity> getActivityRule() {
+        return new AutofillActivityTestRule<TrampolineForResultActivity>(
+                TrampolineForResultActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     @After
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivity.java b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivity.java
index a9924f4..e292c3c 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivity.java
@@ -50,14 +50,6 @@
         return sInstance;
     }
 
-    static void finishIt(UiBot uiBot) {
-        if (sInstance != null) {
-            Log.d(TAG, "So long and thanks for all the fish!");
-            sInstance.finish();
-            uiBot.assertGoneByRelativeId(ID_LABEL, Timeouts.ACTIVITY_RESURRECTION);
-        }
-    }
-
     public SimpleSaveActivity() {
         sInstance = this;
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
index eb59da4..f043471 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
@@ -17,6 +17,7 @@
 
 import static android.autofillservice.cts.AntiTrimmerTextWatcher.TRIMMER_PATTERN;
 import static android.autofillservice.cts.Helper.ID_STATIC_TEXT;
+import static android.autofillservice.cts.Helper.LARGE_STRING;
 import static android.autofillservice.cts.Helper.assertTextAndValue;
 import static android.autofillservice.cts.Helper.assertTextValue;
 import static android.autofillservice.cts.Helper.findNodeByResourceId;
@@ -28,13 +29,17 @@
 import static android.autofillservice.cts.SimpleSaveActivity.TEXT_LABEL;
 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_GENERIC;
 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_USERNAME;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.junit.Assume.assumeTrue;
 
+import android.app.assist.AssistStructure;
+import android.app.assist.AssistStructure.ViewNode;
 import android.autofillservice.cts.CannedFillResponse.CannedDataset;
+import android.autofillservice.cts.InstrumentedAutoFillService.FillRequest;
 import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
 import android.autofillservice.cts.SimpleSaveActivity.FillExpectation;
 import android.content.Intent;
@@ -54,34 +59,32 @@
 import android.view.autofill.AutofillId;
 import android.widget.RemoteViews;
 
-import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
 
 import java.util.regex.Pattern;
 
-public class SimpleSaveActivityTest extends CustomDescriptionWithLinkTestCase {
+public class SimpleSaveActivityTest extends CustomDescriptionWithLinkTestCase<SimpleSaveActivity> {
 
-    @Rule
-    public final AutofillActivityTestRule<SimpleSaveActivity> mActivityRule =
+    private static final AutofillActivityTestRule<SimpleSaveActivity> sActivityRule =
             new AutofillActivityTestRule<SimpleSaveActivity>(SimpleSaveActivity.class, false);
 
-    @Rule
-    public final AutofillActivityTestRule<WelcomeActivity> mWelcomeActivityRule =
+    private static final AutofillActivityTestRule<WelcomeActivity> sWelcomeActivityRule =
             new AutofillActivityTestRule<WelcomeActivity>(WelcomeActivity.class, false);
 
-    private SimpleSaveActivity mActivity;
-
-    private void startActivity() {
-        startActivity(false);
+    public SimpleSaveActivityTest() {
+        super(SimpleSaveActivity.class);
     }
 
-    private void startActivity(boolean remainOnRecents) {
-        final Intent intent = new Intent(mContext, SimpleSaveActivity.class);
-        if (remainOnRecents) {
-            intent.setFlags(
-                    Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS | Intent.FLAG_ACTIVITY_NEW_TASK);
-        }
-        mActivity = mActivityRule.launchActivity(intent);
+    @Override
+    protected AutofillActivityTestRule<SimpleSaveActivity> getActivityRule() {
+        return sActivityRule;
+    }
+
+    @Override
+    protected TestRule getMainTestRule() {
+        return RuleChain.outerRule(sActivityRule).around(sWelcomeActivityRule);
     }
 
     private void restartActivity() {
@@ -133,6 +136,66 @@
         assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_PASSWORD), "PASS");
     }
 
+    @Test
+    public void testAutoFillOneDatasetAndSave_largeAssistStructure() throws Exception {
+        startActivity();
+
+        mActivity.syncRunOnUiThread(
+                () -> mActivity.mInput.setAutofillHints(LARGE_STRING, LARGE_STRING, LARGE_STRING));
+
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT, ID_PASSWORD)
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_INPUT, "id")
+                        .setField(ID_PASSWORD, "pass")
+                        .setPresentation(createPresentation("YO"))
+                        .build())
+                .build());
+
+        // Trigger autofill.
+        mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
+        final FillRequest fillRequest = sReplier.getNextFillRequest();
+        final ViewNode inputOnFill = findNodeByResourceId(fillRequest.structure, ID_INPUT);
+        final String[] hintsOnFill = inputOnFill.getAutofillHints();
+        // Cannot compare these large strings directly becauise it could cause ANR
+        assertThat(hintsOnFill).hasLength(3);
+        Helper.assertEqualsToLargeString(hintsOnFill[0]);
+        Helper.assertEqualsToLargeString(hintsOnFill[1]);
+        Helper.assertEqualsToLargeString(hintsOnFill[2]);
+
+        // Select dataset.
+        final FillExpectation autofillExpecation = mActivity.expectAutoFill("id", "pass");
+        mUiBot.selectDataset("YO");
+        autofillExpecation.assertAutoFilled();
+
+        mActivity.syncRunOnUiThread(() -> {
+            mActivity.mInput.setText("ID");
+            mActivity.mPassword.setText("PASS");
+            mActivity.mCommit.performClick();
+        });
+        final UiObject2 saveUi = mUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
+
+        // Save it...
+        mUiBot.saveForAutofill(saveUi, true);
+
+        // ... and assert results
+        final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+        final ViewNode inputOnSave = findNodeByResourceId(saveRequest.structure, ID_INPUT);
+        assertTextAndValue(inputOnSave, "ID");
+        assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_PASSWORD), "PASS");
+
+        final String[] hintsOnSave = inputOnSave.getAutofillHints();
+        // Cannot compare these large strings directly becauise it could cause ANR
+        assertThat(hintsOnSave).hasLength(3);
+        Helper.assertEqualsToLargeString(hintsOnSave[0]);
+        Helper.assertEqualsToLargeString(hintsOnSave[1]);
+        Helper.assertEqualsToLargeString(hintsOnSave[2]);
+    }
+
     /**
      * Simple test that only uses UiAutomator to interact with the activity, so it indirectly
      * tests the integration of Autofill with Accessibility.
@@ -198,8 +261,12 @@
         try {
             saveTest(true);
         } finally {
-            mUiBot.setScreenOrientation(UiBot.PORTRAIT);
-            cleanUpAfterScreenOrientationIsBackToPortrait();
+            try {
+                mUiBot.setScreenOrientation(UiBot.PORTRAIT);
+                cleanUpAfterScreenOrientationIsBackToPortrait();
+            } catch (Exception e) {
+                mSafeCleanerRule.add(e);
+            }
         }
     }
 
@@ -217,7 +284,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -242,6 +308,125 @@
         assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_INPUT), "108");
     }
 
+    /**
+     * Emulates an app dyanmically adding the password field after username is typed.
+     */
+    @Test
+    @AppModeFull // testAutoFillOneDatasetAndSave() is enough to test ephemeral apps support
+    public void testPartitionedSave() throws Exception {
+        startActivity();
+
+        // Set service.
+        enableService();
+
+        // 1st request
+
+        // Set expectations.
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setRequiredSavableIds(SAVE_DATA_TYPE_USERNAME, ID_INPUT)
+                .build());
+
+        // Trigger autofill.
+        mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
+        sReplier.getNextFillRequest();
+
+        // Set 1st field but don't commit session
+        mActivity.syncRunOnUiThread(() -> mActivity.mInput.setText("108"));
+        mUiBot.assertSaveNotShowing();
+
+        // 2nd request
+
+        // Set expectations.
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setRequiredSavableIds(SAVE_DATA_TYPE_USERNAME | SAVE_DATA_TYPE_PASSWORD,
+                        ID_INPUT, ID_PASSWORD)
+                .build());
+
+        // Trigger autofill.
+        mActivity.syncRunOnUiThread(() -> mActivity.mPassword.requestFocus());
+        sReplier.getNextFillRequest();
+
+        // Trigger save.
+        mActivity.syncRunOnUiThread(() -> {
+            mActivity.mPassword.setText("42");
+            mActivity.mCommit.performClick();
+        });
+        final UiObject2 saveUi = mUiBot.assertSaveShowing(null, SAVE_DATA_TYPE_USERNAME,
+                SAVE_DATA_TYPE_PASSWORD);
+
+        // Save it...
+        mUiBot.saveForAutofill(saveUi, true);
+
+        // ... and assert results
+        final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+        assertThat(saveRequest.contexts.size()).isEqualTo(2);
+
+        assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_INPUT), "108");
+        assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_PASSWORD), "42");
+    }
+
+    /**
+     * Emulates an app using fragments to display username and password in 2 steps.
+     */
+    @Test
+    @AppModeFull // testAutoFillOneDatasetAndSave() is enough to test ephemeral apps support
+    public void testDelayedSave() throws Exception {
+        startActivity();
+
+        // Set service.
+        enableService();
+
+        // 1st fragment.
+
+        // Set expectations.
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setSaveInfoFlags(SaveInfo.FLAG_DELAY_SAVE).build());
+
+        // Trigger autofill.
+        mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
+        sReplier.getNextFillRequest();
+
+        // Trigger delayed save.
+        mActivity.syncRunOnUiThread(() -> {
+            mActivity.mInput.setText("108");
+            mActivity.mCommit.performClick();
+        });
+        mUiBot.assertSaveNotShowing();
+
+        // 2nd fragment.
+
+        // Set expectations.
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setRequiredSavableIds(SAVE_DATA_TYPE_USERNAME | SAVE_DATA_TYPE_PASSWORD,
+                        ID_PASSWORD)
+                .build());
+
+        // Trigger autofill on second "fragment"
+        mActivity.syncRunOnUiThread(() -> mActivity.mPassword.requestFocus());
+        sReplier.getNextFillRequest();
+
+        // Trigger delayed save.
+        mActivity.syncRunOnUiThread(() -> {
+            mActivity.mPassword.setText("42");
+            mActivity.mCommit.performClick();
+        });
+
+        // Save it...
+        mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_USERNAME, SAVE_DATA_TYPE_PASSWORD);
+
+        // ... and assert results
+        final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+        assertThat(saveRequest.contexts.size()).isEqualTo(2);
+
+        // Get username from 1st request.
+        final AssistStructure structure1 = saveRequest.contexts.get(0).getStructure();
+        assertTextAndValue(findNodeByResourceId(structure1, ID_INPUT), "108");
+
+        // Get password from 2nd request.
+        final AssistStructure structure2 = saveRequest.contexts.get(1).getStructure();
+        assertTextAndValue(findNodeByResourceId(structure2, ID_PASSWORD), "42");
+    }
+
     @Test
     public void testSave_launchIntent() throws Exception {
         startActivity();
@@ -250,25 +435,29 @@
         enableService();
 
         // Set expectations.
-        sReplier.setOnSave(WelcomeActivity.createSender(mContext, "Saved by the bell"));
-        sReplier.addResponse(new CannedFillResponse.Builder()
-                .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
-                .build());
+        sReplier.setOnSave(WelcomeActivity.createSender(mContext, "Saved by the bell"))
+                .addResponse(new CannedFillResponse.Builder()
+                        .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
+                        .build());
 
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
             mActivity.mInput.setText("108");
             mActivity.mCommit.performClick();
+
+            // Disable autofill so it's not triggered again after WelcomeActivity finishes
+            // and mActivity is resumed (with focus on mInput) after the session is closed
+            mActivity.mInput.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO);
         });
 
         // Save it...
         mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
         sReplier.getNextSaveRequest();
+
         // ... and assert activity was launched
         WelcomeActivity.assertShowing(mUiBot, "Saved by the bell");
     }
@@ -288,16 +477,23 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
-        // Trigger save and start a new session right away.
+        // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
             mActivity.mInput.setText("108");
             mActivity.mCommit.performClick();
-            mActivity.getAutofillManager().requestAutofill(mActivity.mInput);
         });
 
-        // Make sure Save UI for 1st session was canceled....
+        // Make sure Save UI for 1st session was shown....
+        mUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
+
+        // ...then start the new session right away (without finishing the activity).
+        sReplier.addResponse(CannedFillResponse.NO_RESPONSE);
+        mActivity.syncRunOnUiThread(
+                () -> mActivity.getAutofillManager().requestAutofill(mActivity.mInput));
+        sReplier.getNextFillRequest();
+
+        // Make sure Save UI for 1st session was canceled.
         mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
     }
 
@@ -371,7 +567,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Cancel session.
         mActivity.getAutofillManager().cancel();
@@ -422,7 +617,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -559,7 +753,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -590,7 +783,7 @@
                 break;
             case FINISH_ACTIVITY:
                 // ..then finishes it.
-                WelcomeActivity.finishIt(mUiBot);
+                WelcomeActivity.finishIt();
                 break;
             default:
                 throw new IllegalArgumentException("invalid type: " + type);
@@ -628,7 +821,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -683,7 +875,6 @@
         }
 
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -715,7 +906,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -829,7 +1019,6 @@
         // Trigger autofill.
         mActivity.getAutofillManager().requestAutofill(mActivity.mInput);
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -891,7 +1080,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -1008,6 +1196,7 @@
 
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
+        sReplier.getNextFillRequest();
 
         mActivity.syncRunOnUiThread(() -> {
             mActivity.mInput.setText("id");
@@ -1162,7 +1351,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.setText("108"));
@@ -1223,7 +1411,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
             mActivity.mInput.setText("108");
diff --git a/tests/autofillservice/src/android/autofillservice/cts/TextValueSanitizerTest.java b/tests/autofillservice/src/android/autofillservice/cts/TextValueSanitizerTest.java
index d08c7e7..2b6d5c6 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/TextValueSanitizerTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/TextValueSanitizerTest.java
@@ -93,4 +93,38 @@
         assertThat(sanitizer.sanitize(AutofillValue.forText("  42 ")).getTextValue())
                 .isEqualTo("42");
     }
+
+    @Test
+    public void testSanitize_groupSubstitutionMatch_withOptionalGroup() {
+        final TextValueSanitizer sanitizer =
+                new TextValueSanitizer(Pattern.compile("(\\d*)\\s?(\\d*)?"), "$1$2");
+        assertThat(sanitizer.sanitize(AutofillValue.forText("42 108")).getTextValue())
+                .isEqualTo("42108");
+        assertThat(sanitizer.sanitize(AutofillValue.forText("42108")).getTextValue())
+                .isEqualTo("42108");
+        assertThat(sanitizer.sanitize(AutofillValue.forText("42")).getTextValue())
+                .isEqualTo("42");
+        final TextValueSanitizer ccSanitizer = new TextValueSanitizer(Pattern.compile(
+                "^(\\d{4,5})-?\\s?(\\d{4,6})-?\\s?(\\d{4,5})" // first 3 are required
+                        + "-?\\s?((?:\\d{4,5})?)-?\\s?((?:\\d{3,5})?)$"), // last 2 are optional
+                "$1$2$3$4$5");
+        assertThat(ccSanitizer.sanitize(AutofillValue
+                .forText("1111 2222 3333 4444 5555")).getTextValue())
+                        .isEqualTo("11112222333344445555");
+        assertThat(ccSanitizer.sanitize(AutofillValue
+                .forText("11111-222222-33333-44444-55555")).getTextValue())
+                        .isEqualTo("11111222222333334444455555");
+        assertThat(ccSanitizer.sanitize(AutofillValue
+                .forText("1111 2222 3333 4444")).getTextValue())
+                        .isEqualTo("1111222233334444");
+        assertThat(ccSanitizer.sanitize(AutofillValue
+                .forText("11111-222222-33333-44444-")).getTextValue())
+                        .isEqualTo("111112222223333344444");
+        assertThat(ccSanitizer.sanitize(AutofillValue
+                .forText("1111 2222 3333")).getTextValue())
+                        .isEqualTo("111122223333");
+        assertThat(ccSanitizer.sanitize(AutofillValue
+                .forText("11111-222222-33333 ")).getTextValue())
+                        .isEqualTo("1111122222233333");
+    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/TimePickerClockActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/TimePickerClockActivityTest.java
index 903011f..c7f34800 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/TimePickerClockActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/TimePickerClockActivityTest.java
@@ -17,17 +17,17 @@
 
 import android.platform.test.annotations.AppModeFull;
 
-import org.junit.Rule;
-
 @AppModeFull // Unit test
 public class TimePickerClockActivityTest extends TimePickerTestCase<TimePickerClockActivity> {
 
-    @Rule
-    public final AutofillActivityTestRule<TimePickerClockActivity> mActivityRule =
-        new AutofillActivityTestRule<TimePickerClockActivity>(TimePickerClockActivity.class);
-
     @Override
-    protected TimePickerClockActivity getTimePickerActivity() {
-        return mActivityRule.getActivity();
+    protected AutofillActivityTestRule<TimePickerClockActivity> getActivityRule() {
+        return new AutofillActivityTestRule<TimePickerClockActivity>(
+                TimePickerClockActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/TimePickerSpinnerActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/TimePickerSpinnerActivityTest.java
index 07e4592..a6ac44f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/TimePickerSpinnerActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/TimePickerSpinnerActivityTest.java
@@ -17,17 +17,17 @@
 
 import android.platform.test.annotations.AppModeFull;
 
-import org.junit.Rule;
-
 @AppModeFull // Unit test
 public class TimePickerSpinnerActivityTest extends TimePickerTestCase<TimePickerSpinnerActivity> {
 
-    @Rule
-    public final AutofillActivityTestRule<TimePickerSpinnerActivity> mActivityRule =
-        new AutofillActivityTestRule<TimePickerSpinnerActivity>(TimePickerSpinnerActivity.class);
-
     @Override
-    protected TimePickerSpinnerActivity getTimePickerActivity() {
-        return mActivityRule.getActivity();
+    protected AutofillActivityTestRule<TimePickerSpinnerActivity> getActivityRule() {
+        return new AutofillActivityTestRule<TimePickerSpinnerActivity>(
+                TimePickerSpinnerActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java
index 4bd8cf0..22abcd3 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java
@@ -36,14 +36,14 @@
 /**
  * Base class for {@link AbstractTimePickerActivity} tests.
  */
-abstract class TimePickerTestCase<T extends AbstractTimePickerActivity>
-        extends AutoFillServiceTestCase {
+abstract class TimePickerTestCase<A extends AbstractTimePickerActivity>
+        extends AutoFillServiceTestCase.AutoActivityLaunch<A> {
 
-    protected abstract T getTimePickerActivity();
+    protected A mActivity;
 
     @Test
     public void testAutoFillAndSave() throws Exception {
-        final T activity = getTimePickerActivity();
+        assertWithMessage("subclass did not set mActivity").that(mActivity).isNotNull();
 
         // Set service.
         enableService();
@@ -62,10 +62,10 @@
                 .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_OUTPUT, ID_TIME_PICKER)
                 .build());
 
-        activity.expectAutoFill("4:20", 4, 20);
+        mActivity.expectAutoFill("4:20", 4, 20);
 
         // Trigger auto-fill.
-        activity.onOutput((v) -> v.requestFocus());
+        mActivity.onOutput((v) -> v.requestFocus());
         final FillRequest fillRequest = sReplier.getNextFillRequest();
 
         // Assert properties of TimePicker field.
@@ -75,11 +75,11 @@
         mUiBot.selectDataset("Adventure Time");
 
         // Check the results.
-        activity.assertAutoFilled();
+        mActivity.assertAutoFilled();
 
         // Trigger save.
-        activity.setTime(10, 40);
-        activity.tapOk();
+        mActivity.setTime(10, 40);
+        mActivity.tapOk();
 
         mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Timeout.java b/tests/autofillservice/src/android/autofillservice/cts/Timeout.java
index 543a4d2..261cbab 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Timeout.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Timeout.java
@@ -16,10 +16,11 @@
 package android.autofillservice.cts;
 
 import android.os.SystemClock;
-import androidx.annotation.NonNull;
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+
 import java.util.concurrent.Callable;
 
 /**
@@ -151,9 +152,9 @@
         if (retryMs < 1) {
             throw new IllegalArgumentException("need to sleep at least 1ms, right?");
         }
-        long startTime = System.currentTimeMillis();
+        long startTime = SystemClock.elapsedRealtime();
         int attempt = 0;
-        while (System.currentTimeMillis() - startTime <= mCurrentValue) {
+        while (SystemClock.elapsedRealtime() - startTime <= mCurrentValue) {
             final T result = job.call();
             if (result != null) {
                 // Good news, everyone: job succeeded on first attempt!
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Timeouts.java b/tests/autofillservice/src/android/autofillservice/cts/Timeouts.java
index d59d1a5..9d367c6 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Timeouts.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Timeouts.java
@@ -21,48 +21,76 @@
  */
 final class Timeouts {
 
+    private static final long ONE_TIMEOUT_TO_RULE_THEN_ALL_MS = 20_000;
+    private static final long ONE_NAPTIME_TO_RULE_THEN_ALL_MS = 2_000;
+
     /**
      * Timeout until framework binds / unbinds from service.
      */
-    static final Timeout CONNECTION_TIMEOUT = new Timeout("CONNECTION_TIMEOUT", 5000, 2F, 10000);
+    static final Timeout CONNECTION_TIMEOUT = new Timeout("CONNECTION_TIMEOUT",
+            ONE_TIMEOUT_TO_RULE_THEN_ALL_MS, 2F, ONE_TIMEOUT_TO_RULE_THEN_ALL_MS);
+
+    /**
+     * Timeout for {@link MyAutofillCallback#assertNotCalled()} - test will sleep for that amount of
+     * time as there is no callback that be received to assert it's not shown.
+     */
+    static final long CALLBACK_NOT_CALLED_TIMEOUT_MS = ONE_NAPTIME_TO_RULE_THEN_ALL_MS;
 
     /**
      * Timeout until framework unbinds from a service.
      */
     // TODO: must be higher than RemoteFillService.TIMEOUT_IDLE_BIND_MILLIS, so we should use a
     // @hidden @Testing constants instead...
-    static final Timeout IDLE_UNBIND_TIMEOUT = new Timeout("IDLE_UNBIND_TIMEOUT", 10000, 2F, 10000);
+    static final Timeout IDLE_UNBIND_TIMEOUT = new Timeout("IDLE_UNBIND_TIMEOUT",
+            ONE_TIMEOUT_TO_RULE_THEN_ALL_MS, 2F, ONE_TIMEOUT_TO_RULE_THEN_ALL_MS);
 
     /**
      * Timeout to get the expected number of fill events.
      */
-    static final Timeout FILL_EVENTS_TIMEOUT = new Timeout("FILL_EVENTS_TIMEOUT", 5000, 2F, 10000);
+    static final Timeout FILL_EVENTS_TIMEOUT = new Timeout("FILL_EVENTS_TIMEOUT",
+            ONE_TIMEOUT_TO_RULE_THEN_ALL_MS, 2F, ONE_TIMEOUT_TO_RULE_THEN_ALL_MS);
 
     /**
      * Timeout for expected autofill requests.
      */
-    static final Timeout FILL_TIMEOUT = new Timeout("FILL_TIMEOUT", 5000, 2F, 10000);
+    static final Timeout FILL_TIMEOUT = new Timeout("FILL_TIMEOUT", ONE_TIMEOUT_TO_RULE_THEN_ALL_MS,
+            2F, ONE_TIMEOUT_TO_RULE_THEN_ALL_MS);
 
     /**
      * Timeout for expected save requests.
      */
-    static final Timeout SAVE_TIMEOUT = new Timeout("SAVE_TIMEOUT", 5000, 2F, 10000);
+    static final Timeout SAVE_TIMEOUT = new Timeout("SAVE_TIMEOUT", ONE_TIMEOUT_TO_RULE_THEN_ALL_MS,
+            2F, ONE_TIMEOUT_TO_RULE_THEN_ALL_MS);
 
     /**
      * Timeout used when save is not expected to be shown - test will sleep for that amount of time
      * as there is no callback that be received to assert it's not shown.
      */
-    static final long SAVE_NOT_SHOWN_NAPTIME_MS = 5000;
+    static final long SAVE_NOT_SHOWN_NAPTIME_MS = ONE_NAPTIME_TO_RULE_THEN_ALL_MS;
 
     /**
      * Timeout for UI operations. Typically used by {@link UiBot}.
      */
-    static final Timeout UI_TIMEOUT = new Timeout("UI_TIMEOUT", 5000, 2F, 10000);
+    static final Timeout UI_TIMEOUT = new Timeout("UI_TIMEOUT", ONE_TIMEOUT_TO_RULE_THEN_ALL_MS, 2F,
+            ONE_TIMEOUT_TO_RULE_THEN_ALL_MS);
+
+    /**
+     * Timeout for a11y window change events.
+     */
+    static final long WINDOW_CHANGE_TIMEOUT_MS = ONE_TIMEOUT_TO_RULE_THEN_ALL_MS;
+
+    /**
+     * Timeout used when an a11y window change events is not expected to be generated - test will
+     * sleep for that amount of time as there is no callback that be received to assert it's not
+     * shown.
+     */
+    static final long WINDOW_CHANGE_NOT_GENERATED_NAPTIME_MS = ONE_NAPTIME_TO_RULE_THEN_ALL_MS;
 
     /**
      * Timeout for webview operations. Typically used by {@link UiBot}.
      */
-    static final Timeout WEBVIEW_TIMEOUT = new Timeout("WEBVIEW_TIMEOUT", 8000, 2F, 16000);
+    static final Timeout WEBVIEW_TIMEOUT = new Timeout("WEBVIEW_TIMEOUT",
+            ONE_TIMEOUT_TO_RULE_THEN_ALL_MS, 2F, ONE_TIMEOUT_TO_RULE_THEN_ALL_MS);
 
     /**
      * Timeout for showing the autofill dataset picker UI.
@@ -72,26 +100,27 @@
      *
      * <p>Typically used by {@link UiBot}.
      */
-    static final Timeout UI_DATASET_PICKER_TIMEOUT =
-            new Timeout("UI_DATASET_PICKER_TIMEOUT", 5000, 2F, 10000);
+    static final Timeout UI_DATASET_PICKER_TIMEOUT = new Timeout("UI_DATASET_PICKER_TIMEOUT",
+            ONE_TIMEOUT_TO_RULE_THEN_ALL_MS, 2F, ONE_TIMEOUT_TO_RULE_THEN_ALL_MS);
 
     /**
      * Timeout used when the dataset picker is not expected to be shown - test will sleep for that
      * amount of time as there is no callback that be received to assert it's not shown.
      */
-    static final long DATASET_PICKER_NOT_SHOWN_NAPTIME_MS = 5000;
+    static final long DATASET_PICKER_NOT_SHOWN_NAPTIME_MS = ONE_NAPTIME_TO_RULE_THEN_ALL_MS;
 
     /**
      * Timeout (in milliseconds) for an activity to be brought out to top.
      */
-    static final Timeout ACTIVITY_RESURRECTION =
-            new Timeout("ACTIVITY_RESURRECTION", 6000, 3F, 20000);
+    static final Timeout ACTIVITY_RESURRECTION = new Timeout("ACTIVITY_RESURRECTION",
+            ONE_TIMEOUT_TO_RULE_THEN_ALL_MS, 2F, ONE_TIMEOUT_TO_RULE_THEN_ALL_MS);
 
     /**
      * Timeout for changing the screen orientation.
      */
-    static final Timeout UI_SCREEN_ORIENTATION_TIMEOUT =
-            new Timeout("UI_SCREEN_ORIENTATION_TIMEOUT", 5000, 2F, 10000);
+    static final Timeout UI_SCREEN_ORIENTATION_TIMEOUT = new Timeout(
+            "UI_SCREEN_ORIENTATION_TIMEOUT", ONE_TIMEOUT_TO_RULE_THEN_ALL_MS, 2F,
+            ONE_TIMEOUT_TO_RULE_THEN_ALL_MS);
 
     private Timeouts() {
         throw new UnsupportedOperationException("contain static methods only");
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index c7cca28..f508bc9 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -22,6 +22,7 @@
 import static android.autofillservice.cts.Timeouts.UI_DATASET_PICKER_TIMEOUT;
 import static android.autofillservice.cts.Timeouts.UI_SCREEN_ORIENTATION_TIMEOUT;
 import static android.autofillservice.cts.Timeouts.UI_TIMEOUT;
+import static android.autofillservice.cts.common.ShellHelper.runShellCommand;
 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_ADDRESS;
 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD;
 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS;
@@ -32,6 +33,8 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.junit.Assume.assumeTrue;
+
 import android.app.Instrumentation;
 import android.app.UiAutomation;
 import android.content.Context;
@@ -53,8 +56,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
+import java.io.File;
+import java.io.FileInputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -128,12 +131,52 @@
         mAutoman = instrumentation.getUiAutomation();
     }
 
+    void waitForIdle() {
+        final long before = SystemClock.elapsedRealtimeNanos();
+        mDevice.waitForIdle();
+        final float delta = ((float) (SystemClock.elapsedRealtimeNanos() - before)) / 1_000_000;
+        Log.v(TAG, "device idle in " + delta + "ms");
+    }
+
     void reset() {
         mOkToCallAssertNoDatasets = false;
     }
 
-    UiDevice getDevice() {
-        return mDevice;
+    /**
+     * Assumes the device has a minimum height and width of {@code minSize}, throwing a
+     * {@code AssumptionViolatedException} if it doesn't (so the test is skiped by the JUnit
+     * Runner).
+     */
+    void assumeMinimumResolution(int minSize) {
+        final int width = mDevice.getDisplayWidth();
+        final int heigth = mDevice.getDisplayHeight();
+        final int min = Math.min(width, heigth);
+        assumeTrue("Screen size is too small (" + width + "x" + heigth + ")", min >= minSize);
+        Log.d(TAG, "assumeMinimumResolution(" + minSize + ") passed: screen size is "
+                + width + "x" + heigth);
+    }
+
+    /**
+     * Sets the screen resolution in a way that the IME doesn't interfere with the Autofill UI
+     * when the device is rotated to landscape.
+     *
+     * When called, test must call <p>{@link #resetScreenResolution()} in a {@code finally} block.
+     */
+    void setScreenResolution() {
+        assumeMinimumResolution(500);
+
+        runShellCommand("wm size 1080x1920");
+        runShellCommand("wm density 420");
+    }
+
+    /**
+     * Resets the screen resolution.
+     *
+     * <p>Should always be called after {@link #setScreenResolution()}.
+     */
+    void resetScreenResolution() {
+        runShellCommand("wm density reset");
+        runShellCommand("wm size reset");
     }
 
     /**
@@ -312,7 +355,7 @@
      */
     public void assertNotShowingForSure(String text) throws Exception {
         final UiObject2 object = mDevice.findObject(By.text(text));
-        assertWithMessage("Find node with text '%s'", text).that(object).isNull();
+        assertWithMessage("Found node with text '%s'", text).that(object).isNull();
     }
 
     /**
@@ -455,6 +498,10 @@
         assertNeverShown("save UI for type " + type, SAVE_UI_SELECTOR, SAVE_NOT_SHOWN_NAPTIME_MS);
     }
 
+    void assertSaveNotShowing() throws Exception {
+        assertNeverShown("save UI", SAVE_UI_SELECTOR, SAVE_NOT_SHOWN_NAPTIME_MS);
+    }
+
     private String getSaveTypeString(int type) {
         final String typeResourceName;
         switch (type) {
@@ -677,13 +724,15 @@
             });
         } catch (RetryableException e) {
             if (dumpOnError) {
-                dumpScreen("waitForObject() for " + selector + "failed");
+                dumpScreen("waitForObject() for " + selector + "on "
+                        + (parent == null ? "mDevice" : parent) + " failed");
             }
             throw e;
         }
     }
 
-    private UiObject2 waitForObject(UiObject2 parent, BySelector selector, Timeout timeout)
+    private UiObject2 waitForObject(@Nullable UiObject2 parent, @NonNull BySelector selector,
+            @NonNull Timeout timeout)
             throws Exception {
         return waitForObject(parent, selector, timeout, DUMP_ON_ERROR);
     }
@@ -694,18 +743,18 @@
      * @param selector {@link BySelector} that identifies the object.
      * @param timeout timeout in ms
      */
-    private UiObject2 waitForObject(BySelector selector, Timeout timeout) throws Exception {
+    private UiObject2 waitForObject(@NonNull BySelector selector, @NonNull Timeout timeout)
+            throws Exception {
         return waitForObject(null, selector, timeout);
     }
 
     /**
-     * Execute a Runnable and wait for TYPE_WINDOWS_CHANGED or TYPE_WINDOW_STATE_CHANGED.
-     * TODO: No longer need Retry, Refactoring the Timeout (e.g. we probably need two values:
-     * one large timeout value that expects window event, one small value that expect no window
-     * event)
+     * Execute a Runnable and wait for {@link AccessibilityEvent#TYPE_WINDOWS_CHANGED} or
+     * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}.
      */
-    public void waitForWindowChange(Runnable runnable, long timeoutMillis) throws TimeoutException {
-        mAutoman.executeAndWaitForEvent(runnable, (AccessibilityEvent event) -> {
+    public AccessibilityEvent waitForWindowChange(Runnable runnable, long timeoutMillis)
+            throws TimeoutException {
+        return mAutoman.executeAndWaitForEvent(runnable, (AccessibilityEvent event) -> {
             switch (event.getEventType()) {
                 case AccessibilityEvent.TYPE_WINDOWS_CHANGED:
                 case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
@@ -715,6 +764,10 @@
         }, timeoutMillis);
     }
 
+    public AccessibilityEvent waitForWindowChange(Runnable runnable) throws TimeoutException {
+        return waitForWindowChange(runnable, Timeouts.WINDOW_CHANGE_TIMEOUT_MS);
+    }
+
     /**
      * Waits for and returns a list of objects.
      *
@@ -792,25 +845,21 @@
     }
 
     /**
-     * Dumps the current view hierarchy int the output stream.
+     * Dumps the current view hierarchy and take a screenshot and save both locally so they can be
+     * inspected later.
      */
-    public void dumpScreen(String cause) {
-        new Exception("dumpScreen(cause=" + cause + ") stacktrace").printStackTrace(System.out);
+    public void dumpScreen(@NonNull String cause) {
         try {
-            try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
-                mDevice.dumpWindowHierarchy(os);
-                os.flush();
-                Log.w(TAG, "Dumping window hierarchy because " + cause);
-                for (String line : os.toString("UTF-8").split("\n")) {
-                    Log.w(TAG, line);
-                    // Sleep a little bit to avoid logs being ignored due to spam
-                    SystemClock.sleep(100);
-                }
+            final File file = Helper.createTestFile("hierarchy.xml");
+            if (file == null) return;
+            Log.w(TAG, "Dumping window hierarchy because " + cause + " on " + file);
+            try (FileInputStream fis = new FileInputStream(file)) {
+                mDevice.dumpWindowHierarchy(file);
             }
-        } catch (IOException e) {
-            // Just ignore it...
-            Log.e(TAG, "exception dumping window hierarchy", e);
-            return;
+        } catch (Exception e) {
+            Log.e(TAG, "error dumping screen on " + cause, e);
+        } finally {
+            takeScreenshotAndSave();
         }
     }
 
@@ -826,6 +875,22 @@
     }
 
     /**
+     * Takes a screenshot and save it in the file system for post-mortem analysis.
+     */
+    public void takeScreenshotAndSave() {
+        File file = null;
+        try {
+            file = Helper.createTestFile("screenshot.png");
+            if (file != null) {
+                final Bitmap screenshot = takeScreenshot();
+                Helper.dumpBitmap(screenshot, file);
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Error taking screenshot and saving on " + file, e);
+        }
+    }
+
+    /**
      * Asserts the contents of a child element.
      *
      * @param parent parent object
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UsernameOnlyActivity.java b/tests/autofillservice/src/android/autofillservice/cts/UsernameOnlyActivity.java
new file mode 100644
index 0000000..b75330b
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/UsernameOnlyActivity.java
@@ -0,0 +1,61 @@
+/*
+ * 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.autofillservice.cts;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Button;
+import android.widget.EditText;
+
+public final class UsernameOnlyActivity extends AbstractAutoFillActivity {
+
+    private static final String TAG = "UsernameOnlyActivity";
+
+    private EditText mUsernameEditText;
+    private Button mNextButton;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(getContentView());
+
+        mUsernameEditText = findViewById(R.id.username);
+        mNextButton = findViewById(R.id.next);
+        mNextButton.setOnClickListener((v) -> next());
+    }
+
+    protected int getContentView() {
+        return R.layout.username_only_activity;
+    }
+
+    public void focusOnUsername() {
+        syncRunOnUiThread(() -> mUsernameEditText.requestFocus());
+    }
+
+    void setUsername(String username) {
+        syncRunOnUiThread(() -> mUsernameEditText.setText(username));
+    }
+
+    void next() {
+        final String username = mUsernameEditText.getText().toString();
+        Log.v(TAG, "Going to next screen as user " + username);
+        final Intent intent = new Intent(this, PasswordOnlyActivity.class)
+                .putExtra(PasswordOnlyActivity.EXTRA_USERNAME, username);
+        startActivity(intent);
+        finish();
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ValidatorTest.java b/tests/autofillservice/src/android/autofillservice/cts/ValidatorTest.java
index 622e127..85c6737 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/ValidatorTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/ValidatorTest.java
@@ -32,25 +32,13 @@
 import android.view.View;
 import android.view.autofill.AutofillId;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 /**
  * Simple integration test to verify that the UI is only shown if the validator passes.
  */
 @AppModeFull // Service-specific test
-public class ValidatorTest extends AutoFillServiceTestCase {
-    @Rule
-    public final AutofillActivityTestRule<LoginActivity> mActivityRule =
-        new AutofillActivityTestRule<>(LoginActivity.class);
-
-    private LoginActivity mActivity;
-
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
-    }
+public class ValidatorTest extends AbstractLoginActivityTestCase {
 
     @Test
     public void testShowUiWhenValidatorPass() throws Exception {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ValidatorsTest.java b/tests/autofillservice/src/android/autofillservice/cts/ValidatorsTest.java
index 9f5b69c..bb4561b 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/ValidatorsTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/ValidatorsTest.java
@@ -39,7 +39,7 @@
 
 @RunWith(MockitoJUnitRunner.class)
 @AppModeFull // Unit test
-public class ValidatorsTest extends AutoFillServiceTestCase {
+public class ValidatorsTest {
 
     @Mock private Validator mInvalidValidator;
     @Mock private ValueFinder mValueFinder;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java b/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java
index 84370ca..8c88928 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java
@@ -31,8 +31,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -40,16 +38,20 @@
 
 @RunWith(AndroidJUnit4.class)
 @AppModeFull // Unit test
-public class ViewAttributesTest extends AutoFillServiceTestCase {
-    @Rule
-    public final AutofillActivityTestRule<ViewAttributesTestActivity> mActivityRule =
-            new AutofillActivityTestRule<>(ViewAttributesTestActivity.class);
+public class ViewAttributesTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<ViewAttributesTestActivity> {
 
     private ViewAttributesTestActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<ViewAttributesTestActivity> getActivityRule() {
+        return new AutofillActivityTestRule<ViewAttributesTestActivity>(
+                ViewAttributesTestActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     @Nullable private String[] getHintsFromView(@IdRes int resId) {
@@ -178,6 +180,9 @@
 
     @Test
     public void checkViewLocationInAssistStructure() throws Exception {
+        // If screen is not large enough to contain child, the height/weight will be the residual
+        // space instead of the specific size.
+        mUiBot.assumeMinimumResolution(500);
         onAssistStructure(false, (structure) -> {
                     // check size of outerView
                     AssistStructure.ViewNode outerView = findNodeByResourceId(structure,
diff --git a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityCompatModeTest.java b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityCompatModeTest.java
index b50fb22..5029e9e 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityCompatModeTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityCompatModeTest.java
@@ -21,7 +21,6 @@
 import static android.autofillservice.cts.Helper.assertTextIsSanitized;
 import static android.autofillservice.cts.Helper.findNodeByResourceId;
 import static android.autofillservice.cts.Helper.getContext;
-import static android.autofillservice.cts.Helper.hasAutofillFeature;
 import static android.autofillservice.cts.InstrumentedAutoFillServiceCompatMode.SERVICE_NAME;
 import static android.autofillservice.cts.InstrumentedAutoFillServiceCompatMode.SERVICE_PACKAGE;
 import static android.autofillservice.cts.VirtualContainerActivity.INITIAL_URL_BAR_VALUE;
@@ -39,11 +38,9 @@
 import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
 import android.autofillservice.cts.common.SettingsHelper;
 import android.autofillservice.cts.common.SettingsStateChangerRule;
-import android.content.Context;
 import android.os.SystemClock;
 import android.platform.test.annotations.AppModeFull;
 import android.service.autofill.SaveInfo;
-import android.support.test.InstrumentationRegistry;
 
 import org.junit.After;
 import org.junit.ClassRule;
@@ -54,7 +51,6 @@
  * the Autofill APIs.
  */
 public class VirtualContainerActivityCompatModeTest extends VirtualContainerActivityTest {
-    private static final Context sContext = InstrumentationRegistry.getContext();
 
     @ClassRule
     public static final SettingsStateChangerRule sCompatModeChanger = new SettingsStateChangerRule(
@@ -76,23 +72,19 @@
     }
 
     @Override
-    protected void postActivityLaunched(VirtualContainerActivity activity) {
+    protected void postActivityLaunched() {
         // Set our own compat mode as well..
-        activity.mCustomView.setCompatMode(true);
+        mActivity.mCustomView.setCompatMode(true);
     }
 
     @Override
     protected void enableService() {
         Helper.enableAutofillService(getContext(), SERVICE_NAME);
-        InstrumentedAutoFillServiceCompatMode.setIgnoreUnexpectedRequests(false);
     }
 
     @Override
     protected void disableService() {
-        if (!hasAutofillFeature()) return;
-
-        Helper.disableAutofillService(getContext(), SERVICE_NAME);
-        InstrumentedAutoFillServiceCompatMode.setIgnoreUnexpectedRequests(true);
+        Helper.disableAutofillService(getContext());
     }
 
     @Override
diff --git a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
index 84cb1f6..5ad0f73 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
@@ -49,9 +49,7 @@
 import android.view.ViewGroup;
 import android.view.autofill.AutofillManager;
 
-import org.junit.Before;
 import org.junit.Ignore;
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.concurrent.TimeoutException;
@@ -60,21 +58,14 @@
  * Test case for an activity containing virtual children, either using the explicit Autofill APIs
  * or Compat mode.
  */
-public class VirtualContainerActivityTest extends AutoFillServiceTestCase {
+public class VirtualContainerActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<VirtualContainerActivity> {
 
     // TODO(b/74256300): remove when fixed it :-)
     private static final boolean BUG_74256300_FIXED = false;
 
-    @Rule
-    public final AutofillActivityTestRule<VirtualContainerActivity> mActivityRule =
-            new AutofillActivityTestRule<VirtualContainerActivity>(VirtualContainerActivity.class) {
-        @Override
-        protected void beforeActivityLaunched() {
-            preActivityCreated();
-        }
-    };
-
     private final boolean mCompatMode;
+    private AutofillActivityTestRule<VirtualContainerActivity> mActivityRule;
     protected VirtualContainerActivity mActivity;
 
     public VirtualContainerActivityTest() {
@@ -85,12 +76,6 @@
         mCompatMode = compatMode;
     }
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
-        postActivityLaunched(mActivity);
-    }
-
     /**
      * Hook for subclass to customize test before activity is created.
      */
@@ -99,8 +84,27 @@
     /**
      * Hook for subclass to customize activity after it's launched.
      */
-    protected void postActivityLaunched(
-            @SuppressWarnings("unused") VirtualContainerActivity activity) {
+    protected void postActivityLaunched() {}
+
+    @Override
+    protected AutofillActivityTestRule<VirtualContainerActivity> getActivityRule() {
+        if (mActivityRule == null) {
+            mActivityRule = new AutofillActivityTestRule<VirtualContainerActivity>(
+                    VirtualContainerActivity.class) {
+                @Override
+                protected void beforeActivityLaunched() {
+                    preActivityCreated();
+                }
+
+                @Override
+                protected void afterActivityLaunched() {
+                    mActivity = getActivity();
+                    postActivityLaunched();
+                }
+            };
+
+        }
+        return mActivityRule;
     }
 
     @Test
@@ -129,15 +133,14 @@
      * Focus to username and expect window event
      */
     void focusToUsername() throws TimeoutException {
-        mUiBot.waitForWindowChange(() -> mActivity.mUsername.changeFocus(true),
-                Timeouts.UI_TIMEOUT.getMaxValue());
+        mUiBot.waitForWindowChange(() -> mActivity.mUsername.changeFocus(true));
     }
 
     /**
      * Focus to username and expect no autofill window event
      */
     void focusToUsernameExpectNoWindowEvent() throws Throwable {
-        // TODO should use waitForWindowChange() if we can filter out event of app Activity itself.
+        // TODO: should use waitForWindowChange() if we can filter out event of app Activity itself.
         mActivityRule.runOnUiThread(() -> mActivity.mUsername.changeFocus(true));
     }
 
@@ -145,8 +148,7 @@
      * Focus to password and expect window event
      */
     void focusToPassword() throws TimeoutException {
-        mUiBot.waitForWindowChange(() -> mActivity.mPassword.changeFocus(true),
-                Timeouts.UI_TIMEOUT.getMaxValue());
+        mUiBot.waitForWindowChange(() -> mActivity.mPassword.changeFocus(true));
     }
 
     /**
diff --git a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivity.java b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivity.java
index 08d4c70..d2119dd 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivity.java
@@ -17,6 +17,8 @@
 
 import static android.autofillservice.cts.Timeouts.WEBVIEW_TIMEOUT;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import android.content.Context;
 import android.os.Bundle;
 import android.os.SystemClock;
@@ -48,7 +50,7 @@
     static final String ID_OUTSIDE1 = "outside1";
     static final String ID_OUTSIDE2 = "outside2";
 
-    MyWebView mWebView;
+    private MyWebView mWebView;
 
     private LinearLayout mParent;
     private LinearLayout mOutsideContainer1;
@@ -109,20 +111,21 @@
                     Log.v(TAG, "onPageFinished(): " + url);
                     latch.countDown();
                 }
-
             });
             mWebView.loadUrl(FAKE_URL);
         });
 
         // Wait until it's loaded.
-
         if (!latch.await(WEBVIEW_TIMEOUT.ms(), TimeUnit.MILLISECONDS)) {
             throw new RetryableException(WEBVIEW_TIMEOUT, "WebView not loaded");
         }
 
+        // Sanity check to make sure autofill was enabled when the WebView was created
+        assertThat(mWebView.isAutofillEnabled()).isTrue();
+
         // TODO(b/80317628): re-add check below
         // NOTE: we cannot search by resourceId because WebView does not set them...
-        // uiBot.assertShownByText("Login"); // Login button
+        // uiBot.assertShownByText("Login", WEBVIEW_TIMEOUT); // Login button
 
         return mWebView;
     }
@@ -155,7 +158,7 @@
     }
 
     private UiObject2 getLabel(UiBot uiBot, String label) throws Exception {
-        return uiBot.assertShownByText(label, Timeouts.WEBVIEW_TIMEOUT);
+        return uiBot.assertShownByText(label, WEBVIEW_TIMEOUT);
     }
 
     private UiObject2 getInput(UiBot uiBot, String contentDescription) throws Exception {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java
index 8623967..658e7c5 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java
@@ -29,33 +29,48 @@
 import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
 import android.platform.test.annotations.AppModeFull;
 import android.support.test.uiautomator.UiObject2;
+import android.util.Log;
 import android.view.KeyEvent;
 import android.view.ViewStructure.HtmlInfo;
 import android.view.autofill.AutofillManager;
 
 import org.junit.AfterClass;
-import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Ignore;
-import org.junit.Rule;
 import org.junit.Test;
 
-@AppModeFull(reason = "Flaky in instant mode")
-public class WebViewActivityTest extends AutoFillServiceTestCase {
+public class WebViewActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<WebViewActivity> {
+
+    private static final String TAG = "WebViewActivityTest";
 
     // TODO(b/64951517): WebView currently does not trigger the autofill callbacks when values are
     // set using accessibility.
     private static final boolean INJECT_EVENTS = true;
 
-    @Rule
-    public final AutofillActivityTestRule<WebViewActivity> mActivityRule =
-            new AutofillActivityTestRule<WebViewActivity>(WebViewActivity.class);
-
     private WebViewActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<WebViewActivity> getActivityRule() {
+        return new AutofillActivityTestRule<WebViewActivity>(WebViewActivity.class) {
+
+            // TODO(b/111838239): latest WebView implementation calls AutofillManager.isEnabled() to
+            // disable autofill for optimization when it returns false, and unfortunately the value
+            // returned by that method does not change when the service is enabled / disabled, so we
+            // need to start enable the service before launching the activity.
+            // Once that's fixed, remove this overridden method.
+            @Override
+            protected void beforeActivityLaunched() {
+                super.beforeActivityLaunched();
+                Log.i(TAG, "Setting service before launching the activity");
+                enableService();
+            }
+
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     @BeforeClass
@@ -139,14 +154,6 @@
         mUiBot.assertNoDatasets();
         callback.assertUiHiddenEvent(myWebView, passwordChildId);
 
-        // Make sure screen was autofilled.
-        assertThat(mActivity.getUsernameInput(mUiBot).getText()).isEqualTo("dude");
-        // TODO: proper way to verify text (which is ..... because it's a password) - ideally it
-        // should call passwordInput.isPassword(), but that's not exposed
-        final String password = mActivity.getPasswordInput(mUiBot).getText();
-        assertThat(password).isNotEqualTo("sweet");
-        assertThat(password).hasLength(5);
-
         // Assert structure passed to service.
         try {
             final ViewNode webViewNode =
@@ -276,14 +283,6 @@
         myWebView.assertAutofilled();
         callback.assertUiHiddenEvent(myWebView, usernameChildId);
 
-        // Make sure screen was autofilled.
-        assertThat(mActivity.getUsernameInput(mUiBot).getText()).isEqualTo("dude");
-        // TODO: proper way to verify text (which is ..... because it's a password) - ideally it
-        // should call passwordInput.isPassword(), but that's not exposed
-        final String password = mActivity.getPasswordInput(mUiBot).getText();
-        assertThat(password).isNotEqualTo("sweet");
-        assertThat(password).hasLength(5);
-
         // Now trigger save.
         if (INJECT_EVENTS) {
             mActivity.getUsernameInput(mUiBot).click();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/WelcomeActivity.java b/tests/autofillservice/src/android/autofillservice/cts/WelcomeActivity.java
index e1c726a..9e11da9 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/WelcomeActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/WelcomeActivity.java
@@ -22,12 +22,13 @@
 import android.content.Intent;
 import android.content.IntentSender;
 import android.os.Bundle;
-import androidx.annotation.Nullable;
 import android.support.test.uiautomator.UiObject2;
 import android.text.TextUtils;
 import android.util.Log;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+
 /**
  * Activity that displays a "Welcome USER" message after login.
  */
@@ -75,17 +76,23 @@
         sInstance = null;
     }
 
-    static void finishIt(UiBot uiBot) {
-        if (sInstance != null) {
-            Log.d(TAG, "So long and thanks for all the fish!");
-            sInstance.finish();
-            uiBot.assertGoneByRelativeId(ID_WELCOME, Timeouts.ACTIVITY_RESURRECTION);
-        }
+    @Override
+    public void finish() {
+        super.finish();
+        Log.d(TAG, "So long and thanks for all the finish!");
+
         if (sPendingIntent != null) {
+            Log.v(TAG, " canceling pending intent on finish(): " + sPendingIntent);
             sPendingIntent.cancel();
         }
     }
 
+    static void finishIt() {
+        if (sInstance != null) {
+            sInstance.finish();
+        }
+    }
+
     // TODO: reuse in other places
     static void assertShowingDefaultMessage(UiBot uiBot) throws Exception {
         assertShowing(uiBot, null);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/common/OneTimeSettingsListener.java b/tests/autofillservice/src/android/autofillservice/cts/common/OneTimeSettingsListener.java
index 1ca7955..3707eea 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/common/OneTimeSettingsListener.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/common/OneTimeSettingsListener.java
@@ -19,13 +19,17 @@
 import static android.autofillservice.cts.common.SettingsHelper.NAMESPACE_GLOBAL;
 import static android.autofillservice.cts.common.SettingsHelper.NAMESPACE_SECURE;
 
+import android.autofillservice.cts.JUnitHelper;
+import android.autofillservice.cts.RetryableException;
 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;
@@ -34,16 +38,18 @@
  * 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";
+    private static final long DEFAULT_TIMEOUT_MS = 30_000;
+
     private final CountDownLatch mLatch = new CountDownLatch(1);
     private final ContentResolver mResolver;
     private final String mKey;
-
-    public OneTimeSettingsListener(Context context, String key) {
-        this(context, NAMESPACE_SECURE, key);
-    }
+    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;
@@ -73,10 +79,15 @@
      */
     public void assertCalled() {
         try {
-            final boolean updated = mLatch.await(5, TimeUnit.SECONDS);
+            final boolean updated = mLatch.await(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
             if (!updated) {
-                throw new IllegalStateException("Settings " + mKey + " not called in 5s");
+                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, JUnitHelper.getCurrentTestName() + "/" + mKey + ": " + delta + "ms");
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();
             throw new IllegalStateException("Interrupted", e);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/common/SettingsHelper.java b/tests/autofillservice/src/android/autofillservice/cts/common/SettingsHelper.java
index 219c55b..59167f4 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/common/SettingsHelper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/common/SettingsHelper.java
@@ -97,7 +97,7 @@
             @NonNull String key) {
 
         final String currentValue = get(namespace, key);
-        if (currentValue == null || currentValue.equals("null")) {
+        if (currentValue == null) {
             // Already set, ignore
             return;
         }
@@ -108,7 +108,7 @@
         observer.assertCalled();
 
         final String newValue = get(namespace, key);
-        assertWithMessage("invalid value for '%s' settings", key).that(newValue).isEqualTo("null");
+        assertWithMessage("invalid value for '%s' settings", key).that(newValue).isNull();
     }
 
     public static void syncDelete(@NonNull Context context, @NonNull String key) {
@@ -118,9 +118,14 @@
     /**
      * Gets the value of a given preference using Shell command.
      */
-    @NonNull
+    @Nullable
     public static String get(@NonNull String namespace, @NonNull String key) {
-        return runShellCommand("settings get %s %s", namespace, key);
+        final String value = runShellCommand("settings get %s %s", namespace, key);
+        if (value == null || value.equals("null")) {
+            return null;
+        } else {
+            return value;
+        }
     }
 
     @NonNull
diff --git a/tests/autofillservice/src/android/autofillservice/cts/common/StateKeeperRule.java b/tests/autofillservice/src/android/autofillservice/cts/common/StateKeeperRule.java
index 77963d1..9784db5 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/common/StateKeeperRule.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/common/StateKeeperRule.java
@@ -44,6 +44,18 @@
         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() {
@@ -51,6 +63,7 @@
             @Override
             public void evaluate() throws Throwable {
                 final T previousValue = mStateManager.get();
+                preEvaluate(description);
                 try {
                     base.evaluate();
                 } finally {
@@ -59,6 +72,7 @@
                         mStateManager.set(previousValue);
                     }
                 }
+                postEvaluate(description);
             }
         };
     }
diff --git a/tests/backup/Android.mk b/tests/backup/Android.mk
index e12f2c4..985f372 100644
--- a/tests/backup/Android.mk
+++ b/tests/backup/Android.mk
@@ -27,7 +27,12 @@
     android.test.base.stubs \
 
 
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner ctstestserver mockito-target-minus-junit4
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    compatibility-device-util \
+    ctstestrunner \
+    ctstestserver \
+    mockito-target-minus-junit4 \
+    testng
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/backup/AndroidTest.xml b/tests/backup/AndroidTest.xml
index 8e0bfa0..5a4a916 100644
--- a/tests/backup/AndroidTest.xml
+++ b/tests/backup/AndroidTest.xml
@@ -22,6 +22,7 @@
         <option name="test-file-name" value="CtsFullBackupApp.apk" />
         <option name="test-file-name" value="CtsKeyValueBackupApp.apk" />
         <option name="test-file-name" value="CtsBackupTestCases.apk" />
+        <option name="test-file-name" value="CtsPermissionBackupApp.apk" />
     </target_preparer>
     <target_preparer class="android.cts.backup.BackupPreparer">
         <option name="enable-backup-if-needed" value="true" />
diff --git a/libs/deviceutillegacy/Android.mk b/tests/backup/app/permission/Android.mk
similarity index 67%
rename from libs/deviceutillegacy/Android.mk
rename to tests/backup/app/permission/Android.mk
index 8cfa6d1..24ccfbe 100644
--- a/libs/deviceutillegacy/Android.mk
+++ b/tests/backup/app/permission/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2014 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.
@@ -16,21 +16,20 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    compatibility-device-util \
-    junit
-
-LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src)
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_MODULE := ctsdeviceutillegacy
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
+
+LOCAL_PACKAGE_NAME := CtsPermissionBackupApp
 LOCAL_SDK_VERSION := current
 
-include $(BUILD_STATIC_JAVA_LIBRARY)
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    compatibility-device-util \
+    ctstestrunner
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/app/app2/AndroidManifest.xml b/tests/backup/app/permission/AndroidManifest.xml
similarity index 63%
copy from tests/app/app2/AndroidManifest.xml
copy to tests/backup/app/permission/AndroidManifest.xml
index 0926251..8dd735a 100644
--- a/tests/app/app2/AndroidManifest.xml
+++ b/tests/backup/app/permission/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2017 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,16 +14,17 @@
   ~ 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.app2">
-    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+    package="android.backup.permission" >
 
-    <application>
+    <uses-permission android:name="android.permission.READ_SMS"/>
+    <uses-permission android:name="android.permission.SEND_SMS"/>
+    <uses-permission android:name="android.permission.READ_CONTACTS"/>
+    <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
+
+    <application
+        android:label="Android Permission Backup CTS App">
         <uses-library android:name="android.test.runner" />
-
-        <service android:name="android.app.stubs.LocalService"
-                 android:exported="true"/>
-        <service android:name=".AlertWindowService"
-                 android:exported="true"/>
     </application>
 </manifest>
diff --git a/tests/backup/src/android/backup/cts/AgentBindingTest.java b/tests/backup/src/android/backup/cts/AgentBindingTest.java
new file mode 100644
index 0000000..29a7c70
--- /dev/null
+++ b/tests/backup/src/android/backup/cts/AgentBindingTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.backup.cts;
+
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assume.assumeTrue;
+import static org.testng.Assert.expectThrows;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.IBinder;
+import android.platform.test.annotations.AppModeFull;
+
+import java.lang.reflect.Method;
+
+@AppModeFull
+public class AgentBindingTest extends BaseBackupCtsTest {
+    private Context mContext;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mContext = getInstrumentation().getTargetContext();
+    }
+
+    public void testUnbindBackupAgent_isNotCallableFromCts() throws Exception {
+        if (!isBackupSupported()) {
+            return;
+        }
+        expectThrows(Exception.class, () -> unbindBackupAgent(mContext.getApplicationInfo()));
+    }
+
+    public void testBindBackupAgent_isNotCallableFromCts() throws Exception {
+        if (!isBackupSupported()) {
+            return;
+        }
+        expectThrows(Exception.class, () -> bindBackupAgent(mContext.getPackageName(), 0, 0));
+    }
+
+    private static void unbindBackupAgent(ApplicationInfo applicationInfo) throws Exception {
+        callActivityManagerMethod(
+                "unbindBackupAgent",
+                new Class<?>[] {ApplicationInfo.class},
+                new Object[] {applicationInfo});
+    }
+
+    private static void bindBackupAgent(String packageName, int backupRestoreMode, int userId)
+            throws Exception {
+        callActivityManagerMethod(
+                "bindBackupAgent",
+                new Class<?>[] {String.class, int.class, int.class},
+                new Object[] {packageName, backupRestoreMode, userId});
+    }
+
+    private static void callActivityManagerMethod(
+            String methodName, Class<?>[] types, Object[] args) throws Exception {
+        Class<?> activityManagerClass = Class.forName("android.app.IActivityManager");
+        Object activityManager = getActivityManager();
+        Method bindBackupAgentMethod = activityManagerClass.getMethod(methodName, types);
+        bindBackupAgentMethod.invoke(activityManager, args);
+    }
+
+    private static Object getActivityManager() throws Exception {
+        Class<?> serviceManagerClass = Class.forName("android.os.ServiceManager");
+        Class<?> stubClass = Class.forName("android.app.IActivityManager$Stub");
+        Method asInterfaceMethod = stubClass.getMethod("asInterface", IBinder.class);
+        Method getServiceMethod = serviceManagerClass.getMethod("getService", String.class);
+        return asInterfaceMethod.invoke(
+                null, (IBinder) getServiceMethod.invoke(serviceManagerClass, "activity"));
+    }
+}
diff --git a/tests/backup/src/android/backup/cts/BaseBackupCtsTest.java b/tests/backup/src/android/backup/cts/BaseBackupCtsTest.java
index 343f2d9..9002eee 100644
--- a/tests/backup/src/android/backup/cts/BaseBackupCtsTest.java
+++ b/tests/backup/src/android/backup/cts/BaseBackupCtsTest.java
@@ -19,34 +19,36 @@
 import android.app.Instrumentation;
 import android.content.pm.PackageManager;
 import android.os.ParcelFileDescriptor;
+import android.platform.test.annotations.AppModeFull;
 import android.test.InstrumentationTestCase;
 
+import com.android.compatibility.common.util.BackupUtils;
 import com.android.compatibility.common.util.LogcatInspector;
 
-import java.io.BufferedReader;
-import java.io.FileInputStream;
-import java.io.IOException;
 import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
 
 /**
  * Base class for backup instrumentation tests.
  *
  * Ensures that backup is enabled and local transport selected, and provides some utility methods.
  */
+@AppModeFull
 public class BaseBackupCtsTest extends InstrumentationTestCase {
     private static final String APP_LOG_TAG = "BackupCTSApp";
 
-    private static final String LOCAL_TRANSPORT =
-            "android/com.android.internal.backup.LocalTransport";
-
-    private boolean isBackupSupported;
+    private boolean mIsBackupSupported;
     private LogcatInspector mLogcatInspector =
             new LogcatInspector() {
                 @Override
-                protected InputStream executeShellCommand(String command) throws IOException {
-                    return executeStreamedShellCommand(getInstrumentation(), command);
+                protected InputStream executeShellCommand(String command) {
+                    return executeInstrumentationShellCommand(getInstrumentation(), command);
+                }
+            };
+    private BackupUtils mBackupUtils =
+            new BackupUtils() {
+                @Override
+                protected InputStream executeShellCommand(String command) {
+                    return executeInstrumentationShellCommand(getInstrumentation(), command);
                 }
             };
 
@@ -54,28 +56,24 @@
     protected void setUp() throws Exception {
         super.setUp();
         PackageManager packageManager = getInstrumentation().getContext().getPackageManager();
-        isBackupSupported = packageManager != null
-                && packageManager.hasSystemFeature(PackageManager.FEATURE_BACKUP);
+        mIsBackupSupported =
+                packageManager != null
+                        && packageManager.hasSystemFeature(PackageManager.FEATURE_BACKUP);
 
-        if (isBackupSupported) {
-            assertTrue("Backup not enabled", isBackupEnabled());
-            assertTrue("LocalTransport not selected", isLocalTransportSelected());
-            exec("setprop log.tag." + APP_LOG_TAG +" VERBOSE");
+        if (mIsBackupSupported) {
+            assertTrue("Backup not enabled", mBackupUtils.isBackupEnabled());
+            assertTrue("LocalTransport not selected", mBackupUtils.isLocalTransportSelected());
+            getBackupUtils()
+                    .executeShellCommandSync("setprop log.tag." + APP_LOG_TAG +" VERBOSE");
         }
     }
 
-    public boolean isBackupSupported() {
-        return isBackupSupported;
+    protected BackupUtils getBackupUtils() {
+        return mBackupUtils;
     }
 
-    private boolean isBackupEnabled() throws Exception {
-        String output = exec("bmgr enabled");
-        return output.contains("currently enabled");
-    }
-
-    private boolean isLocalTransportSelected() throws Exception {
-        String output = exec("bmgr list transports");
-        return output.contains("* " + LOCAL_TRANSPORT);
+    protected boolean isBackupSupported() {
+        return mIsBackupSupported;
     }
 
     /** See {@link LogcatInspector#mark(String)}. */
@@ -91,51 +89,20 @@
     }
 
     protected void createTestFileOfSize(String packageName, int size) throws Exception {
-        exec("am start -a android.intent.action.MAIN " +
-            "-c android.intent.category.LAUNCHER " +
-            "-n " + packageName + "/android.backup.app.MainActivity " +
-            "-e file_size " + size);
+        getBackupUtils().executeShellCommandSync(
+                "am start -a android.intent.action.MAIN "
+                        + "-c android.intent.category.LAUNCHER "
+                        + "-n "
+                        + packageName
+                        + "/android.backup.app.MainActivity "
+                        + "-e file_size " + size);
         waitForLogcat(30, "File created!");
     }
 
-    protected String exec(String command) throws Exception {
-        try (InputStream in = executeStreamedShellCommand(getInstrumentation(), command)) {
-            BufferedReader br = new BufferedReader(
-                    new InputStreamReader(in, StandardCharsets.UTF_8));
-            String str;
-            StringBuilder out = new StringBuilder();
-            while ((str = br.readLine()) != null) {
-                out.append(str);
-            }
-            return out.toString();
-        }
-    }
-
-    private static FileInputStream executeStreamedShellCommand(
-            Instrumentation instrumentation, String command) throws IOException {
+    private static InputStream executeInstrumentationShellCommand(
+            Instrumentation instrumentation, String command) {
         final ParcelFileDescriptor pfd =
                 instrumentation.getUiAutomation().executeShellCommand(command);
         return new ParcelFileDescriptor.AutoCloseInputStream(pfd);
     }
-
-    private static void drainAndClose(BufferedReader reader) {
-        try {
-            while (reader.read() >= 0) {
-                // do nothing.
-            }
-        } catch (IOException ignored) {
-        }
-        closeQuietly(reader);
-    }
-
-    private static void closeQuietly(AutoCloseable closeable) {
-        if (closeable != null) {
-            try {
-                closeable.close();
-            } catch (RuntimeException rethrown) {
-                throw rethrown;
-            } catch (Exception ignored) {
-            }
-        }
-    }
 }
diff --git a/tests/backup/src/android/backup/cts/FullBackupLifecycleTest.java b/tests/backup/src/android/backup/cts/FullBackupLifecycleTest.java
index 9f81ecd..16825d7 100644
--- a/tests/backup/src/android/backup/cts/FullBackupLifecycleTest.java
+++ b/tests/backup/src/android/backup/cts/FullBackupLifecycleTest.java
@@ -16,9 +16,14 @@
 
 package android.backup.cts;
 
+import static com.android.compatibility.common.util.BackupUtils.LOCAL_TRANSPORT_TOKEN;
+
+import android.platform.test.annotations.AppModeFull;
+
 /**
  * Verifies that key methods are called in expected order during backup / restore.
  */
+@AppModeFull
 public class FullBackupLifecycleTest extends BaseBackupCtsTest {
 
     private static final String BACKUP_APP_NAME = "android.backup.app";
@@ -37,7 +42,7 @@
         createTestFileOfSize(BACKUP_APP_NAME, LOCAL_TRANSPORT_CONFORMING_FILE_SIZE);
 
         // Request backup and wait for it to complete
-        exec("bmgr backupnow " + BACKUP_APP_NAME);
+        getBackupUtils().backupNowSync(BACKUP_APP_NAME);
 
         waitForLogcat(TIMEOUT_SECONDS,
             backupSeparator,
@@ -48,7 +53,7 @@
         String restoreSeparator = markLogcat();
 
         // Now request restore and wait for it to complete
-        exec("bmgr restore " + BACKUP_APP_NAME);
+        getBackupUtils().restoreSync(LOCAL_TRANSPORT_TOKEN, BACKUP_APP_NAME);
 
         waitForLogcat(TIMEOUT_SECONDS,
             restoreSeparator,
diff --git a/tests/backup/src/android/backup/cts/FullBackupQuotaTest.java b/tests/backup/src/android/backup/cts/FullBackupQuotaTest.java
index 56d489d..39de875 100644
--- a/tests/backup/src/android/backup/cts/FullBackupQuotaTest.java
+++ b/tests/backup/src/android/backup/cts/FullBackupQuotaTest.java
@@ -16,12 +16,15 @@
 
 package android.backup.cts;
 
+import android.platform.test.annotations.AppModeFull;
+
 /**
  * Verifies receiving quotaExceeded() callback on full backup.
  *
  * Uses test app that creates large file and receives the callback.
  * {@link com.android.internal.backup.LocalTransport} is used, it has size quota 25MB.
  */
+@AppModeFull
 public class FullBackupQuotaTest extends BaseBackupCtsTest {
 
     private static final String BACKUP_APP_NAME = "android.backup.app";
@@ -41,7 +44,7 @@
         createTestFileOfSize(BACKUP_APP_NAME, LOCAL_TRANSPORT_EXCEEDING_FILE_SIZE);
 
         // Request backup and wait for quota exceeded event in logcat
-        exec("bmgr backupnow " + BACKUP_APP_NAME);
+        getBackupUtils().backupNowSync(BACKUP_APP_NAME);
         waitForLogcat(TIMEOUT_SECONDS,separator,
             "Quota exceeded!");
     }
@@ -51,8 +54,9 @@
             return;
         }
         // get the app out of (possibly) stopped state so that backup can be run
-        exec("cmd activity broadcast -a android.backup.app.ACTION_WAKE_UP " +
-                "-n android.backup.app/.WakeUpReceiver");
+        getBackupUtils().executeShellCommandSync(
+                "cmd activity broadcast -a android.backup.app.ACTION_WAKE_UP "
+                        + "-n android.backup.app/.WakeUpReceiver");
 
         // give it 3s for the broadcast to be delivered
         try {
@@ -60,7 +64,7 @@
         } catch (InterruptedException e) {}
 
         String separator = markLogcat();
-        exec("bmgr backupnow " + BACKUP_APP_NAME);
+        getBackupUtils().backupNowSync(BACKUP_APP_NAME);
         waitForLogcat(TIMEOUT_SECONDS,separator,
             "quota is " + LOCAL_TRANSPORT_BACKUP_QUOTA);
     }
diff --git a/tests/backup/src/android/backup/cts/KeyValueLifecycleTest.java b/tests/backup/src/android/backup/cts/KeyValueLifecycleTest.java
index 0bb5243..3d4b97e 100644
--- a/tests/backup/src/android/backup/cts/KeyValueLifecycleTest.java
+++ b/tests/backup/src/android/backup/cts/KeyValueLifecycleTest.java
@@ -16,9 +16,14 @@
 
 package android.backup.cts;
 
+import static com.android.compatibility.common.util.BackupUtils.LOCAL_TRANSPORT_TOKEN;
+
+import android.platform.test.annotations.AppModeFull;
+
 /**
  * Verifies that key methods are called in expected order during backup / restore.
  */
+@AppModeFull
 public class KeyValueLifecycleTest extends BaseBackupCtsTest {
 
     private static final String BACKUP_APP_NAME = "android.backup.kvapp";
@@ -37,7 +42,7 @@
         createTestFileOfSize(BACKUP_APP_NAME, LOCAL_TRANSPORT_CONFORMING_FILE_SIZE);
 
         // Request backup and wait for it to complete
-        exec("bmgr backupnow " + BACKUP_APP_NAME);
+        getBackupUtils().backupNowSync(BACKUP_APP_NAME);
 
         waitForLogcat(TIMEOUT_SECONDS,backupSeparator,
             "onCreate",
@@ -47,7 +52,7 @@
         String restoreSeparator = markLogcat();
 
         // Now request restore and wait for it to complete
-        exec("bmgr restore " + BACKUP_APP_NAME);
+        getBackupUtils().restoreSync(LOCAL_TRANSPORT_TOKEN, BACKUP_APP_NAME);
 
         waitForLogcat(TIMEOUT_SECONDS, restoreSeparator,
             "onCreate",
diff --git a/tests/backup/src/android/backup/cts/KeyValueQuotaTest.java b/tests/backup/src/android/backup/cts/KeyValueQuotaTest.java
index 2e42e21..50de751 100644
--- a/tests/backup/src/android/backup/cts/KeyValueQuotaTest.java
+++ b/tests/backup/src/android/backup/cts/KeyValueQuotaTest.java
@@ -16,12 +16,15 @@
 
 package android.backup.cts;
 
+import android.platform.test.annotations.AppModeFull;
+
 /**
  * Verifies receiving quotaExceeded() callback on full backup.
  *
  * Uses test app that creates large file and receives the callback.
  * {@link com.android.internal.backup.LocalTransport} is used, it has size quota 25MB.
  */
+@AppModeFull
 public class KeyValueQuotaTest extends BaseBackupCtsTest {
 
     private static final String BACKUP_APP_NAME = "android.backup.kvapp";
@@ -41,7 +44,7 @@
         createTestFileOfSize(BACKUP_APP_NAME, LOCAL_TRANSPORT_EXCEEDING_FILE_SIZE);
 
         // Request backup and wait for quota exceeded event in logcat
-        exec("bmgr backupnow " + BACKUP_APP_NAME);
+        getBackupUtils().backupNowSync(BACKUP_APP_NAME);
         waitForLogcat(TIMEOUT_SECONDS, separator,
             "Quota exceeded!");
     }
@@ -54,7 +57,7 @@
         createTestFileOfSize(BACKUP_APP_NAME, 1);
 
         String separator = markLogcat();
-        exec("bmgr backupnow " + BACKUP_APP_NAME);
+        getBackupUtils().backupNowSync(BACKUP_APP_NAME);
         waitForLogcat(TIMEOUT_SECONDS, separator,
             "quota is " + LOCAL_TRANSPORT_BACKUP_QUOTA);
     }
diff --git a/tests/backup/src/android/backup/cts/PermissionTest.java b/tests/backup/src/android/backup/cts/PermissionTest.java
new file mode 100644
index 0000000..bf81def
--- /dev/null
+++ b/tests/backup/src/android/backup/cts/PermissionTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.backup.cts;
+
+import static com.android.compatibility.common.util.BackupUtils.LOCAL_TRANSPORT_TOKEN;
+
+import android.Manifest;
+import android.app.Instrumentation;
+import android.content.pm.PackageManager;
+import android.os.ParcelFileDescriptor;
+import android.platform.test.annotations.AppModeFull;
+
+import com.android.compatibility.common.util.BackupUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Verifies that restored permissions are the same with backup value.
+ */
+@AppModeFull
+public class PermissionTest extends BaseBackupCtsTest {
+
+    /** The name of the package of the app under test */
+    private static final String APP_PACKAGE = "android.backup.permission";
+
+    /** The name of the package for backup */
+    private static final String ANDROID_PACKAGE = "android";
+
+    private BackupUtils mBackupUtils =
+            new BackupUtils() {
+                @Override
+                protected InputStream executeShellCommand(String command) throws IOException {
+                    ParcelFileDescriptor pfd =
+                            getInstrumentation().getUiAutomation().executeShellCommand(command);
+                    return new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+                }
+            };
+
+    /**
+     * Test backup and restore of grant runtime permission.
+     *
+     * Test logic:
+     * 1. Grant SEND_SMS and WRITE_CONTACTS permissions to APP_PACKAGE.
+     * 2. Backup android package, revoke SEND_SMS and WRITE_CONTACTS permissions to APP_PACKAGE.
+     * Then restore android package.
+     * 3. Check restored SEND_SMS and WRITE_CONTACTS permissions.
+     *
+     * @see PackageManagerService#serializeRuntimePermissionGrantsLPr(XmlSerializer, int) and
+     * PackageManagerService#processRestoredPermissionGrantsLPr(XmlPullParser, int)
+     */
+    public void testGrantRuntimePermission() throws Exception {
+        grantRuntimePermission(Manifest.permission.SEND_SMS);
+        grantRuntimePermission(Manifest.permission.WRITE_CONTACTS);
+
+        mBackupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE);
+        revokeRuntimePermission(Manifest.permission.SEND_SMS);
+        revokeRuntimePermission(Manifest.permission.WRITE_CONTACTS);
+        mBackupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE);
+
+        assertEquals(PackageManager.PERMISSION_GRANTED,
+                checkPermission(Manifest.permission.SEND_SMS));
+        assertEquals(PackageManager.PERMISSION_GRANTED,
+                checkPermission(Manifest.permission.WRITE_CONTACTS));
+    }
+
+    private int checkPermission(String permission) {
+        return getInstrumentation().getContext().getPackageManager().checkPermission(permission,
+                APP_PACKAGE);
+    }
+
+    private void grantRuntimePermission(String permission) {
+        if (checkPermission(permission) != PackageManager.PERMISSION_GRANTED) {
+            getInstrumentation().getUiAutomation().grantRuntimePermission(APP_PACKAGE, permission);
+            assertEquals(PackageManager.PERMISSION_GRANTED, checkPermission(permission));
+        }
+    }
+
+    private void revokeRuntimePermission(String permission) {
+        if (checkPermission(permission) == PackageManager.PERMISSION_GRANTED) {
+            getInstrumentation().getUiAutomation().revokeRuntimePermission(APP_PACKAGE, permission);
+            assertEquals(PackageManager.PERMISSION_DENIED, checkPermission(permission));
+        }
+    }
+}
diff --git a/tests/camera/Android.mk b/tests/camera/Android.mk
index 369d566..349083d 100644
--- a/tests/camera/Android.mk
+++ b/tests/camera/Android.mk
@@ -46,7 +46,9 @@
 	mockito-target-minus-junit4 \
 	android-ex-camera2 \
 	CtsCameraUtils \
-	truth-prebuilt
+	truth-prebuilt \
+	androidx.heifwriter_heifwriter \
+	android-support-test
 
 LOCAL_JNI_SHARED_LIBRARIES := \
 	libctscamera2_jni \
diff --git a/tests/camera/AndroidTest.xml b/tests/camera/AndroidTest.xml
index 2a2cdc9..83cdd03 100644
--- a/tests/camera/AndroidTest.xml
+++ b/tests/camera/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS Camera test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="camera" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsCameraTestCases.apk" />
@@ -23,7 +24,7 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.camera.cts" />
         <option name="runtime-hint" value="12m7s" />
-        <!-- test-timeout unit is ms, value = 200 min -->
-        <option name="test-timeout" value="12000000" />
+        <!-- test-timeout unit is ms, value = 20 min -->
+        <option name="test-timeout" value="1200000" />
     </test>
 </configuration>
diff --git a/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java b/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
index bd9bec8..92b171a 100644
--- a/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
@@ -48,7 +48,6 @@
 import android.hardware.camera2.cts.rs.ScriptYuvToRgb;
 import android.os.Handler;
 import android.os.HandlerThread;
-import android.platform.test.annotations.AppModeFull;
 import android.renderscript.Allocation;
 import android.renderscript.Script.LaunchOptions;
 import android.test.AndroidTestCase;
@@ -72,7 +71,6 @@
  *
  * <p>YUV_420_888: flexible YUV420, it is a mandatory format for camera.</p>
  */
-@AppModeFull
 public class AllocationTest extends AndroidTestCase {
     private static final String TAG = "AllocationTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
diff --git a/tests/camera/src/android/hardware/camera2/cts/BurstCaptureRawTest.java b/tests/camera/src/android/hardware/camera2/cts/BurstCaptureRawTest.java
index 6e0949d..7b23abf 100644
--- a/tests/camera/src/android/hardware/camera2/cts/BurstCaptureRawTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/BurstCaptureRawTest.java
@@ -27,20 +27,21 @@
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
 import android.hardware.camera2.cts.CameraTestUtils.SimpleImageReaderListener;
+import android.hardware.camera2.cts.helpers.StaticMetadata;
 import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.media.Image;
-import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 import android.util.Range;
 import android.util.Size;
 
 import java.util.ArrayList;
 
+import org.junit.Test;
+
 /**
  * Basic tests for burst capture in RAW formats.
  */
-@AppModeFull
 public class BurstCaptureRawTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "BurstCaptureRawTest";
     private static final int RAW_FORMATS[] = {
@@ -55,31 +56,31 @@
             EXPOSURE_MULTIPLIERS.length * SENSITIVITY_MLTIPLIERS.length;
 
     @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();
     }
 
     /**
      * Verify raw sensor size information is correctly configured.
      */
+    @Test
     public void testRawSensorSize() throws Exception {
         Log.i(TAG, "Begin testRawSensorSize");
         for (String id : mCameraIds) {
             try {
-                openDevice(id);
-
                 ArrayList<Integer> supportedRawList = new ArrayList<Integer>(RAW_FORMATS.length);
-                if (!checkCapability(supportedRawList, RAW_FORMATS)) {
+                if (!checkCapability(id, supportedRawList, RAW_FORMATS)) {
                     Log.i(TAG, "Capability is not supported on camera " + id
                             + ". Skip the test.");
                     continue;
                 }
 
+                openDevice(id);
                 Size[] rawSizes = mStaticInfo.getRawOutputSizesChecked();
                 assertTrue("No capture sizes available for RAW format!", rawSizes.length != 0);
 
@@ -99,6 +100,7 @@
      * be honored.
      * </p>
      */
+    @Test
     public void testMetadataRoundDown() throws Exception {
         Log.i(TAG, "Begin testMetadataRoundDown");
 
@@ -114,6 +116,7 @@
      * sync.
      * </p>
      */
+    @Test
     public void testManualAutoSwitch() throws Exception {
         Log.i(TAG, "Begin testManualAutoSwitch");
 
@@ -125,6 +128,7 @@
     /**
      * Per frame timestamp test in non-stalled RAW formats
      */
+    @Test
     public void testTimestamp() throws Exception {
         Log.i(TAG, "Begin testTimestamp");
 
@@ -437,15 +441,17 @@
      *
      * @return true if the it is has the capability to execute the test.
      */
-    private boolean checkCapability(ArrayList<Integer> supportedRawList, int[] testedFormats) {
+    private boolean checkCapability(String id, ArrayList<Integer> supportedRawList,
+            int[] testedFormats) {
+        StaticMetadata staticInfo = mAllStaticInfo.get(id);
         // make sure the sensor has manual support
-        if (!mStaticInfo.isHardwareLevelAtLeastFull()) {
+        if (!staticInfo.isHardwareLevelAtLeastFull()) {
             Log.w(TAG, "Full hardware level is not supported");
             return false;
         }
 
         // get the list of supported RAW format
-        StreamConfigurationMap config = mStaticInfo.getValueFromKeyNonNull(
+        StreamConfigurationMap config = staticInfo.getValueFromKeyNonNull(
                 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
 
         // check for the RAW support
@@ -671,15 +677,14 @@
         final int PREPARE_TIMEOUT_MS = 10000;
         for (String id : mCameraIds) {
             try {
-                openDevice(id);
-
                 ArrayList<Integer> supportedRawList = new ArrayList<Integer>(RAW_FORMATS.length);
-                if (!checkCapability(supportedRawList, testedFormats)) {
+                if (!checkCapability(id, supportedRawList, testedFormats)) {
                     Log.i(TAG, "Capability is not supported on camera " + id
                             + ". Skip the test.");
                     continue;
                 }
 
+                openDevice(id);
                 // test each supported RAW format
                 for (int rawFormat : supportedRawList) {
                     Log.i(TAG, "Testing format " + imageFormatToString(rawFormat) + ".");
diff --git a/tests/camera/src/android/hardware/camera2/cts/BurstCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/BurstCaptureTest.java
index b0aae41..bbedf05 100644
--- a/tests/camera/src/android/hardware/camera2/cts/BurstCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/BurstCaptureTest.java
@@ -23,10 +23,10 @@
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.cts.helpers.StaticMetadata;
 import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.media.Image;
-import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 import android.util.Range;
 import android.util.Size;
@@ -34,7 +34,8 @@
 import java.util.List;
 import java.util.ArrayList;
 
-@AppModeFull
+import org.junit.Test;
+
 public class BurstCaptureTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "BurstCaptureTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -44,28 +45,30 @@
      * Test YUV burst capture with full-AUTO control.
      * Also verifies sensor settings operation if READ_SENSOR_SETTINGS is available.
      */
+    @Test
     public void testYuvBurst() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
                 String id = mCameraIds[i];
                 Log.i(TAG, "Testing YUV Burst for camera " + id);
-                openDevice(id);
 
-                if (!mStaticInfo.isColorOutputSupported()) {
+                StaticMetadata staticInfo = mAllStaticInfo.get(id);
+                if (!staticInfo.isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                 }
-                if (!mStaticInfo.isAeLockSupported() || !mStaticInfo.isAwbLockSupported()) {
+                if (!staticInfo.isAeLockSupported() || !staticInfo.isAwbLockSupported()) {
                     Log.i(TAG, "AE/AWB lock is not supported in camera " + id +
                             ". Skip the test");
                     continue;
                 }
 
-                if (mStaticInfo.isHardwareLevelLegacy()) {
+                if (staticInfo.isHardwareLevelLegacy()) {
                     Log.i(TAG, "Legacy camera doesn't report min frame duration" +
                             ". Skip the test");
                     continue;
                 }
 
+                openDevice(id);
                 yuvBurstTestByCamera(id);
             } finally {
                 closeDevice();
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
index 98fa7af..191c049 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
@@ -46,7 +46,6 @@
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.SystemClock;
-import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 import android.util.Range;
 import android.view.Surface;
@@ -77,7 +76,6 @@
 /**
  * <p>Basic test for CameraDevice APIs.</p>
  */
-@AppModeFull
 public class CameraDeviceTest extends Camera2AndroidTestCase {
     private static final String TAG = "CameraDeviceTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -645,13 +643,13 @@
     public void testPrepare() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
-                openDevice(mCameraIds[i], mCameraMockListener);
-                waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(mCameraIds[i], mCameraMockListener);
+                waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
 
                 prepareTestByCamera();
             }
@@ -668,17 +666,18 @@
     public void testPrepareForSharedSurfaces() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
-                openDevice(mCameraIds[i], mCameraMockListener);
-                waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
-                if (mStaticInfo.isHardwareLevelLegacy()) {
+                StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIds[i]);
+                if (staticInfo.isHardwareLevelLegacy()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] + " is legacy, skipping");
                     continue;
                 }
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!staticInfo.isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(mCameraIds[i], mCameraMockListener);
+                waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
 
                 prepareTestForSharedSurfacesByCamera();
             }
@@ -694,13 +693,13 @@
     public void testCreateSessions() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
-                openDevice(mCameraIds[i], mCameraMockListener);
-                waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(mCameraIds[i], mCameraMockListener);
+                waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
 
                 testCreateSessionsByCamera(mCameraIds[i]);
             }
@@ -716,13 +715,13 @@
     public void testCreateCustomSession() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
-                openDevice(mCameraIds[i], mCameraMockListener);
-                waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(mCameraIds[i], mCameraMockListener);
+                waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
 
                 testCreateCustomSessionByCamera(mCameraIds[i]);
             }
@@ -880,13 +879,14 @@
     public void testSessionParametersStateLeak() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
-                openDevice(mCameraIds[i], mCameraMockListener);
-                waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(mCameraIds[i], mCameraMockListener);
+                waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
+
                 testSessionParametersStateLeakByCamera(mCameraIds[i]);
             }
             finally {
@@ -1049,13 +1049,13 @@
     public void testCreateSessionWithParameters() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
-                openDevice(mCameraIds[i], mCameraMockListener);
-                waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(mCameraIds[i], mCameraMockListener);
+                waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
 
                 testCreateSessionWithParametersByCamera(mCameraIds[i], /*reprocessable*/false);
                 testCreateSessionWithParametersByCamera(mCameraIds[i], /*reprocessable*/true);
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
index dd7197c8..170cc3f 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
@@ -32,7 +32,6 @@
 import android.hardware.camera2.cts.helpers.CameraErrorCollector;
 import android.os.Handler;
 import android.os.HandlerThread;
-import android.platform.test.annotations.AppModeFull;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
@@ -51,7 +50,6 @@
 /**
  * <p>Basic test for CameraManager class.</p>
  */
-@AppModeFull
 public class CameraManagerTest extends AndroidTestCase {
     private static final String TAG = "CameraManagerTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
diff --git a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
index 8f8bf87..faca7e2 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -29,6 +29,7 @@
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
+import android.hardware.camera2.cts.helpers.StaticMetadata;
 import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
 import android.hardware.camera2.params.BlackLevelPattern;
 import android.hardware.camera2.params.ColorSpaceTransform;
@@ -39,7 +40,6 @@
 import android.hardware.camera2.params.TonemapCurve;
 import android.media.Image;
 import android.os.Parcel;
-import android.platform.test.annotations.AppModeFull;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Range;
@@ -52,6 +52,8 @@
 import java.util.Arrays;
 import java.util.List;
 
+import org.junit.Test;
+
 /**
  * <p>
  * Basic test for camera CaptureRequest key controls.
@@ -61,7 +63,6 @@
  * manual ISP control and other per-frame control and synchronization.
  * </p>
  */
-@AppModeFull
 public class CaptureRequestTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "CaptureRequestTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -125,18 +126,19 @@
     }
 
     @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 CaptureRequest settings parcelling.
      */
+    @Test
     public void testSettingsBinderParcel() throws Exception {
         SurfaceTexture outputTexture = new SurfaceTexture(/* random texture ID */ 5);
         Surface surface = new Surface(outputTexture);
@@ -228,16 +230,16 @@
      * value changes (when requests have lock ON).
      * </p>
      */
+    @Test
     public void testBlackLevelLock() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
-                openDevice(mCameraIds[i]);
-
-                if (!mStaticInfo.isCapabilitySupported(
+                if (!mAllStaticInfo.get(mCameraIds[i]).isCapabilitySupported(
                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
                     continue;
                 }
 
+                openDevice(mCameraIds[i]);
                 SimpleCaptureCallback listener = new SimpleCaptureCallback();
                 CaptureRequest.Builder requestBuilder =
                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
@@ -282,13 +284,14 @@
      *   close enough to the optical black level values.
      * </p>
      */
+    @Test
     public void testDynamicBlackWhiteLevel() throws Exception {
         for (String id : mCameraIds) {
             try {
-                openDevice(id);
-                if (!mStaticInfo.isDynamicBlackLevelSupported()) {
+                if (!mAllStaticInfo.get(id).isDynamicBlackLevelSupported()) {
                     continue;
                 }
+                openDevice(id);
                 dynamicBlackWhiteLevelTestByCamera();
             } finally {
                 closeDevice();
@@ -309,24 +312,25 @@
      * requested by setting {@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE} to ON.
      * </p>
      */
+    @Test
     public void testLensShadingMap() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
-                openDevice(mCameraIds[i]);
-
-                if (!mStaticInfo.isManualLensShadingMapSupported()) {
+                StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIds[i]);
+                if (!staticInfo.isManualLensShadingMapSupported()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " doesn't support lens shading controls, skipping test");
                     continue;
                 }
 
                 List<Integer> lensShadingMapModes = Arrays.asList(CameraTestUtils.toObject(
-                        mStaticInfo.getAvailableLensShadingMapModesChecked()));
+                        staticInfo.getAvailableLensShadingMapModesChecked()));
 
                 if (!lensShadingMapModes.contains(STATISTICS_LENS_SHADING_MAP_MODE_ON)) {
                     continue;
                 }
 
+                openDevice(mCameraIds[i]);
                 SimpleCaptureCallback listener = new SimpleCaptureCallback();
                 CaptureRequest.Builder requestBuilder =
                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
@@ -386,17 +390,17 @@
      * correct.
      * </p>
      */
+    @Test
     public void testAntiBandingModes() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
-                openDevice(mCameraIds[i]);
-
                 // Without manual sensor control, exposure time cannot be verified
-                if (!mStaticInfo.isCapabilitySupported(
+                if (!mAllStaticInfo.get(mCameraIds[i]).isCapabilitySupported(
                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
                     continue;
                 }
 
+                openDevice(mCameraIds[i]);
                 int[] modes = mStaticInfo.getAeAvailableAntiBandingModesChecked();
 
                 Size previewSz =
@@ -422,16 +426,17 @@
      * API specifications.
      * </p>
      */
+    @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++) {
             try {
-                openDevice(mCameraIds[i]);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
 
+                openDevice(mCameraIds[i]);
                 Size maxPreviewSz = mOrderedPreviewSizes.get(0); // Max preview size.
 
                 // Update preview surface with given size for all sub-tests.
@@ -454,16 +459,17 @@
      * and {@link CaptureResult#FLASH_STATE} result.
      * </p>
      */
+    @Test
     public void testFlashControl() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
-                openDevice(mCameraIds[i]);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
 
+                openDevice(mCameraIds[i]);
                 SimpleCaptureCallback listener = new SimpleCaptureCallback();
                 CaptureRequest.Builder requestBuilder =
                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
@@ -496,15 +502,16 @@
     /**
      * Test face detection modes and results.
      */
+    @Test
     public void testFaceDetection() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
-                openDevice(mCameraIds[i]);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(mCameraIds[i]);
                 faceDetectionTestByCamera();
             } finally {
                 closeDevice();
@@ -515,15 +522,16 @@
     /**
      * Test tone map modes and controls.
      */
+    @Test
     public void testToneMapControl() throws Exception {
         for (String id : mCameraIds) {
             try {
-                openDevice(id);
-                if (!mStaticInfo.isManualToneMapSupported()) {
+                if (!mAllStaticInfo.get(id).isManualToneMapSupported()) {
                     Log.i(TAG, "Camera " + id +
                             " doesn't support tone mapping controls, skipping test");
                     continue;
                 }
+                openDevice(id);
                 toneMapTestByCamera();
             } finally {
                 closeDevice();
@@ -534,15 +542,16 @@
     /**
      * Test color correction modes and controls.
      */
+    @Test
     public void testColorCorrectionControl() throws Exception {
         for (String id : mCameraIds) {
             try {
-                openDevice(id);
-                if (!mStaticInfo.isColorCorrectionSupported()) {
+                if (!mAllStaticInfo.get(id).isColorCorrectionSupported()) {
                     Log.i(TAG, "Camera " + id +
                             " doesn't support color correction controls, skipping test");
                     continue;
                 }
+                openDevice(id);
                 colorCorrectionTestByCamera();
             } finally {
                 closeDevice();
@@ -553,16 +562,17 @@
     /**
      * Test edge mode control for Fps not exceeding 30.
      */
+    @Test
     public void testEdgeModeControl() throws Exception {
         for (String id : mCameraIds) {
             try {
-                openDevice(id);
-                if (!mStaticInfo.isEdgeModeControlSupported()) {
+                if (!mAllStaticInfo.get(id).isEdgeModeControlSupported()) {
                     Log.i(TAG, "Camera " + id +
                             " doesn't support EDGE_MODE controls, skipping test");
                     continue;
                 }
 
+                openDevice(id);
                 List<Range<Integer>> fpsRanges = getTargetFpsRangesUpTo30(mStaticInfo);
                 edgeModesTestByCamera(fpsRanges);
             } finally {
@@ -574,16 +584,17 @@
     /**
      * Test edge mode control for Fps greater than 30.
      */
+    @Test
     public void testEdgeModeControlFastFps() throws Exception {
         for (String id : mCameraIds) {
             try {
-                openDevice(id);
-                if (!mStaticInfo.isEdgeModeControlSupported()) {
+                if (!mAllStaticInfo.get(id).isEdgeModeControlSupported()) {
                     Log.i(TAG, "Camera " + id +
                             " doesn't support EDGE_MODE controls, skipping test");
                     continue;
                 }
 
+                openDevice(id);
                 List<Range<Integer>> fpsRanges = getTargetFpsRangesGreaterThan30(mStaticInfo);
                 edgeModesTestByCamera(fpsRanges);
             } finally {
@@ -596,22 +607,24 @@
     /**
      * Test focus distance control.
      */
+    @Test
     public void testFocusDistanceControl() throws Exception {
         for (String id : mCameraIds) {
             try {
-                openDevice(id);
-                if (!mStaticInfo.hasFocuser()) {
+                StaticMetadata staticInfo = mAllStaticInfo.get(id);
+                if (!staticInfo.hasFocuser()) {
                     Log.i(TAG, "Camera " + id + " has no focuser, skipping test");
                     continue;
                 }
 
-                if (!mStaticInfo.isCapabilitySupported(
+                if (!staticInfo.isCapabilitySupported(
                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
                     Log.i(TAG, "Camera " + id +
                             " does not support MANUAL_SENSOR, skipping test");
                     continue;
                 }
 
+                openDevice(id);
                 focusDistanceTestByCamera();
             } finally {
                 closeDevice();
@@ -622,16 +635,17 @@
     /**
      * Test noise reduction mode for fps ranges not exceeding 30
      */
+    @Test
     public void testNoiseReductionModeControl() throws Exception {
         for (String id : mCameraIds) {
             try {
-                openDevice(id);
-                if (!mStaticInfo.isNoiseReductionModeControlSupported()) {
+                if (!mAllStaticInfo.get(id).isNoiseReductionModeControlSupported()) {
                     Log.i(TAG, "Camera " + id +
                             " doesn't support noise reduction mode, skipping test");
                     continue;
                 }
 
+                openDevice(id);
                 List<Range<Integer>> fpsRanges = getTargetFpsRangesUpTo30(mStaticInfo);
                 noiseReductionModeTestByCamera(fpsRanges);
             } finally {
@@ -643,16 +657,17 @@
     /**
      * Test noise reduction mode for fps ranges greater than 30
      */
+    @Test
     public void testNoiseReductionModeControlFastFps() throws Exception {
         for (String id : mCameraIds) {
             try {
-                openDevice(id);
-                if (!mStaticInfo.isNoiseReductionModeControlSupported()) {
+                if (!mAllStaticInfo.get(id).isNoiseReductionModeControlSupported()) {
                     Log.i(TAG, "Camera " + id +
                             " doesn't support noise reduction mode, skipping test");
                     continue;
                 }
 
+                openDevice(id);
                 List<Range<Integer>> fpsRanges = getTargetFpsRangesGreaterThan30(mStaticInfo);
                 noiseReductionModeTestByCamera(fpsRanges);
             } finally {
@@ -666,14 +681,15 @@
      *
      * <p>The color correction gain and transform shouldn't be changed when AWB is locked.</p>
      */
+    @Test
     public void testAwbModeAndLock() throws Exception {
         for (String id : mCameraIds) {
             try {
-                openDevice(id);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(id);
                 awbModeAndLockTestByCamera();
             } finally {
                 closeDevice();
@@ -684,14 +700,15 @@
     /**
      * Test different AF modes.
      */
+    @Test
     public void testAfModes() throws Exception {
         for (String id : mCameraIds) {
             try {
-                openDevice(id);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(id);
                 afModeTestByCamera();
             } finally {
                 closeDevice();
@@ -702,11 +719,12 @@
     /**
      * Test video and optical stabilizations.
      */
+    @Test
     public void testCameraStabilizations() throws Exception {
         for (String id : mCameraIds) {
             try {
-                openDevice(id);
-                List<Key<?>> keys = mStaticInfo.getCharacteristics().getKeys();
+                StaticMetadata staticInfo = mAllStaticInfo.get(id);
+                List<Key<?>> keys = staticInfo.getCharacteristics().getKeys();
                 if (!(keys.contains(
                         CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES) ||
                         keys.contains(
@@ -714,10 +732,11 @@
                     Log.i(TAG, "Camera " + id + " doesn't support any stabilization modes");
                     continue;
                 }
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!staticInfo.isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(id);
                 stabilizationTestByCamera();
             } finally {
                 closeDevice();
@@ -729,14 +748,15 @@
      * Test digitalZoom (center wise and non-center wise), validate the returned crop regions.
      * The max preview size is used for each camera.
      */
+    @Test
     public void testDigitalZoom() throws Exception {
         for (String id : mCameraIds) {
             try {
-                openDevice(id);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(id);
                 Size maxPreviewSize = mOrderedPreviewSizes.get(0);
                 digitalZoomTestByCamera(maxPreviewSize);
             } finally {
@@ -749,14 +769,15 @@
      * Test digital zoom and all preview size combinations.
      * TODO: this and above test should all be moved to preview test class.
      */
+    @Test
     public void testDigitalZoomPreviewCombinations() throws Exception {
         for (String id : mCameraIds) {
             try {
-                openDevice(id);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(id);
                 digitalZoomPreviewCombinationTestByCamera();
             } finally {
                 closeDevice();
@@ -767,11 +788,12 @@
     /**
      * Test scene mode controls.
      */
+    @Test
     public void testSceneModes() throws Exception {
         for (String id : mCameraIds) {
             try {
-                openDevice(id);
-                if (mStaticInfo.isSceneModeSupported()) {
+                if (mAllStaticInfo.get(id).isSceneModeSupported()) {
+                    openDevice(id);
                     sceneModeTestByCamera();
                 }
             } finally {
@@ -783,14 +805,15 @@
     /**
      * Test effect mode controls.
      */
+    @Test
     public void testEffectModes() throws Exception {
         for (String id : mCameraIds) {
             try {
-                openDevice(id);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(id);
                 effectModeTestByCamera();
             } finally {
                 closeDevice();
diff --git a/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java b/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
index 4146333..9e4a080 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
@@ -28,7 +28,6 @@
 import android.media.ImageReader;
 import android.os.Build;
 import android.os.SystemClock;
-import android.platform.test.annotations.AppModeFull;
 import android.util.Pair;
 import android.util.Size;
 import android.hardware.camera2.cts.helpers.CameraErrorCollector;
@@ -51,7 +50,6 @@
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
-@AppModeFull
 public class CaptureResultTest extends Camera2AndroidTestCase {
     private static final String TAG = "CaptureResultTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -155,14 +153,13 @@
         final int WAIT_FOR_RESULT_TIMOUT_MS = 2000;
         for (String id : mCameraIds) {
             try {
-                openDevice(id);
-
                 // Skip the test if partial result is not supported
-                int partialResultCount = mStaticInfo.getPartialResultCount();
+                int partialResultCount = mAllStaticInfo.get(id).getPartialResultCount();
                 if (partialResultCount == 1) {
                     continue;
                 }
 
+                openDevice(id);
                 // Create image reader and surface.
                 if (mStaticInfo.isColorOutputSupported()) {
                     Size size = mOrderedPreviewSizes.get(0);
@@ -274,12 +271,12 @@
             SimpleImageReaderListener jpegListener = new SimpleImageReaderListener();
             SimpleImageReaderListener prevListener = new SimpleImageReaderListener();
             try {
-                openDevice(id);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
 
+                openDevice(id);
                 CaptureRequest.Builder previewBuilder =
                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                 CaptureRequest.Builder multiBuilder =
diff --git a/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java b/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
index 0689b9b..4af437c 100644
--- a/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
@@ -41,7 +41,6 @@
 import android.media.ImageReader;
 import android.os.ConditionVariable;
 import android.os.SystemClock;
-import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Size;
@@ -66,7 +65,6 @@
 /**
  * Tests for the DngCreator API.
  */
-@AppModeFull
 public class DngCreatorTest extends Camera2AndroidTestCase {
     private static final String TAG = "DngCreatorTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -140,15 +138,14 @@
             FileOutputStream fileStream = null;
             ByteArrayOutputStream outputStream = null;
             try {
-                openDevice(deviceId);
-
-                if (!mStaticInfo.isCapabilitySupported(
+                if (!mAllStaticInfo.get(deviceId).isCapabilitySupported(
                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
                     Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
                             ". Skip the test.");
                     continue;
                 }
 
+                openDevice(deviceId);
                 Size activeArraySize = mStaticInfo.getRawDimensChecked();
 
                 // Create capture image reader
@@ -167,7 +164,7 @@
 
                 if (VERBOSE) {
                     // Write DNG to file
-                    String dngFilePath = DEBUG_FILE_NAME_BASE + "/camera_basic_" + deviceId + "_" +
+                    String dngFilePath = mDebugFileNameBase + "/camera_basic_" + deviceId + "_" +
                             DEBUG_DNG_FILE;
                     // Write out captured DNG file for the first camera device if setprop is enabled
                     fileStream = new FileOutputStream(dngFilePath);
@@ -217,15 +214,14 @@
             FileOutputStream fileStream = null;
             ByteArrayOutputStream outputStream = null;
             try {
-                openDevice(deviceId);
-
-                if (!mStaticInfo.isCapabilitySupported(
+                if (!mAllStaticInfo.get(deviceId).isCapabilitySupported(
                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
                     Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
                             ". Skip the test.");
                     continue;
                 }
 
+                openDevice(deviceId);
                 Size activeArraySize = mStaticInfo.getRawDimensChecked();
 
                 Size[] targetPreviewSizes =
@@ -276,7 +272,7 @@
                 outputStream = new ByteArrayOutputStream();
                 dngCreator.writeImage(outputStream, resultPair.first.get(0));
 
-                String filePath = DEBUG_FILE_NAME_BASE + "/camera_thumb_" + deviceId + "_" +
+                String filePath = mDebugFileNameBase + "/camera_thumb_" + deviceId + "_" +
                         DEBUG_DNG_FILE;
                 // Write out captured DNG file for the first camera device
                 fileStream = new FileOutputStream(filePath);
@@ -410,7 +406,7 @@
                             new DngCreator(data.characteristics, data.imagePair.second);
 
                     // Write DNG to file
-                    String dngFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId + "_" +
+                    String dngFilePath = mDebugFileNameBase + "/camera_" + deviceId + "_" +
                             DEBUG_DNG_FILE;
                     // Write out captured DNG file for the first camera device if setprop is enabled
                     fileStream = new FileOutputStream(dngFilePath);
@@ -420,7 +416,7 @@
                     Log.v(TAG, "Test DNG file for camera " + deviceId + " saved to " + dngFilePath);
 
                     // Write JPEG to file
-                    String jpegFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId + "_jpeg.jpg";
+                    String jpegFilePath = mDebugFileNameBase + "/camera_" + deviceId + "_jpeg.jpg";
                     // Write out captured DNG file for the first camera device if setprop is enabled
                     fileChannel = new FileOutputStream(jpegFilePath).getChannel();
                     ByteBuffer jPlane = jpeg.getPlanes()[0].getBuffer();
@@ -431,7 +427,7 @@
                             jpegFilePath);
 
                     // Write jpeg generated from demosaiced RAW frame to file
-                    String rawFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId + "_raw.jpg";
+                    String rawFilePath = mDebugFileNameBase + "/camera_" + deviceId + "_raw.jpg";
                     // Write out captured DNG file for the first camera device if setprop is enabled
                     fileStream = new FileOutputStream(rawFilePath);
                     rawBitmap.compress(Bitmap.CompressFormat.JPEG, 90, fileStream);
@@ -480,8 +476,9 @@
             DngCreator dngCreator = new DngCreator(data.characteristics, data.imagePair.second);
 
             // Write DNG to file
-            String dngFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId + "_"
-                    + TEST_DNG_FILE;
+            String dngFilePath = mDebugFileNameBase + "/camera_" +
+                deviceId + "_" + TEST_DNG_FILE;
+
             // Write out captured DNG file for the first camera device if setprop is enabled
             try (FileOutputStream fileStream = new FileOutputStream(dngFilePath)) {
                 dngCreator.writeImage(fileStream, raw);
@@ -509,15 +506,14 @@
         CapturedData data = new CapturedData();
         List<CameraTestUtils.SimpleImageReaderListener> captureListeners = new ArrayList<>();
         try {
-            openDevice(deviceId);
-
-            if (!mStaticInfo.isCapabilitySupported(
+            if (!mAllStaticInfo.get(deviceId).isCapabilitySupported(
                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
                 Log.i(TAG, "RAW capability is not supported in camera " + deviceId
                         + ". Skip the test.");
                 return null;
             }
 
+            openDevice(deviceId);
             Size activeArraySize = mStaticInfo.getRawDimensChecked();
 
             // Get largest jpeg size
@@ -610,7 +606,7 @@
             FileOutputStream fileStream = null;
             try {
                 // Write JPEG patch to file
-                String jpegFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId +
+                String jpegFilePath = mDebugFileNameBase + "/camera_" + deviceId +
                         "_jpeg_patch.jpg";
                 fileStream = new FileOutputStream(jpegFilePath);
                 jpegPatch.compress(Bitmap.CompressFormat.JPEG, 90, fileStream);
@@ -620,7 +616,7 @@
                         jpegFilePath);
 
                 // Write RAW patch to file
-                String rawFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId +
+                String rawFilePath = mDebugFileNameBase + "/camera_" + deviceId +
                         "_raw_patch.jpg";
                 fileStream = new FileOutputStream(rawFilePath);
                 rawPatch.compress(Bitmap.CompressFormat.JPEG, 90, fileStream);
diff --git a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index 78f7523..47c7ffa 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -32,7 +32,6 @@
 import android.media.CamcorderProfile;
 import android.media.ImageReader;
 import android.os.Build;
-import android.platform.test.annotations.AppModeFull;
 import android.test.AndroidTestCase;
 import android.util.Log;
 import android.util.Rational;
@@ -55,7 +54,6 @@
 /**
  * Extended tests for static camera characteristics.
  */
-@AppModeFull
 public class ExtendedCameraCharacteristicsTest extends AndroidTestCase {
     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);
@@ -891,32 +889,28 @@
             Rect activeArray = c.get(
                 CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
 
-            // Legacy device doesn't have preCorrectionActiveArraySize metadata.
-            Integer hwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
-            if (hwLevel != LEGACY) {
-                // Verify pre-correction array encloses active array
-                mCollector.expectTrue("preCorrectionArray [" + precorrectionArray.left + ", " +
-                        precorrectionArray.top + ", " + precorrectionArray.right + ", " +
-                        precorrectionArray.bottom + "] does not enclose activeArray[" +
-                        activeArray.left + ", " + activeArray.top + ", " + activeArray.right +
-                        ", " + activeArray.bottom,
-                        precorrectionArray.contains(activeArray.left, activeArray.top) &&
-                        precorrectionArray.contains(activeArray.right-1, activeArray.bottom-1));
+            // Verify pre-correction array encloses active array
+            mCollector.expectTrue("preCorrectionArray [" + precorrectionArray.left + ", " +
+                    precorrectionArray.top + ", " + precorrectionArray.right + ", " +
+                    precorrectionArray.bottom + "] does not enclose activeArray[" +
+                    activeArray.left + ", " + activeArray.top + ", " + activeArray.right +
+                    ", " + activeArray.bottom,
+                    precorrectionArray.contains(activeArray.left, activeArray.top) &&
+                    precorrectionArray.contains(activeArray.right-1, activeArray.bottom-1));
 
-                // Verify pixel array encloses pre-correction array
-                mCollector.expectTrue("preCorrectionArray [" + precorrectionArray.left + ", " +
-                        precorrectionArray.top + ", " + precorrectionArray.right + ", " +
-                        precorrectionArray.bottom + "] isn't enclosed by pixelArray[" +
-                        pixelArraySize.getWidth() + ", " + pixelArraySize.getHeight() + "]",
-                        precorrectionArray.left >= 0 &&
-                        precorrectionArray.left < pixelArraySize.getWidth() &&
-                        precorrectionArray.right > 0 &&
-                        precorrectionArray.right <= pixelArraySize.getWidth() &&
-                        precorrectionArray.top >= 0 &&
-                        precorrectionArray.top < pixelArraySize.getHeight() &&
-                        precorrectionArray.bottom > 0 &&
-                        precorrectionArray.bottom <= pixelArraySize.getHeight());
-            }
+            // Verify pixel array encloses pre-correction array
+            mCollector.expectTrue("preCorrectionArray [" + precorrectionArray.left + ", " +
+                    precorrectionArray.top + ", " + precorrectionArray.right + ", " +
+                    precorrectionArray.bottom + "] isn't enclosed by pixelArray[" +
+                    pixelArraySize.getWidth() + ", " + pixelArraySize.getHeight() + "]",
+                    precorrectionArray.left >= 0 &&
+                    precorrectionArray.left < pixelArraySize.getWidth() &&
+                    precorrectionArray.right > 0 &&
+                    precorrectionArray.right <= pixelArraySize.getWidth() &&
+                    precorrectionArray.top >= 0 &&
+                    precorrectionArray.top < pixelArraySize.getHeight() &&
+                    precorrectionArray.bottom > 0 &&
+                    precorrectionArray.bottom <= pixelArraySize.getHeight());
 
             if (supportDepth) {
                 mCollector.expectTrue("Supports DEPTH_OUTPUT but does not support DEPTH16",
diff --git a/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java b/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
index 6ca9601..af6e5e0 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.Test;
+
 /**
  * Quick-running test for very basic camera operation for all cameras
  * and both camera APIs.
@@ -58,18 +60,19 @@
     private static final int FRAMES_TO_WAIT_FOR_CAPTURE = 100;
 
     @Presubmit
+    @Test
     public void testCamera2() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
                 Log.i(TAG, "Testing camera2 API for camera device " + mCameraIds[i]);
-                openDevice(mCameraIds[i]);
 
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
 
+                openDevice(mCameraIds[i]);
                 camera2TestByCamera();
             } finally {
                 closeDevice();
@@ -184,6 +187,7 @@
     }
 
     @Presubmit
+    @Test
     public void testCamera1() throws Exception {
         for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
             Camera camera = null;
diff --git a/tests/camera/src/android/hardware/camera2/cts/FlashlightTest.java b/tests/camera/src/android/hardware/camera2/cts/FlashlightTest.java
index f0b3179..1aa6a17 100644
--- a/tests/camera/src/android/hardware/camera2/cts/FlashlightTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/FlashlightTest.java
@@ -24,7 +24,6 @@
 import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
 import android.util.Log;
 import android.os.SystemClock;
-import android.platform.test.annotations.AppModeFull;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.concurrent.ArrayBlockingQueue;
@@ -36,7 +35,6 @@
 /**
  * <p>Tests for flashlight API.</p>
  */
-@AppModeFull
 public class FlashlightTest extends Camera2AndroidTestCase {
     private static final String TAG = "FlashlightTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
diff --git a/tests/camera/src/android/hardware/camera2/cts/HeifWriterTest.java b/tests/camera/src/android/hardware/camera2/cts/HeifWriterTest.java
new file mode 100644
index 0000000..ec76aa0
--- /dev/null
+++ b/tests/camera/src/android/hardware/camera2/cts/HeifWriterTest.java
@@ -0,0 +1,378 @@
+/*
+ * 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.camera.cts;
+
+import android.graphics.ImageFormat;
+import android.graphics.SurfaceTexture;
+import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.params.OutputConfiguration;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.media.MediaMetadataRetriever;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.SystemClock;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+import android.util.Size;
+import android.view.Surface;
+
+import static android.hardware.camera2.cts.CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS;
+import static android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
+import static android.hardware.camera2.cts.CameraTestUtils.getValueNotNull;
+
+import static androidx.heifwriter.HeifWriter.INPUT_MODE_SURFACE;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.heifwriter.HeifWriter;
+
+import com.android.compatibility.common.util.MediaUtils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class HeifWriterTest extends Camera2AndroidTestCase {
+    private static final String TAG = HeifWriterTest.class.getSimpleName();
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private static final String OUTPUT_FILENAME = "output.heic";
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testHeif() throws Exception {
+        final int NUM_SINGLE_CAPTURE_TESTED = 3;
+        final int NUM_HEIC_CAPTURE_TESTED = 2;
+        final int SESSION_WARMUP_MS = 1000;
+        final int HEIF_STOP_TIMEOUT = 3000 * NUM_SINGLE_CAPTURE_TESTED;
+
+        if (!canEncodeHeic()) {
+            MediaUtils.skipTest("heic encoding is not supported on this device");
+            return;
+        }
+
+        for (String id : mCameraIds) {
+            try {
+                Log.v(TAG, "Testing HEIF capture for Camera " + id);
+                openDevice(id);
+
+                Size[] availableSizes = mStaticInfo.getAvailableSizesForFormatChecked(
+                        ImageFormat.PRIVATE,
+                        StaticMetadata.StreamDirection.Output);
+
+                // for each resolution, test imageReader:
+                for (Size sz : availableSizes) {
+                    HeifWriter heifWriter = null;
+                    OutputConfiguration outConfig = null;
+                    Surface latestSurface = null;
+                    CaptureRequest.Builder reqStill = null;
+                    int width = sz.getWidth();
+                    int height = sz.getHeight();
+                    for (int cap = 0; cap < NUM_HEIC_CAPTURE_TESTED; cap++) {
+                        if (VERBOSE) {
+                            Log.v(TAG, "Testing size " + sz.toString() + " format PRIVATE"
+                                    + " for camera " + mCamera.getId() + ". Iteration:" + cap);
+                        }
+
+                        try {
+                            TestConfig.Builder builder = new TestConfig.Builder(/*useGrid*/false);
+                            builder.setNumImages(NUM_SINGLE_CAPTURE_TESTED);
+                            builder.setSize(sz);
+                            String filename = "Cam" + id + "_" + width + "x" + height +
+                                    "_" + cap + ".heic";
+                            builder.setOutputPath(
+                                    new File(Environment.getExternalStorageDirectory(),
+                                    filename).getAbsolutePath());
+                            TestConfig config = builder.build();
+
+                            try {
+                                heifWriter = new HeifWriter.Builder(
+                                        config.mOutputPath,
+                                        width, height, INPUT_MODE_SURFACE)
+                                    .setGridEnabled(config.mUseGrid)
+                                    .setMaxImages(config.mMaxNumImages)
+                                    .setQuality(config.mQuality)
+                                    .setPrimaryIndex(config.mNumImages - 1)
+                                    .setHandler(mHandler)
+                                    .build();
+                            } catch (IOException e) {
+                                // Continue in case the size is not supported
+                                continue;
+                            }
+
+                            // First capture. Start capture session
+                            latestSurface = heifWriter.getInputSurface();
+                            outConfig = new OutputConfiguration(latestSurface);
+                            List<OutputConfiguration> configs =
+                                new ArrayList<OutputConfiguration>();
+                            configs.add(outConfig);
+
+                            SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1);
+                            Surface previewSurface = new Surface(preview);
+                            preview.setDefaultBufferSize(640, 480);
+                            configs.add(new OutputConfiguration(previewSurface));
+
+                            CaptureRequest.Builder reqPreview = mCamera.createCaptureRequest(
+                                    CameraDevice.TEMPLATE_PREVIEW);
+                            reqPreview.addTarget(previewSurface);
+
+                            reqStill = mCamera.createCaptureRequest(
+                                    CameraDevice.TEMPLATE_STILL_CAPTURE);
+                            reqStill.addTarget(previewSurface);
+                            reqStill.addTarget(latestSurface);
+
+                            // Start capture session and preview
+                            createSessionByConfigs(configs);
+                            startCapture(reqPreview.build(), /*repeating*/true, null, null);
+
+                            SystemClock.sleep(SESSION_WARMUP_MS);
+
+                            heifWriter.start();
+
+                            // Start capture.
+                            CaptureRequest request = reqStill.build();
+                            SimpleCaptureCallback listener = new SimpleCaptureCallback();
+
+                            int numImages = config.mNumImages;
+
+                            for (int i = 0; i < numImages; i++) {
+                                startCapture(request, /*repeating*/false, listener, mHandler);
+                            }
+
+                            // Validate capture result.
+                            CaptureResult result = validateCaptureResult(
+                                    ImageFormat.PRIVATE, sz, listener, numImages);
+
+                            // TODO: convert capture results into EXIF and send to heifwriter
+
+                            heifWriter.stop(HEIF_STOP_TIMEOUT);
+
+                            verifyResult(config.mOutputPath, width, height,
+                                    config.mRotation, config.mUseGrid,
+                                    Math.min(numImages, config.mMaxNumImages));
+                        } finally {
+                            if (heifWriter != null) {
+                                heifWriter.close();
+                                heifWriter = null;
+                            }
+                            stopCapture(/*fast*/false);
+                        }
+                    }
+                }
+            } finally {
+                closeDevice(id);
+            }
+        }
+    }
+
+    private static boolean canEncodeHeic() {
+        return MediaUtils.hasEncoder(MediaFormat.MIMETYPE_VIDEO_HEVC)
+            || MediaUtils.hasEncoder(MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC);
+    }
+
+    private static class TestConfig {
+        final boolean mUseGrid;
+        final int mMaxNumImages;
+        final int mNumImages;
+        final int mWidth;
+        final int mHeight;
+        final int mRotation;
+        final int mQuality;
+        final String mOutputPath;
+
+        TestConfig(boolean useGrid, int maxNumImages, int numImages,
+                   int width, int height, int rotation, int quality,
+                   String outputPath) {
+            mUseGrid = useGrid;
+            mMaxNumImages = maxNumImages;
+            mNumImages = numImages;
+            mWidth = width;
+            mHeight = height;
+            mRotation = rotation;
+            mQuality = quality;
+            mOutputPath = outputPath;
+        }
+
+        static class Builder {
+            final boolean mUseGrid;
+            int mMaxNumImages;
+            int mNumImages;
+            int mWidth;
+            int mHeight;
+            int mRotation;
+            final int mQuality;
+            String mOutputPath;
+
+            Builder(boolean useGrids) {
+                mUseGrid = useGrids;
+                mMaxNumImages = mNumImages = 4;
+                mWidth = 1920;
+                mHeight = 1080;
+                mRotation = 0;
+                mQuality = 100;
+                mOutputPath = new File(Environment.getExternalStorageDirectory(),
+                        OUTPUT_FILENAME).getAbsolutePath();
+            }
+
+            Builder setNumImages(int numImages) {
+                mMaxNumImages = mNumImages = numImages;
+                return this;
+            }
+
+            Builder setRotation(int rotation) {
+                mRotation = rotation;
+                return this;
+            }
+
+            Builder setSize(Size sz) {
+                mWidth = sz.getWidth();
+                mHeight = sz.getHeight();
+                return this;
+            }
+
+            Builder setOutputPath(String path) {
+                mOutputPath = path;
+                return this;
+            }
+
+            private void cleanupStaleOutputs() {
+                File outputFile = new File(mOutputPath);
+                if (outputFile.exists()) {
+                    outputFile.delete();
+                }
+            }
+
+            TestConfig build() {
+                cleanupStaleOutputs();
+                return new TestConfig(mUseGrid, mMaxNumImages, mNumImages,
+                        mWidth, mHeight, mRotation, mQuality, mOutputPath);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "TestConfig"
+                    + ": mUseGrid " + mUseGrid
+                    + ", mMaxNumImages " + mMaxNumImages
+                    + ", mNumImages " + mNumImages
+                    + ", mWidth " + mWidth
+                    + ", mHeight " + mHeight
+                    + ", mRotation " + mRotation
+                    + ", mQuality " + mQuality
+                    + ", mOutputPath " + mOutputPath;
+        }
+    }
+
+    private void verifyResult(
+            String filename, int width, int height, int rotation, boolean useGrid, int numImages)
+            throws Exception {
+        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+        retriever.setDataSource(filename);
+        String hasImage = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE);
+        if (!"yes".equals(hasImage)) {
+            throw new Exception("No images found in file " + filename);
+        }
+        assertEquals("Wrong image count", numImages,
+                Integer.parseInt(retriever.extractMetadata(
+                    MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT)));
+        assertEquals("Wrong width", width,
+                Integer.parseInt(retriever.extractMetadata(
+                    MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH)));
+        assertEquals("Wrong height", height,
+                Integer.parseInt(retriever.extractMetadata(
+                    MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT)));
+        assertEquals("Wrong rotation", rotation,
+                Integer.parseInt(retriever.extractMetadata(
+                    MediaMetadataRetriever.METADATA_KEY_IMAGE_ROTATION)));
+        retriever.release();
+
+        if (useGrid) {
+            MediaExtractor extractor = new MediaExtractor();
+            extractor.setDataSource(filename);
+            MediaFormat format = extractor.getTrackFormat(0);
+            int tileWidth = format.getInteger(MediaFormat.KEY_TILE_WIDTH);
+            int tileHeight = format.getInteger(MediaFormat.KEY_TILE_HEIGHT);
+            int gridRows = format.getInteger(MediaFormat.KEY_GRID_ROWS);
+            int gridCols = format.getInteger(MediaFormat.KEY_GRID_COLUMNS);
+            assertTrue("Wrong tile width or grid cols",
+                    ((width + tileWidth - 1) / tileWidth) == gridCols);
+            assertTrue("Wrong tile height or grid rows",
+                    ((height + tileHeight - 1) / tileHeight) == gridRows);
+            extractor.release();
+        }
+    }
+
+    /**
+     * Validate capture results.
+     *
+     * @param format The format of this capture.
+     * @param size The capture size.
+     * @param listener The capture listener to get capture result callbacks.
+     * @return the last verified CaptureResult
+     */
+    private CaptureResult validateCaptureResult(
+            int format, Size size, SimpleCaptureCallback listener, int numFrameVerified) {
+        CaptureResult result = null;
+        for (int i = 0; i < numFrameVerified; i++) {
+            result = listener.getCaptureResult(CAPTURE_RESULT_TIMEOUT_MS);
+            if (mStaticInfo.isCapabilitySupported(
+                    CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS)) {
+                Long exposureTime = getValueNotNull(result, CaptureResult.SENSOR_EXPOSURE_TIME);
+                Integer sensitivity = getValueNotNull(result, CaptureResult.SENSOR_SENSITIVITY);
+                mCollector.expectInRange(
+                        String.format(
+                                "Capture for format %d, size %s exposure time is invalid.",
+                                format, size.toString()),
+                        exposureTime,
+                        mStaticInfo.getExposureMinimumOrDefault(),
+                        mStaticInfo.getExposureMaximumOrDefault()
+                );
+                mCollector.expectInRange(
+                        String.format("Capture for format %d, size %s sensitivity is invalid.",
+                                format, size.toString()),
+                        sensitivity,
+                        mStaticInfo.getSensitivityMinimumOrDefault(),
+                        mStaticInfo.getSensitivityMaximumOrDefault()
+                );
+            }
+            // TODO: add more key validations.
+        }
+        return result;
+    }
+}
diff --git a/tests/camera/src/android/hardware/camera2/cts/IdleUidTest.java b/tests/camera/src/android/hardware/camera2/cts/IdleUidTest.java
index 70e928a..dd7053b 100644
--- a/tests/camera/src/android/hardware/camera2/cts/IdleUidTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/IdleUidTest.java
@@ -32,7 +32,6 @@
 import android.hardware.camera2.CameraManager;
 import android.os.Handler;
 import android.os.HandlerThread;
-import android.platform.test.annotations.AppModeFull;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -52,7 +51,6 @@
  * get an error callback losing the camera handle. Similarly if the UID is
  * already idle it cannot obtain a camera handle.
  */
-@AppModeFull
 @RunWith(AndroidJUnit4.class)
 public final class IdleUidTest {
     private static final long CAMERA_OPERATION_TIMEOUT_MILLIS = 5000; // 5 sec
diff --git a/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java b/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
index 8852778..7c380db 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
@@ -39,7 +39,6 @@
 import android.media.Image.Plane;
 import android.media.ImageReader;
 import android.os.ConditionVariable;
-import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 import android.util.Size;
 import android.view.Surface;
@@ -67,7 +66,6 @@
  * <p>Some invalid access test. </p>
  * <p>TODO: Add more format tests? </p>
  */
-@AppModeFull
 public class ImageReaderTest extends Camera2AndroidTestCase {
     private static final String TAG = "ImageReaderTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -223,12 +221,12 @@
         for (String id : mCameraIds) {
             try {
                 Log.v(TAG, "Testing long processing on repeating raw for camera " + id);
-                openDevice(id);
 
-                if (!mStaticInfo.isCapabilitySupported(
+                if (!mAllStaticInfo.get(id).isCapabilitySupported(
                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
                     continue;
                 }
+                openDevice(id);
 
                 bufferFormatLongProcessingTimeTestByCamera(ImageFormat.RAW_SENSOR);
             } finally {
@@ -241,13 +239,13 @@
         for (String id : mCameraIds) {
             try {
                 Log.v(TAG, "Testing long processing on repeating YUV for camera " + id);
-                openDevice(id);
 
-                if (!mStaticInfo.isCapabilitySupported(
+                if (!mAllStaticInfo.get(id).isCapabilitySupported(
                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
                     continue;
                 }
 
+                openDevice(id);
                 bufferFormatLongProcessingTimeTestByCamera(ImageFormat.YUV_420_888);
             } finally {
                 closeDevice(id);
@@ -285,12 +283,12 @@
         for (String id : mCameraIds) {
             try {
                 Log.v(TAG, "YUV and JPEG testing for camera " + id);
-                openDevice(id);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id +
                             " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(id);
                 bufferFormatWithYuvTestByCamera(ImageFormat.JPEG);
             } finally {
                 closeDevice(id);
@@ -306,12 +304,12 @@
         for (String id : mCameraIds) {
             try {
                 Log.v(TAG, "YUV and RAW testing for camera " + id);
-                openDevice(id);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id +
                             " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(id);
                 bufferFormatWithYuvTestByCamera(ImageFormat.RAW_SENSOR);
             } finally {
                 closeDevice(id);
@@ -329,13 +327,13 @@
         for (String id : mCameraIds) {
             try {
                 Log.v(TAG, "Testing all YUV image resolutions for camera " + id);
-                openDevice(id);
 
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
 
+                openDevice(id);
                 // Skip warmup on FULL mode devices.
                 int warmupCaptureNumber = (mStaticInfo.isHardwareLevelLegacy()) ?
                         MAX_NUM_IMAGES - 1 : 0;
@@ -510,16 +508,16 @@
                             if (difference > tolerance) {
                                 // Dump files if running in verbose mode
                                 if (DEBUG) {
-                                    String jpegFileName = DEBUG_FILE_NAME_BASE + "/" + captureSz +
+                                    String jpegFileName = mDebugFileNameBase + "/" + captureSz +
                                             "_jpeg.jpg";
                                     dumpFile(jpegFileName, jpegBmap);
-                                    String fullSizeJpegFileName = DEBUG_FILE_NAME_BASE + "/" +
+                                    String fullSizeJpegFileName = mDebugFileNameBase + "/" +
                                             captureSz + "_full_jpeg.jpg";
                                     dumpFile(fullSizeJpegFileName, compressedJpegData);
-                                    String yuvFileName = DEBUG_FILE_NAME_BASE + "/" + captureSz +
+                                    String yuvFileName = mDebugFileNameBase + "/" + captureSz +
                                             "_yuv.jpg";
                                     dumpFile(yuvFileName, yuvBmap);
-                                    String fullSizeYuvFileName = DEBUG_FILE_NAME_BASE + "/" +
+                                    String fullSizeYuvFileName = mDebugFileNameBase + "/" +
                                             captureSz + "_full_yuv.jpg";
                                     int[] fullYUVColors = convertPixelYuvToRgba(yuvImage.getWidth(),
                                             yuvImage.getHeight(), 0, 0, yuvImage);
@@ -901,7 +899,7 @@
                     Image img = mReader.acquireNextImage();
                     assertNotNull("Unable to acquire next image", img);
                     CameraTestUtils.validateImage(img, sz.getWidth(), sz.getHeight(), format,
-                            DEBUG_FILE_NAME_BASE);
+                            mDebugFileNameBase);
 
                     // Verify the exposure time and iso match the requested values.
                     CaptureResult result = listener.getCaptureResult(CAPTURE_RESULT_TIMEOUT_MS);
@@ -1044,7 +1042,7 @@
             assertNotNull("Unable to acquire the latest image", img);
             if (VERBOSE) Log.v(TAG, "Got the latest image");
             CameraTestUtils.validateImage(img, sz.getWidth(), sz.getHeight(), format,
-                    DEBUG_FILE_NAME_BASE);
+                    mDebugFileNameBase);
             HardwareBuffer hwb = img.getHardwareBuffer();
             assertNotNull("Unable to retrieve the Image's HardwareBuffer", hwb);
             if (VERBOSE) Log.v(TAG, "finish validation of image " + numImageVerified);
diff --git a/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java b/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java
index 65c8eb1..6216f36 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java
@@ -28,7 +28,6 @@
 import android.media.Image.Plane;
 import android.media.ImageReader;
 import android.media.ImageWriter;
-import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 import android.util.Size;
 import android.view.Surface;
@@ -45,7 +44,6 @@
  * interface or ImageReader.
  * </p>
  */
-@AppModeFull
 public class ImageWriterTest extends Camera2AndroidTestCase {
     private static final String TAG = "ImageWriterTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -94,11 +92,11 @@
         for (String id : mCameraIds) {
             try {
                 Log.i(TAG, "Testing Camera " + id);
-                openDevice(id);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(id);
                 readerWriterFormatTestByCamera(ImageFormat.YUV_420_888);
             } finally {
                 closeDevice(id);
@@ -243,8 +241,8 @@
             mCollector.expectTrue("ImageWriter 1st output image should match 1st input image",
                     isImageStronglyEqual(cameraImage, outputImage));
             if (DEBUG) {
-                String img1FileName = DEBUG_FILE_NAME_BASE + "/" + maxSize + "_image1_copy.yuv";
-                String outputImg1FileName = DEBUG_FILE_NAME_BASE + "/" + maxSize
+                String img1FileName = mDebugFileNameBase + "/" + maxSize + "_image1_copy.yuv";
+                String outputImg1FileName = mDebugFileNameBase + "/" + maxSize
                         + "_outputImage2_copy.yuv";
                 dumpFile(img1FileName, getDataFromImage(cameraImage));
                 dumpFile(outputImg1FileName, getDataFromImage(outputImage));
@@ -263,7 +261,7 @@
             // make a copy of image1 data, as it will be closed after queueInputImage;
             byte[] img1Data = getDataFromImage(cameraImage);
             if (DEBUG) {
-                String img2FileName = DEBUG_FILE_NAME_BASE + "/" + maxSize + "_image2.yuv";
+                String img2FileName = mDebugFileNameBase + "/" + maxSize + "_image2.yuv";
                 dumpFile(img2FileName, img1Data);
             }
 
@@ -280,7 +278,7 @@
                     + "2nd output image", Arrays.equals(img1Data, outputImageData));
 
             if (DEBUG) {
-                String outputImgFileName = DEBUG_FILE_NAME_BASE + "/" + maxSize +
+                String outputImgFileName = mDebugFileNameBase + "/" + maxSize +
                         "_outputImage2.yuv";
                 dumpFile(outputImgFileName, outputImageData);
             }
diff --git a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
index 33365b2..b608f93 100644
--- a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
@@ -39,7 +39,6 @@
 import android.media.CamcorderProfile;
 import android.media.Image;
 import android.media.ImageReader;
-import android.platform.test.annotations.AppModeFull;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Range;
@@ -59,12 +58,13 @@
 import java.util.Map;
 import java.util.Set;
 
+import org.junit.Test;
+
 import static org.mockito.Mockito.*;
 
 /**
  * Tests exercising logical camera setup, configuration, and usage.
  */
-@AppModeFull
 public final class LogicalCameraDeviceTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "LogicalCameraTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -77,35 +77,26 @@
 
     private static final double FRAME_DURATION_THRESHOLD = 0.03;
 
-    private HashMap<String, StaticMetadata> mAllStaticInfo;
-
     @Override
-    protected void setUp() throws Exception {
+    public void setUp() throws Exception {
         super.setUp();
-
-        mAllStaticInfo = new HashMap<String, StaticMetadata>();
-        for (String cameraId : mCameraIds) {
-            StaticMetadata staticMetadata = new StaticMetadata(
-                    mCameraManager.getCameraCharacteristics(cameraId),
-                    CheckLevel.ASSERT, /*collector*/null);
-            mAllStaticInfo.put(cameraId, staticMetadata);
-        }
     }
 
     /**
      * Test that passing in invalid physical camera ids in OutputConfiguragtion behaves as expected
      * for logical multi-camera and non-logical multi-camera.
      */
+    @Test
     public void testInvalidPhysicalCameraIdInOutputConfiguration() throws Exception {
         for (String id : mCameraIds) {
             try {
                 Log.i(TAG, "Testing Camera " + id);
-                openDevice(id);
-                if (mStaticInfo.isHardwareLevelLegacy()) {
+                if (mAllStaticInfo.get(id).isHardwareLevelLegacy()) {
                     Log.i(TAG, "Camera " + id + " is legacy, skipping");
                     continue;
                 }
 
+                openDevice(id);
                 Size yuvSize = mOrderedPreviewSizes.get(0);
                 // Create a YUV image reader.
                 ImageReader imageReader = ImageReader.newInstance(yuvSize.getWidth(),
@@ -141,23 +132,25 @@
      * Test for making sure that streaming from physical streams work as expected, and
      * FPS isn't slowed down.
      */
+    @Test
     public void testBasicPhysicalStreaming() throws Exception {
 
         for (String id : mCameraIds) {
             try {
                 Log.i(TAG, "Testing Camera " + id);
-                openDevice(id);
 
-                if (!mStaticInfo.isColorOutputSupported()) {
+                StaticMetadata staticInfo = mAllStaticInfo.get(id);
+                if (!staticInfo.isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
 
-                if (!mStaticInfo.isLogicalMultiCamera()) {
+                if (!staticInfo.isLogicalMultiCamera()) {
                     Log.i(TAG, "Camera " + id + " is not a logical multi-camera, skipping");
                     continue;
                 }
 
+                openDevice(id);
                 assertTrue("Logical multi-camera must be LIMITED or higher",
                         mStaticInfo.isHardwareLevelAtLeastLimited());
 
@@ -180,23 +173,25 @@
      * Test for making sure that logical/physical stream requests work when both logical stream
      * and physical stream are configured.
      */
+    @Test
     public void testBasicLogicalPhysicalStreamCombination() throws Exception {
 
         for (String id : mCameraIds) {
             try {
                 Log.i(TAG, "Testing Camera " + id);
-                openDevice(id);
 
-                if (!mStaticInfo.isColorOutputSupported()) {
+                StaticMetadata staticInfo = mAllStaticInfo.get(id);
+                if (!staticInfo.isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
 
-                if (!mStaticInfo.isLogicalMultiCamera()) {
+                if (!staticInfo.isLogicalMultiCamera()) {
                     Log.i(TAG, "Camera " + id + " is not a logical multi-camera, skipping");
                     continue;
                 }
 
+                openDevice(id);
                 assertTrue("Logical multi-camera must be LIMITED or higher",
                         mStaticInfo.isHardwareLevelAtLeastLimited());
 
@@ -291,23 +286,25 @@
     /**
      * Test for making sure that multiple requests for physical cameras work as expected.
      */
+    @Test
     public void testBasicPhysicalRequests() throws Exception {
 
         for (String id : mCameraIds) {
             try {
                 Log.i(TAG, "Testing Camera " + id);
-                openDevice(id);
 
-                if (!mStaticInfo.isColorOutputSupported()) {
+                StaticMetadata staticInfo = mAllStaticInfo.get(id);
+                if (!staticInfo.isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
 
-                if (!mStaticInfo.isLogicalMultiCamera()) {
+                if (!staticInfo.isLogicalMultiCamera()) {
                     Log.i(TAG, "Camera " + id + " is not a logical multi-camera, skipping");
                     continue;
                 }
 
+                openDevice(id);
                 assertTrue("Logical multi-camera must be LIMITED or higher",
                         mStaticInfo.isHardwareLevelAtLeastLimited());
 
@@ -411,22 +408,24 @@
     /**
      * Tests invalid/incorrect multiple physical capture request cases.
      */
+    @Test
     public void testInvalidPhysicalCameraRequests() throws Exception {
 
         for (String id : mCameraIds) {
             try {
                 Log.i(TAG, "Testing Camera " + id);
-                openDevice(id);
 
-                if (mStaticInfo.isHardwareLevelLegacy()) {
+                StaticMetadata staticInfo = mAllStaticInfo.get(id);
+                if (staticInfo.isHardwareLevelLegacy()) {
                     Log.i(TAG, "Camera " + id + " is legacy, skipping");
                     continue;
                 }
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!staticInfo.isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
 
+                openDevice(id);
                 Size yuvSize = mOrderedPreviewSizes.get(0);
                 List<OutputConfiguration> outputConfigs = new ArrayList<>();
                 List<ImageReader> imageReaders = new ArrayList<>();
@@ -536,7 +535,7 @@
         }
 
         // Find display size from window service.
-        Context context = getInstrumentation().getTargetContext();
+        Context context = mActivityRule.getActivity().getApplicationContext();
         WindowManager windowManager =
                 (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         Display display = windowManager.getDefaultDisplay();
diff --git a/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java b/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
index 9bdb898..8a9daae 100644
--- a/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
@@ -34,7 +34,6 @@
 import android.media.ImageReader;
 import android.os.SystemClock;
 import android.os.ConditionVariable;
-import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 import android.util.Size;
 import android.view.Surface;
@@ -46,10 +45,11 @@
 import java.util.Arrays;
 import java.util.List;
 
+import org.junit.Test;
+
 /**
  * CameraDevice test by using combination of SurfaceView, TextureView and ImageReader
  */
-@AppModeFull
 public class MultiViewTest extends Camera2MultiViewTestCase {
     private static final String TAG = "MultiViewTest";
     private final static long WAIT_FOR_COMMAND_TO_COMPLETE = 5000; //ms
@@ -58,6 +58,7 @@
     private final static int IMG_READER_COUNT = 2;
     private final static int YUV_IMG_READER_COUNT = 3;
 
+    @Test
     public void testTextureViewPreview() throws Exception {
         for (String cameraId : mCameraIds) {
             Exception prior = null;
@@ -86,6 +87,7 @@
         }
     }
 
+    @Test
     public void testTextureViewPreviewWithImageReader() throws Exception {
         for (String cameraId : mCameraIds) {
             Exception prior = null;
@@ -133,6 +135,7 @@
         }
     }
 
+    @Test
     public void testDualTextureViewPreview() throws Exception {
         for (String cameraId : mCameraIds) {
             Exception prior = null;
@@ -165,6 +168,7 @@
         }
     }
 
+    @Test
     public void testDualTextureViewAndImageReaderPreview() throws Exception {
         for (String cameraId : mCameraIds) {
             Exception prior = null;
@@ -209,6 +213,7 @@
         }
     }
 
+    @Test
     public void testDualCameraPreview() throws Exception {
         final int NUM_CAMERAS_TESTED = 2;
         if (mCameraIds.length < NUM_CAMERAS_TESTED) {
@@ -248,6 +253,7 @@
     /*
      * Verify dynamic shared surface behavior.
      */
+    @Test
     public void testSharedSurfaceBasic() throws Exception {
         for (String cameraId : mCameraIds) {
             try {
@@ -389,6 +395,7 @@
     /*
      * Verify dynamic shared surface behavior using multiple ImageReaders.
      */
+    @Test
     public void testSharedSurfaceImageReaderSwitch() throws Exception {
         for (String cameraId : mCameraIds) {
             try {
@@ -477,6 +484,7 @@
     /*
      * Verify dynamic shared surface behavior using YUV ImageReaders.
      */
+    @Test
     public void testSharedSurfaceYUVImageReaderSwitch() throws Exception {
         int YUVFormats[] = {ImageFormat.YUV_420_888, ImageFormat.YUV_422_888,
             ImageFormat.YUV_444_888, ImageFormat.YUY2, ImageFormat.YV12,
@@ -583,6 +591,7 @@
     /*
      * Test the dynamic shared surface limit.
      */
+    @Test
     public void testSharedSurfaceLimit() throws Exception {
         for (String cameraId : mCameraIds) {
             try {
@@ -697,6 +706,7 @@
     /*
      * Test dynamic shared surface switch behavior.
      */
+    @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
     public void testSharedSurfaceSwitch() throws Exception {
         for (String cameraId : mCameraIds) {
             try {
@@ -821,6 +831,7 @@
     /*
      * Verify behavior of sharing surfaces within one OutputConfiguration
      */
+    @Test
     public void testSharedSurfaces() throws Exception {
         for (String cameraId : mCameraIds) {
             try {
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
index d54ab17..3cac3b5 100644
--- a/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
@@ -18,15 +18,18 @@
 
 import android.graphics.SurfaceTexture;
 import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
-import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 import android.util.Size;
 import android.view.Surface;
 
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+
 /**
  * <p>Basic test for CameraManager class.</p>
  */
-@AppModeFull
 public class NativeCameraDeviceTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "NativeCameraDeviceTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -38,16 +41,19 @@
         Log.i("NativeCameraDeviceTest", "after loadlibrary");
     }
 
+    @Test
     public void testCameraDeviceOpenAndClose() {
         assertTrue("testCameraDeviceOpenAndClose fail, see log for details",
                 testCameraDeviceOpenAndCloseNative());
     }
 
+    @Test
     public void testCameraDeviceCreateCaptureRequest() {
         assertTrue("testCameraDeviceCreateCaptureRequest fail, see log for details",
                 testCameraDeviceCreateCaptureRequestNative());
     }
 
+    @Test
     public void testCameraDeviceSessionOpenAndClose() {
         // Init preview surface to a guaranteed working size
         updatePreviewSurface(new Size(640, 480));
@@ -55,6 +61,7 @@
                 testCameraDeviceSessionOpenAndCloseNative(mPreviewSurface));
     }
 
+    @Test
     public void testCameraDeviceSimplePreview() {
         // Init preview surface to a guaranteed working size
         updatePreviewSurface(new Size(640, 480));
@@ -62,6 +69,7 @@
                 testCameraDeviceSimplePreviewNative(mPreviewSurface));
     }
 
+    @Test
     public void testCameraDevicePreviewWithSessionParameters() {
         // Init preview surface to a guaranteed working size
         updatePreviewSurface(new Size(640, 480));
@@ -69,6 +77,7 @@
                 testCameraDevicePreviewWithSessionParametersNative(mPreviewSurface));
     }
 
+    @Test
     public void testCameraDeviceSharedOutputUpdate() {
         // Init preview surface to a guaranteed working size
         Size previewSize = new Size(640, 480);
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeCameraManagerTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeCameraManagerTest.java
index 864b7d1..08e0363 100644
--- a/tests/camera/src/android/hardware/camera2/cts/NativeCameraManagerTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeCameraManagerTest.java
@@ -16,14 +16,12 @@
 
 package android.hardware.camera2.cts;
 
-import android.platform.test.annotations.AppModeFull;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
 /**
  * <p>Basic test for CameraManager class.</p>
  */
-@AppModeFull
 public class NativeCameraManagerTest extends AndroidTestCase {
     private static final String TAG = "NativeCameraManagerTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java
index fe01fbd..b2469a9 100644
--- a/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java
@@ -17,13 +17,11 @@
 package android.hardware.camera2.cts;
 
 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
-import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 
 /**
  * <p>Basic test for CameraManager class.</p>
  */
-@AppModeFull
 public class NativeImageReaderTest extends Camera2AndroidTestCase {
     private static final String TAG = "NativeImageReaderTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -37,7 +35,7 @@
 
     public void testJpeg() {
         assertTrue("testJpeg fail, see log for details",
-                testJpegNative(DEBUG_FILE_NAME_BASE));
+                testJpegNative(mDebugFileNameBase));
     }
 
     public void testImageReaderCloseAcquiredImages() {
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeStillCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeStillCaptureTest.java
index 8882b40..a892970 100644
--- a/tests/camera/src/android/hardware/camera2/cts/NativeStillCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeStillCaptureTest.java
@@ -17,15 +17,17 @@
 package android.hardware.camera2.cts;
 
 import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
-import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 import android.util.Size;
 import android.view.Surface;
 
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
 /**
  * <p>Basic test for CameraManager class.</p>
  */
-@AppModeFull
 public class NativeStillCaptureTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "NativeStillCaptureTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -37,11 +39,12 @@
         Log.i("NativeStillCaptureTest", "after loadlibrary");
     }
 
+    @Test
     public void testStillCapture() {
         // Init preview surface to a guaranteed working size
         updatePreviewSurface(new Size(640, 480));
         assertTrue("testStillCapture fail, see log for details",
-                testStillCaptureNative(DEBUG_FILE_NAME_BASE, mPreviewSurface));
+                testStillCaptureNative(mDebugFileNameBase, mPreviewSurface));
     }
 
     private static native boolean testStillCaptureNative(
diff --git a/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java b/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
index 9006b09..2c418e1 100644
--- a/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
@@ -18,6 +18,7 @@
 
 import static com.android.ex.camera2.blocking.BlockingSessionCallback.SESSION_CLOSED;
 
+import android.app.Instrumentation;
 import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraCaptureSession;
@@ -39,7 +40,7 @@
 import android.media.ImageWriter;
 import android.os.ConditionVariable;
 import android.os.SystemClock;
-import android.platform.test.annotations.AppModeFull;
+import android.support.test.InstrumentationRegistry;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Range;
@@ -59,11 +60,15 @@
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotNull;
+
 /**
  * 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.
  */
-@AppModeFull
 public class PerformanceTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "PerformanceTest";
     private static final String REPORT_LOG_NAME = "CtsCameraTestCases";
@@ -92,13 +97,16 @@
     private ImageWriter mWriter;
     private SimpleCaptureCallback mZslResultListener;
 
+    private Instrumentation mInstrumentation;
+
     @Override
-    protected void setUp() throws Exception {
+    public void setUp() throws Exception {
         super.setUp();
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
     }
 
     @Override
-    protected void tearDown() throws Exception {
+    public void tearDown() throws Exception {
         super.tearDown();
     }
 
@@ -116,6 +124,7 @@
      * For depth-only devices, timing is done with the DEPTH16 format instead.
      * </p>
      */
+    @Test
     public void testCameraLaunch() throws Exception {
         double[] avgCameraLaunchTimes = new double[mCameraIds.length];
 
@@ -206,7 +215,7 @@
                 closeImageReader();
             }
             counter++;
-            mReportLog.submit(getInstrumentation());
+            mReportLog.submit(mInstrumentation);
 
             if (VERBOSE) {
                 Log.v(TAG, "Camera " + id + " device open times(ms): "
@@ -246,7 +255,7 @@
             mReportLog = new DeviceReportLog(REPORT_LOG_NAME, streamName);
             mReportLog.setSummary("camera_launch_average_time_for_all_cameras",
                     Stat.getAverage(avgCameraLaunchTimes), ResultType.LOWER_BETTER, ResultUnit.MS);
-            mReportLog.submit(getInstrumentation());
+            mReportLog.submit(mInstrumentation);
         }
     }
 
@@ -263,6 +272,7 @@
      * out the capture request and getting the full capture result.
      * </p>
      */
+    @Test
     public void testSingleCapture() throws Exception {
         double[] avgResultTimes = new double[mCameraIds.length];
 
@@ -277,13 +287,12 @@
             double[] getPartialTimes = new double[NUM_TEST_LOOPS];
             double[] getResultTimes = new double[NUM_TEST_LOOPS];
             try {
-                openDevice(id);
-
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
 
+                openDevice(id);
 
                 boolean partialsExpected = mStaticInfo.getPartialResultCount() > 1;
                 long startTimeMs;
@@ -362,7 +371,7 @@
                 closeDevice();
             }
             counter++;
-            mReportLog.submit(getInstrumentation());
+            mReportLog.submit(mInstrumentation);
         }
 
         // Result will not be reported in CTS report if no summary is printed.
@@ -371,7 +380,7 @@
             mReportLog = new DeviceReportLog(REPORT_LOG_NAME, streamName);
             mReportLog.setSummary("camera_capture_result_average_latency_for_all_cameras",
                     Stat.getAverage(avgResultTimes), ResultType.LOWER_BETTER, ResultUnit.MS);
-            mReportLog.submit(getInstrumentation());
+            mReportLog.submit(mInstrumentation);
         }
     }
 
@@ -384,6 +393,7 @@
      * gap between results.
      * </p>
      */
+    @Test
     public void testMultipleCapture() throws Exception {
         double[] avgResultTimes = new double[mCameraIds.length];
         double[] avgDurationMs = new double[mCameraIds.length];
@@ -445,13 +455,12 @@
             double[] getResultTimes = new double[NUM_MAX_IMAGES];
             double[] frameDurationMs = new double[NUM_MAX_IMAGES-1];
             try {
-                openDevice(id);
-
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
 
+                openDevice(id);
                 for (int i = 0; i < NUM_TEST_LOOPS; i++) {
 
                     // setup builders and listeners
@@ -562,7 +571,7 @@
                 closeDevice();
             }
             counter++;
-            mReportLog.submit(getInstrumentation());
+            mReportLog.submit(mInstrumentation);
         }
 
         // Result will not be reported in CTS report if no summary is printed.
@@ -571,11 +580,11 @@
             mReportLog = new DeviceReportLog(REPORT_LOG_NAME, streamName);
             mReportLog.setSummary("camera_multiple_capture_result_average_latency_for_all_cameras",
                     Stat.getAverage(avgResultTimes), ResultType.LOWER_BETTER, ResultUnit.MS);
-            mReportLog.submit(getInstrumentation());
+            mReportLog.submit(mInstrumentation);
             mReportLog = new DeviceReportLog(REPORT_LOG_NAME, streamName);
             mReportLog.setSummary("camera_multiple_capture_frame_duration_average_for_all_cameras",
                     Stat.getAverage(avgDurationMs), ResultType.LOWER_BETTER, ResultUnit.MS);
-            mReportLog.submit(getInstrumentation());
+            mReportLog.submit(mInstrumentation);
         }
     }
 
@@ -583,6 +592,7 @@
      * Test reprocessing shot-to-shot latency with default NR and edge options, i.e., from the time
      * a reprocess request is issued to the time the reprocess image is returned.
      */
+    @Test
     public void testReprocessingLatency() throws Exception {
         for (String id : mCameraIds) {
             for (int format : REPROCESS_FORMATS) {
@@ -601,7 +611,7 @@
                 } finally {
                     closeReaderWriters();
                     closeDevice();
-                    mReportLog.submit(getInstrumentation());
+                    mReportLog.submit(mInstrumentation);
                 }
             }
         }
@@ -612,6 +622,7 @@
      * during a given amount of time.
      *
      */
+    @Test
     public void testReprocessingThroughput() throws Exception {
         for (String id : mCameraIds) {
             for (int format : REPROCESS_FORMATS) {
@@ -630,7 +641,7 @@
                 } finally {
                     closeReaderWriters();
                     closeDevice();
-                    mReportLog.submit(getInstrumentation());
+                    mReportLog.submit(mInstrumentation);
                 }
             }
         }
@@ -640,6 +651,7 @@
      * Test reprocessing shot-to-shot latency with High Quality NR and edge options, i.e., from the
      * time a reprocess request is issued to the time the reprocess image is returned.
      */
+    @Test
     public void testHighQualityReprocessingLatency() throws Exception {
         for (String id : mCameraIds) {
             for (int format : REPROCESS_FORMATS) {
@@ -658,7 +670,7 @@
                 } finally {
                     closeReaderWriters();
                     closeDevice();
-                    mReportLog.submit(getInstrumentation());
+                    mReportLog.submit(mInstrumentation);
                 }
             }
         }
@@ -669,6 +681,7 @@
      * be reprocessed during a given amount of time.
      *
      */
+    @Test
     public void testHighQualityReprocessingThroughput() throws Exception {
         for (String id : mCameraIds) {
             for (int format : REPROCESS_FORMATS) {
@@ -687,7 +700,7 @@
                 } finally {
                     closeReaderWriters();
                     closeDevice();
-                    mReportLog.submit(getInstrumentation());
+                    mReportLog.submit(mInstrumentation);
                 }
             }
         }
@@ -696,6 +709,7 @@
     /**
      * Testing reprocessing caused preview stall (frame drops)
      */
+    @Test
     public void testReprocessingCaptureStall() throws Exception {
         for (String id : mCameraIds) {
             for (int format : REPROCESS_FORMATS) {
@@ -713,7 +727,7 @@
                 } finally {
                     closeReaderWriters();
                     closeDevice();
-                    mReportLog.submit(getInstrumentation());
+                    mReportLog.submit(mInstrumentation);
                 }
             }
         }
diff --git a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
index 651b8b6..7340658 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
@@ -22,6 +22,7 @@
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.cts.helpers.StaticMetadata;
 import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.util.Size;
@@ -39,7 +40,6 @@
 import android.media.MediaRecorder;
 import android.os.Environment;
 import android.os.SystemClock;
-import android.platform.test.annotations.AppModeFull;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
 import android.util.Range;
@@ -50,6 +50,8 @@
 
 import junit.framework.AssertionFailedError;
 
+import org.junit.Test;
+
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -61,7 +63,6 @@
  * CameraDevice video recording use case tests by using MediaRecorder and
  * MediaCodec.
  */
-@AppModeFull
 @LargeTest
 public class RecordingTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "RecordingTest";
@@ -78,7 +79,6 @@
     private static final int BIT_RATE_MIN = 64000;
     private static final int BIT_RATE_MAX = 40000000;
     private static final int VIDEO_FRAME_RATE = 30;
-    private final String VIDEO_FILE_PATH = Environment.getExternalStorageDirectory().getPath();
     private static final int[] mCamcorderProfileList = {
             CamcorderProfile.QUALITY_HIGH,
             CamcorderProfile.QUALITY_2160P,
@@ -104,12 +104,12 @@
     private long mRecordingStartTime;
 
     @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();
     }
 
@@ -117,29 +117,30 @@
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
                 Log.i(TAG, "Testing basic recording for camera " + mCameraIds[i]);
-                // Re-use the MediaRecorder object for the same camera device.
-                mMediaRecorder = new MediaRecorder();
-                openDevice(mCameraIds[i]);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIds[i]);
+                if (!staticInfo.isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
 
                 // External camera doesn't support CamcorderProfile recording
-                if (mStaticInfo.isExternalCamera()) {
+                if (staticInfo.isExternalCamera()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " does not support CamcorderProfile, skipping");
                     continue;
                 }
 
-                if (!mStaticInfo.isVideoStabilizationSupported() && useVideoStab) {
+                if (!staticInfo.isVideoStabilizationSupported() && useVideoStab) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " does not support video stabilization, skipping the stabilization"
                             + " test");
                     continue;
                 }
 
+                // Re-use the MediaRecorder object for the same camera device.
+                mMediaRecorder = new MediaRecorder();
+                openDevice(mCameraIds[i]);
                 initSupportedVideoSize(mCameraIds[i]);
 
                 basicRecordingTestByCamera(mCamcorderProfileList, useVideoStab);
@@ -163,6 +164,7 @@
      * recorded video. Preview is set to the video size.
      * </p>
      */
+    @Test
     public void testBasicVideoStabilizationRecording() throws Exception {
         doBasicRecording(/*useVideoStab*/true);
     }
@@ -179,6 +181,7 @@
      * recorded video. Preview is set to the video size.
      * </p>
      */
+    @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
     public void testBasicRecording() throws Exception {
         doBasicRecording(/*useVideoStab*/false);
     }
@@ -192,6 +195,7 @@
      * from a persistent input surface that's used across multiple recording sessions.
      * </p>
      */
+    @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
     public void testRecordingFromPersistentSurface() throws Exception {
         if (!MediaUtils.checkCodecForDomain(true /* encoder */, "video")) {
             return; // skipped
@@ -217,18 +221,20 @@
      * validated according to the recording configuration.
      * </p>
      */
+    @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++) {
             try {
                 Log.i(TAG, "Testing supported video size recording for camera " + mCameraIds[i]);
-                // Re-use the MediaRecorder object for the same camera device.
-                mMediaRecorder = new MediaRecorder();
-                openDevice(mCameraIds[i]);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
+                // Re-use the MediaRecorder object for the same camera device.
+                mMediaRecorder = new MediaRecorder();
+                openDevice(mCameraIds[i]);
+
                 initSupportedVideoSize(mCameraIds[i]);
 
                 recordingSizeTestByCamera();
@@ -244,6 +250,7 @@
      *
      * <p>The recording should be working fine for any kind of start/stop orders.</p>
      */
+    @Test
     public void testCameraRecorderOrdering() {
         // TODO: need implement
     }
@@ -258,6 +265,7 @@
      * validated according to the recording configuration.
      * </p>
      */
+    @Test
     public void testMediaCodecRecording() throws Exception {
         // TODO. Need implement.
     }
@@ -274,6 +282,7 @@
      * checked to make sure no frame drop caused by video snapshot.
      * </p>
      */
+    @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
     public void testVideoSnapshot() throws Exception {
         videoSnapshotHelper(/*burstTest*/false);
     }
@@ -290,6 +299,7 @@
      * configuration.
      * </p>
      */
+    @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
     public void testBurstVideoSnapshot() throws Exception {
         videoSnapshotHelper(/*burstTest*/true);
     }
@@ -297,36 +307,42 @@
     /**
      * Test timelapse recording, where capture rate is slower than video (playback) frame rate.
      */
+    @Test
     public void testTimelapseRecording() throws Exception {
         // TODO. Need implement.
     }
 
+    @Test
     public void testSlowMotionRecording() throws Exception {
         slowMotionRecording();
     }
 
+    @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
     public void testConstrainedHighSpeedRecording() throws Exception {
         constrainedHighSpeedRecording();
     }
 
+    @Test
     public void testAbandonedHighSpeedRequest() throws Exception {
         for (String id : mCameraIds) {
             try {
                 Log.i(TAG, "Testing bad suface for createHighSpeedRequestList for camera " + id);
-                // Re-use the MediaRecorder object for the same camera device.
-                mMediaRecorder = new MediaRecorder();
-                openDevice(id);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                StaticMetadata staticInfo = mAllStaticInfo.get(id);
+                if (!staticInfo.isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id +
                             " does not support color outputs, skipping");
                     continue;
                 }
-                if (!mStaticInfo.isConstrainedHighSpeedVideoSupported()) {
+                if (!staticInfo.isConstrainedHighSpeedVideoSupported()) {
                     Log.i(TAG, "Camera " + id +
                             " does not support constrained high speed video, skipping");
                     continue;
                 }
 
+                // Re-use the MediaRecorder object for the same camera device.
+                mMediaRecorder = new MediaRecorder();
+                openDevice(id);
+
                 StreamConfigurationMap config =
                         mStaticInfo.getValueFromKeyNonNull(
                                 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
@@ -348,7 +364,7 @@
                     continue;
                 }
 
-                mOutMediaFileName = VIDEO_FILE_PATH + "/test_video.mp4";
+                mOutMediaFileName = mDebugFileNameBase + "/test_video.mp4";
                 prepareRecording(size, videoFramerate, captureRate);
                 updatePreviewSurfaceWithVideo(size, captureRate);
 
@@ -428,23 +444,26 @@
      * profile of highest framerate. Make sure that the video framerate are still accurate.
      * </p>
      */
+    @Test
     public void testRecordingFramerateLowToHigh() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
                 Log.i(TAG, "Testing basic recording for camera " + mCameraIds[i]);
-                // Re-use the MediaRecorder object for the same camera device.
-                mMediaRecorder = new MediaRecorder();
-                openDevice(mCameraIds[i]);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIds[i]);
+                if (!staticInfo.isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
-                if (mStaticInfo.isExternalCamera()) {
+                if (staticInfo.isExternalCamera()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " does not support CamcorderProfile, skipping");
                     continue;
                 }
+                // Re-use the MediaRecorder object for the same camera device.
+                mMediaRecorder = new MediaRecorder();
+                openDevice(mCameraIds[i]);
+
                 initSupportedVideoSize(mCameraIds[i]);
 
                 int minFpsProfileId = -1, minFps = 1000;
@@ -480,21 +499,23 @@
      * Test preview and video surfaces sharing the same camera stream.
      * </p>
      */
+    @Test
     public void testVideoPreviewSurfaceSharing() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
-                // Re-use the MediaRecorder object for the same camera device.
-                mMediaRecorder = new MediaRecorder();
-                openDevice(mCameraIds[i]);
-                if (mStaticInfo.isHardwareLevelLegacy()) {
+                StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIds[i]);
+                if (staticInfo.isHardwareLevelLegacy()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] + " is legacy, skipping");
                     continue;
                 }
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!staticInfo.isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
+                // Re-use the MediaRecorder object for the same camera device.
+                mMediaRecorder = new MediaRecorder();
+                openDevice(mCameraIds[i]);
 
                 initSupportedVideoSize(mCameraIds[i]);
 
@@ -520,10 +541,10 @@
             }
 
             // Configure preview and recording surfaces.
-            mOutMediaFileName = VIDEO_FILE_PATH + "/test_video_share.mp4";
+            mOutMediaFileName = mDebugFileNameBase + "/test_video_share.mp4";
             if (DEBUG_DUMP) {
-                mOutMediaFileName = VIDEO_FILE_PATH + "/test_video_share_" + mCamera.getId() + "_"
-                        + sz.toString() + ".mp4";
+                mOutMediaFileName = mDebugFileNameBase + "/test_video_share_" + mCamera.getId() +
+                    "_" + sz.toString() + ".mp4";
             }
 
             // Allow external camera to use variable fps range
@@ -601,18 +622,20 @@
         for (String id : mCameraIds) {
             try {
                 Log.i(TAG, "Testing slow motion recording for camera " + id);
-                // Re-use the MediaRecorder object for the same camera device.
-                mMediaRecorder = new MediaRecorder();
-                openDevice(id);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                StaticMetadata staticInfo = mAllStaticInfo.get(id);
+                if (!staticInfo.isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id +
                             " does not support color outputs, skipping");
                     continue;
                 }
-                if (!mStaticInfo.isHighSpeedVideoSupported()) {
+                if (!staticInfo.isHighSpeedVideoSupported()) {
                     continue;
                 }
 
+                // Re-use the MediaRecorder object for the same camera device.
+                mMediaRecorder = new MediaRecorder();
+                openDevice(id);
+
                 StreamConfigurationMap config =
                         mStaticInfo.getValueFromKeyNonNull(
                                 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
@@ -634,9 +657,9 @@
                         continue;
                     }
 
-                    mOutMediaFileName = VIDEO_FILE_PATH + "/test_slowMo_video.mp4";
+                    mOutMediaFileName = mDebugFileNameBase + "/test_slowMo_video.mp4";
                     if (DEBUG_DUMP) {
-                        mOutMediaFileName = VIDEO_FILE_PATH + "/test_slowMo_video_" + id + "_"
+                        mOutMediaFileName = mDebugFileNameBase + "/test_slowMo_video_" + id + "_"
                                 + size.toString() + ".mp4";
                     }
 
@@ -674,15 +697,16 @@
         for (String id : mCameraIds) {
             try {
                 Log.i(TAG, "Testing constrained high speed recording for camera " + id);
-                // Re-use the MediaRecorder object for the same camera device.
-                mMediaRecorder = new MediaRecorder();
-                openDevice(id);
 
-                if (!mStaticInfo.isConstrainedHighSpeedVideoSupported()) {
+                if (!mAllStaticInfo.get(id).isConstrainedHighSpeedVideoSupported()) {
                     Log.i(TAG, "Camera " + id + " doesn't support high speed recording, skipping.");
                     continue;
                 }
 
+                // Re-use the MediaRecorder object for the same camera device.
+                mMediaRecorder = new MediaRecorder();
+                openDevice(id);
+
                 StreamConfigurationMap config =
                         mStaticInfo.getValueFromKeyNonNull(
                                 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
@@ -710,8 +734,8 @@
 
                         startConstrainedPreview(fpsRange, previewResultListener);
 
-                        mOutMediaFileName = VIDEO_FILE_PATH + "/test_cslowMo_video_" + captureRate +
-                                "fps_" + id + "_" + size.toString() + ".mp4";
+                        mOutMediaFileName = mDebugFileNameBase + "/test_cslowMo_video_" +
+                            captureRate + "fps_" + id + "_" + size.toString() + ".mp4";
 
                         prepareRecording(size, VIDEO_FRAME_RATE, captureRate);
 
@@ -942,9 +966,9 @@
             }
 
             // Configure preview and recording surfaces.
-            mOutMediaFileName = VIDEO_FILE_PATH + "/test_video.mp4";
+            mOutMediaFileName = mDebugFileNameBase + "/test_video.mp4";
             if (DEBUG_DUMP) {
-                mOutMediaFileName = VIDEO_FILE_PATH + "/test_video_" + cameraId + "_"
+                mOutMediaFileName = mDebugFileNameBase + "/test_video_" + cameraId + "_"
                         + videoSz.toString() + ".mp4";
             }
 
@@ -996,9 +1020,9 @@
             }
 
             // Configure preview and recording surfaces.
-            mOutMediaFileName = VIDEO_FILE_PATH + "/test_video.mp4";
+            mOutMediaFileName = mDebugFileNameBase + "/test_video.mp4";
             if (DEBUG_DUMP) {
-                mOutMediaFileName = VIDEO_FILE_PATH + "/test_video_" + mCamera.getId() + "_"
+                mOutMediaFileName = mDebugFileNameBase + "/test_video_" + mCamera.getId() + "_"
                         + sz.toString() + ".mp4";
             }
 
@@ -1074,23 +1098,25 @@
             for (String id : mCameraIds) {
                 try {
                     Log.i(TAG, "Testing video snapshot for camera " + id);
-                    // Re-use the MediaRecorder object for the same camera device.
-                    mMediaRecorder = new MediaRecorder();
 
-                    openDevice(id);
-
-                    if (!mStaticInfo.isColorOutputSupported()) {
+                    StaticMetadata staticInfo = mAllStaticInfo.get(id);
+                    if (!staticInfo.isColorOutputSupported()) {
                         Log.i(TAG, "Camera " + id +
                                 " does not support color outputs, skipping");
                         continue;
                     }
 
-                    if (mStaticInfo.isExternalCamera()) {
+                    if (staticInfo.isExternalCamera()) {
                         Log.i(TAG, "Camera " + id +
                                 " does not support CamcorderProfile, skipping");
                         continue;
                     }
 
+                    // Re-use the MediaRecorder object for the same camera device.
+                    mMediaRecorder = new MediaRecorder();
+
+                    openDevice(id);
+
                     initSupportedVideoSize(id);
 
                     videoSnapshotTestByCamera(burstTest);
@@ -1258,9 +1284,9 @@
             }
 
             // Configure preview and recording surfaces.
-            mOutMediaFileName = VIDEO_FILE_PATH + "/test_video.mp4";
+            mOutMediaFileName = mDebugFileNameBase + "/test_video.mp4";
             if (DEBUG_DUMP) {
-                mOutMediaFileName = VIDEO_FILE_PATH + "/test_video_" + cameraId + "_"
+                mOutMediaFileName = mDebugFileNameBase + "/test_video_" + cameraId + "_"
                         + videoSz.toString() + ".mp4";
             }
 
diff --git a/tests/camera/src/android/hardware/camera2/cts/ReprocessCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
index f30ca92..8fa1077 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
@@ -32,7 +32,6 @@
 import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
 import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
 import android.hardware.camera2.params.InputConfiguration;
-import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 import android.util.Size;
 import android.view.Surface;
@@ -44,10 +43,11 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import org.junit.Test;
+
 /**
  * <p>Tests for Reprocess API.</p>
  */
-@AppModeFull
 public class ReprocessCaptureTest extends Camera2SurfaceViewTestCase  {
     private static final String TAG = "ReprocessCaptureTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -88,6 +88,7 @@
     /**
      * Test YUV_420_888 -> YUV_420_888 with maximal supported sizes
      */
+    @Test
     public void testBasicYuvToYuvReprocessing() throws Exception {
         for (String id : mCameraIds) {
             if (!isYuvReprocessSupported(id)) {
@@ -102,6 +103,7 @@
     /**
      * Test YUV_420_888 -> JPEG with maximal supported sizes
      */
+    @Test
     public void testBasicYuvToJpegReprocessing() throws Exception {
         for (String id : mCameraIds) {
             if (!isYuvReprocessSupported(id)) {
@@ -116,6 +118,7 @@
     /**
      * Test OPAQUE -> YUV_420_888 with maximal supported sizes
      */
+    @Test
     public void testBasicOpaqueToYuvReprocessing() throws Exception {
         for (String id : mCameraIds) {
             if (!isOpaqueReprocessSupported(id)) {
@@ -130,6 +133,7 @@
     /**
      * Test OPAQUE -> JPEG with maximal supported sizes
      */
+    @Test
     public void testBasicOpaqueToJpegReprocessing() throws Exception {
         for (String id : mCameraIds) {
             if (!isOpaqueReprocessSupported(id)) {
@@ -144,6 +148,7 @@
     /**
      * Test all supported size and format combinations.
      */
+    @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
     public void testReprocessingSizeFormat() throws Exception {
         for (String id : mCameraIds) {
             if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
@@ -165,6 +170,7 @@
     /**
      * Test all supported size and format combinations with preview.
      */
+    @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
     public void testReprocessingSizeFormatWithPreview() throws Exception {
         for (String id : mCameraIds) {
             if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
@@ -185,6 +191,7 @@
     /**
      * Test recreating reprocessing sessions.
      */
+    @Test
     public void testRecreateReprocessingSessions() throws Exception {
         for (String id : mCameraIds) {
             if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
@@ -222,6 +229,7 @@
     /**
      * Verify issuing cross session capture requests is invalid.
      */
+    @Test
     public void testCrossSessionCaptureException() throws Exception {
         for (String id : mCameraIds) {
             // Test one supported input format -> JPEG
@@ -291,6 +299,7 @@
     /**
      * Test burst reprocessing captures with and without preview.
      */
+    @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
     public void testBurstReprocessing() throws Exception {
         for (String id : mCameraIds) {
             if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
@@ -314,6 +323,7 @@
     /**
      * Test burst captures mixed with regular and reprocess captures with and without preview.
      */
+    @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
     public void testMixedBurstReprocessing() throws Exception {
         for (String id : mCameraIds) {
             if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
@@ -339,6 +349,7 @@
      * Test aborting reprocess capture requests of the largest input and output sizes for each
      * supported format.
      */
+    @Test
     public void testReprocessAbort() throws Exception {
         for (String id : mCameraIds) {
             if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
@@ -368,6 +379,7 @@
     /**
      * Test reprocess timestamps for the largest input and output sizes for each supported format.
      */
+    @Test
     public void testReprocessTimestamps() throws Exception {
         for (String id : mCameraIds) {
             if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
@@ -398,6 +410,7 @@
      * Test reprocess jpeg output's exif data for the largest input and output sizes for each
      * supported format.
      */
+    @Test
     public void testReprocessJpegExif() throws Exception {
         for (String id : mCameraIds) {
             if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
@@ -428,6 +441,7 @@
         }
     }
 
+    @Test
     public void testReprocessRequestKeys() throws Exception {
         for (String id : mCameraIds) {
             if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
@@ -963,7 +977,8 @@
                 // Verify output image's and result's JPEG EXIF data.
                 Image image = getReprocessOutputImageReaderListener().getImage(CAPTURE_TIMEOUT_MS);
                 verifyJpegKeys(image, reprocessResults[i], reprocessOutputSize,
-                        testThumbnailSizes[i], EXIF_TEST_DATA[i], mStaticInfo, mCollector);
+                        testThumbnailSizes[i], EXIF_TEST_DATA[i], mStaticInfo, mCollector,
+                        mDebugFileNameBase);
                 image.close();
 
             }
@@ -1371,7 +1386,7 @@
     }
 
     private void dumpImage(Image image, String name) {
-        String filename = DEBUG_FILE_NAME_BASE + name;
+        String filename = mDebugFileNameBase + name;
         switch(image.getFormat()) {
             case ImageFormat.JPEG:
                 filename += ".jpg";
diff --git a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index 47020ed..ca02e05 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -40,7 +40,6 @@
 import android.media.Image;
 import android.media.ImageReader;
 import android.media.ImageWriter;
-import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 import android.util.Size;
 import android.view.Display;
@@ -62,7 +61,6 @@
 /**
  * Tests exercising edge cases in camera setup, configuration, and usage.
  */
-@AppModeFull
 public class RobustnessTest extends Camera2AndroidTestCase {
     private static final String TAG = "RobustnessTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -443,19 +441,20 @@
         for (String id : mCameraIds) {
             Log.i(TAG, String.format("Testing Camera %s", id));
 
-            openDevice(id);
             try {
                 // Legacy devices do not support precapture trigger; don't test devices that
                 // can't focus
-                if (mStaticInfo.isHardwareLevelLegacy() || !mStaticInfo.hasFocuser()) {
+                StaticMetadata staticInfo = mAllStaticInfo.get(id);
+                if (staticInfo.isHardwareLevelLegacy() || !staticInfo.hasFocuser()) {
                     continue;
                 }
                 // Depth-only devices won't support AE
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!staticInfo.isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
 
+                openDevice(id);
                 int[] availableAfModes = mStaticInfo.getAfAvailableModesChecked();
                 int[] availableAeModes = mStaticInfo.getAeAvailableModesChecked();
 
@@ -581,19 +580,20 @@
         for (String id : mCameraIds) {
             Log.i(TAG, String.format("Testing Camera %s", id));
 
-            openDevice(id);
             try {
                 // Legacy devices do not support precapture trigger; don't test devices that
                 // can't focus
-                if (mStaticInfo.isHardwareLevelLegacy() || !mStaticInfo.hasFocuser()) {
+                StaticMetadata staticInfo = mAllStaticInfo.get(id);
+                if (staticInfo.isHardwareLevelLegacy() || !staticInfo.hasFocuser()) {
                     continue;
                 }
                 // Depth-only devices won't support AE
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!staticInfo.isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
 
+                openDevice(id);
                 int[] availableAfModes = mStaticInfo.getAfAvailableModesChecked();
                 int[] availableAeModes = mStaticInfo.getAeAvailableModesChecked();
 
@@ -682,19 +682,20 @@
         for (String id : mCameraIds) {
             Log.i(TAG, String.format("Testing Camera %s", id));
 
-            openDevice(id);
             try {
                 // Legacy devices do not support precapture trigger; don't test devices that
                 // can't focus
-                if (mStaticInfo.isHardwareLevelLegacy() || !mStaticInfo.hasFocuser()) {
+                StaticMetadata staticInfo = mAllStaticInfo.get(id);
+                if (staticInfo.isHardwareLevelLegacy() || !staticInfo.hasFocuser()) {
                     continue;
                 }
                 // Depth-only devices won't support AE
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!staticInfo.isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
 
+                openDevice(id);
                 int[] availableAfModes = mStaticInfo.getAfAvailableModesChecked();
                 int[] availableAeModes = mStaticInfo.getAeAvailableModesChecked();
 
@@ -797,19 +798,20 @@
         for (String id : mCameraIds) {
             Log.i(TAG, String.format("Testing Camera %s", id));
 
-            openDevice(id);
             try {
                 // Legacy devices do not support precapture trigger; don't test devices that
                 // can't focus
-                if (mStaticInfo.isHardwareLevelLegacy() || !mStaticInfo.hasFocuser()) {
+                StaticMetadata staticInfo = mAllStaticInfo.get(id);
+                if (staticInfo.isHardwareLevelLegacy() || !staticInfo.hasFocuser()) {
                     continue;
                 }
                 // Depth-only devices won't support AE
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!staticInfo.isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
 
+                openDevice(id);
                 int[] availableAfModes = mStaticInfo.getAfAvailableModesChecked();
                 int[] availableAeModes = mStaticInfo.getAeAvailableModesChecked();
 
@@ -908,6 +910,188 @@
         }
     }
 
+    public void testAeAndAfCausality() throws Exception {
+
+        for (String id : mCameraIds) {
+            Log.i(TAG, String.format("Testing Camera %s", id));
+
+            try {
+                // Legacy devices do not support precapture trigger; don't test devices that
+                // can't focus
+                StaticMetadata staticInfo = mAllStaticInfo.get(id);
+                if (staticInfo.isHardwareLevelLegacy() || !staticInfo.hasFocuser()) {
+                    continue;
+                }
+                // Depth-only devices won't support AE
+                if (!staticInfo.isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
+                    continue;
+                }
+
+                openDevice(id);
+                int[] availableAfModes = mStaticInfo.getAfAvailableModesChecked();
+                int[] availableAeModes = mStaticInfo.getAeAvailableModesChecked();
+                final int maxPipelineDepth = mStaticInfo.getCharacteristics().get(
+                        CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH);
+
+                for (int afMode : availableAfModes) {
+                    if (afMode == CameraCharacteristics.CONTROL_AF_MODE_OFF ||
+                            afMode == CameraCharacteristics.CONTROL_AF_MODE_EDOF) {
+                        // Only test AF modes that have meaningful trigger behavior
+                        continue;
+                    }
+                    for (int aeMode : availableAeModes) {
+                        if (aeMode ==  CameraCharacteristics.CONTROL_AE_MODE_OFF) {
+                            // Only test AE modes that have meaningful trigger behavior
+                            continue;
+                        }
+
+                        SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1);
+
+                        CaptureRequest.Builder previewRequest =
+                                prepareTriggerTestSession(preview, aeMode, afMode);
+
+                        SimpleCaptureCallback captureListener =
+                                new CameraTestUtils.SimpleCaptureCallback();
+
+                        mCameraSession.setRepeatingRequest(previewRequest.build(), captureListener,
+                                mHandler);
+
+                        List<CaptureRequest> triggerRequests =
+                                new ArrayList<CaptureRequest>(maxPipelineDepth+1);
+                        for (int i = 0; i < maxPipelineDepth; i++) {
+                            triggerRequests.add(previewRequest.build());
+                        }
+
+                        // Cancel triggers
+                        cancelTriggersAndWait(previewRequest, captureListener, afMode);
+
+                        //
+                        // Standard sequence - Part 1 AF trigger
+
+                        if (VERBOSE) {
+                            Log.v(TAG, String.format("Triggering AF"));
+                        }
+
+                        previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER,
+                                CaptureRequest.CONTROL_AF_TRIGGER_START);
+                        previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
+                                CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
+                        triggerRequests.add(previewRequest.build());
+
+                        mCameraSession.captureBurst(triggerRequests, captureListener, mHandler);
+
+                        TotalCaptureResult[] triggerResults =
+                                captureListener.getTotalCaptureResultsForRequests(
+                                triggerRequests, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES);
+                        for (int i = 0; i < maxPipelineDepth; i++) {
+                            TotalCaptureResult triggerResult = triggerResults[i];
+                            int afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE);
+                            int afTrigger = triggerResult.get(CaptureResult.CONTROL_AF_TRIGGER);
+
+                            verifyStartingAfState(afMode, afState);
+                            assertTrue(String.format("In AF mode %s, previous AF_TRIGGER must not "
+                                    + "be START before TRIGGER_START",
+                                    StaticMetadata.getAfModeName(afMode)),
+                                    afTrigger != CaptureResult.CONTROL_AF_TRIGGER_START);
+                        }
+
+                        int afState =
+                                triggerResults[maxPipelineDepth].get(CaptureResult.CONTROL_AF_STATE);
+                        boolean focusComplete = false;
+                        for (int i = 0;
+                             i < MAX_TRIGGER_SEQUENCE_FRAMES && !focusComplete;
+                             i++) {
+
+                            focusComplete = verifyAfSequence(afMode, afState, focusComplete);
+
+                            CaptureResult focusResult = captureListener.getCaptureResult(
+                                    CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
+                            afState = focusResult.get(CaptureResult.CONTROL_AF_STATE);
+                        }
+
+                        assertTrue("Focusing never completed!", focusComplete);
+
+                        // Standard sequence - Part 2 AE trigger
+
+                        if (VERBOSE) {
+                            Log.v(TAG, String.format("Triggering AE"));
+                        }
+                        // Remove AF trigger request
+                        triggerRequests.remove(maxPipelineDepth);
+
+                        previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER,
+                                CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
+                        previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
+                                CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
+                        triggerRequests.add(previewRequest.build());
+
+                        mCameraSession.captureBurst(triggerRequests, captureListener, mHandler);
+
+                        triggerResults = captureListener.getTotalCaptureResultsForRequests(
+                                triggerRequests, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES);
+
+                        for (int i = 0; i < maxPipelineDepth; i++) {
+                            TotalCaptureResult triggerResult = triggerResults[i];
+                            int aeState = triggerResult.get(CaptureResult.CONTROL_AE_STATE);
+                            int aeTrigger = triggerResult.get(
+                                    CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER);
+
+                            assertTrue(String.format("In AE mode %s, previous AE_TRIGGER must not "
+                                    + "be START before TRIGGER_START",
+                                    StaticMetadata.getAeModeName(aeMode)),
+                                    aeTrigger != CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER_START);
+                            assertTrue(String.format("In AE mode %s, previous AE_STATE must not be"
+                                    + " PRECAPTURE_TRIGGER before TRIGGER_START",
+                                    StaticMetadata.getAeModeName(aeMode)),
+                                    aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE);
+                        }
+
+                        // Stand sequence - Part 3 Cancel AF trigger
+                        if (VERBOSE) {
+                            Log.v(TAG, String.format("Cancel AF trigger"));
+                        }
+                        // Remove AE trigger request
+                        triggerRequests.remove(maxPipelineDepth);
+                        previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER,
+                                CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
+                        triggerRequests.add(previewRequest.build());
+
+                        mCameraSession.captureBurst(triggerRequests, captureListener, mHandler);
+                        triggerResults = captureListener.getTotalCaptureResultsForRequests(
+                                triggerRequests, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES);
+                        for (int i = 0; i < maxPipelineDepth; i++) {
+                            TotalCaptureResult triggerResult = triggerResults[i];
+                            afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE);
+                            int afTrigger = triggerResult.get(CaptureResult.CONTROL_AF_TRIGGER);
+
+                            assertTrue(
+                                    String.format("In AF mode %s, previous AF_TRIGGER must not " +
+                                    "be CANCEL before TRIGGER_CANCEL",
+                                    StaticMetadata.getAfModeName(afMode)),
+                                    afTrigger != CaptureResult.CONTROL_AF_TRIGGER_CANCEL);
+                            assertTrue(
+                                    String.format("In AF mode %s, previous AF_STATE must be LOCKED"
+                                    + " before CANCEL, but is %s",
+                                    StaticMetadata.getAfModeName(afMode),
+                                    StaticMetadata.AF_STATE_NAMES[afState]),
+                                    afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
+                                    afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED);
+                        }
+
+                        stopCapture(/*fast*/ false);
+                        preview.release();
+                    }
+
+                }
+
+            } finally {
+                closeDevice(id);
+            }
+        }
+
+    }
+
     public void testAbandonRepeatingRequestSurface() throws Exception {
         for (String id : mCameraIds) {
             Log.i(TAG, String.format(
@@ -1207,6 +1391,17 @@
         afState = previewResult.get(CaptureResult.CONTROL_AF_STATE);
         aeState = previewResult.get(CaptureResult.CONTROL_AE_STATE);
 
+        verifyStartingAfState(afMode, afState);
+
+        // After several frames, AE must no longer be in INACTIVE state
+        assertTrue(String.format("AE state must be SEARCHING, CONVERGED, " +
+                        "or FLASH_REQUIRED, is %s", StaticMetadata.AE_STATE_NAMES[aeState]),
+                aeState == CaptureResult.CONTROL_AE_STATE_SEARCHING ||
+                aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED ||
+                aeState == CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED);
+    }
+
+    private void verifyStartingAfState(int afMode, int afState) {
         switch (afMode) {
             case CaptureResult.CONTROL_AF_MODE_AUTO:
             case CaptureResult.CONTROL_AF_MODE_MACRO:
@@ -1228,13 +1423,6 @@
             default:
                 fail("unexpected af mode");
         }
-
-        // After several frames, AE must no longer be in INACTIVE state
-        assertTrue(String.format("AE state must be SEARCHING, CONVERGED, " +
-                        "or FLASH_REQUIRED, is %s", StaticMetadata.AE_STATE_NAMES[aeState]),
-                aeState == CaptureResult.CONTROL_AE_STATE_SEARCHING ||
-                aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED ||
-                aeState == CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED);
     }
 
     private boolean verifyAfSequence(int afMode, int afState, boolean focusComplete) {
diff --git a/tests/camera/src/android/hardware/camera2/cts/StaticMetadataTest.java b/tests/camera/src/android/hardware/camera2/cts/StaticMetadataTest.java
index 00ed180..5eddfd1 100644
--- a/tests/camera/src/android/hardware/camera2/cts/StaticMetadataTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/StaticMetadataTest.java
@@ -29,7 +29,6 @@
 import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
 import android.hardware.camera2.params.StreamConfigurationMap;
-import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Size;
@@ -51,7 +50,6 @@
  * Note that most of the tests in this class don't require camera open.
  * </p>
  */
-@AppModeFull
 public class StaticMetadataTest extends Camera2AndroidTestCase {
     private static final String TAG = "StaticMetadataTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
diff --git a/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
index e77ad79..0fd0cdca 100644
--- a/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
@@ -37,11 +37,11 @@
 import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
 import android.hardware.camera2.cts.CameraTestUtils.SimpleImageReaderListener;
 import android.hardware.camera2.cts.helpers.Camera2Focuser;
+import android.hardware.camera2.cts.helpers.StaticMetadata;
 import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
 import android.hardware.camera2.params.MeteringRectangle;
 import android.media.Image;
 import android.os.ConditionVariable;
-import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 import android.util.Range;
 import android.util.Rational;
@@ -55,7 +55,8 @@
 import java.util.Arrays;
 import java.util.List;
 
-@AppModeFull
+import org.junit.Test;
+
 public class StillCaptureTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "StillCaptureTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -74,28 +75,29 @@
     private static final int MAX_ALLOCATED_BITMAPS = 3;
 
     @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 JPEG capture exif fields for each camera.
      */
+    @Test
     public void testJpegExif() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
                 Log.i(TAG, "Testing JPEG exif for Camera " + mCameraIds[i]);
-                openDevice(mCameraIds[i]);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(mCameraIds[i]);
                 jpegExifTestByCamera();
             } finally {
                 closeDevice();
@@ -114,15 +116,16 @@
      * is CONTINUOUS_PICTURE.
      * </p>
      */
+    @Test
     public void testTakePicture() throws Exception{
         for (String id : mCameraIds) {
             try {
                 Log.i(TAG, "Testing basic take picture for Camera " + id);
-                openDevice(id);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(id);
                 takePictureTestByCamera(/*aeRegions*/null, /*awbRegions*/null, /*afRegions*/null);
             } finally {
                 closeDevice();
@@ -141,15 +144,16 @@
      * is CONTINUOUS_PICTURE. Same as testTakePicture, but with enableZSL set.
      * </p>
      */
+    @Test
     public void testTakePictureZsl() throws Exception{
         for (String id : mCameraIds) {
             try {
                 Log.i(TAG, "Testing basic ZSL take picture for Camera " + id);
-                openDevice(id);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(id);
                 CaptureRequest.Builder stillRequest =
                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
                 stillRequest.set(CaptureRequest.CONTROL_ENABLE_ZSL, true);
@@ -166,19 +170,20 @@
     /**
      * Test basic Raw capture. Raw buffer avaiablility is checked, but raw buffer data is not.
      */
+    @Test
     public void testBasicRawCapture()  throws Exception {
        for (int i = 0; i < mCameraIds.length; i++) {
            try {
                Log.i(TAG, "Testing raw capture for Camera " + mCameraIds[i]);
-               openDevice(mCameraIds[i]);
 
-               if (!mStaticInfo.isCapabilitySupported(
+               if (!mAllStaticInfo.get(mCameraIds[i]).isCapabilitySupported(
                        CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
                    Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
                            ". Skip the test.");
                    continue;
                }
 
+               openDevice(mCameraIds[i]);
                rawCaptureTestByCamera(/*stillRequest*/null);
            } finally {
                closeDevice();
@@ -190,18 +195,19 @@
     /**
      * Test basic Raw ZSL capture. Raw buffer avaiablility is checked, but raw buffer data is not.
      */
+    @Test
     public void testBasicRawZslCapture()  throws Exception {
        for (int i = 0; i < mCameraIds.length; i++) {
            try {
                Log.i(TAG, "Testing raw ZSL capture for Camera " + mCameraIds[i]);
-               openDevice(mCameraIds[i]);
 
-               if (!mStaticInfo.isCapabilitySupported(
+               if (!mAllStaticInfo.get(mCameraIds[i]).isCapabilitySupported(
                        CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
                    Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
                            ". Skip the test.");
                    continue;
                }
+               openDevice(mCameraIds[i]);
                CaptureRequest.Builder stillRequest =
                        mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
                stillRequest.set(CaptureRequest.CONTROL_ENABLE_ZSL, true);
@@ -222,18 +228,19 @@
      * - Running preview until AE/AF can settle.
      * - Capturing with a request targeting all three output streams.
      */
+    @Test
     public void testFullRawCapture() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
                 Log.i(TAG, "Testing raw+JPEG capture for Camera " + mCameraIds[i]);
-                openDevice(mCameraIds[i]);
-                if (!mStaticInfo.isCapabilitySupported(
+                if (!mAllStaticInfo.get(mCameraIds[i]).isCapabilitySupported(
                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
                     Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
                             ". Skip the test.");
                     continue;
                 }
 
+                openDevice(mCameraIds[i]);
                 fullRawCaptureTestByCamera(/*stillRequest*/null);
             } finally {
                 closeDevice();
@@ -250,17 +257,18 @@
      * - Running preview until AE/AF can settle.
      * - Capturing with a request targeting all three output streams.
      */
+    @Test
     public void testFullRawZSLCapture() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
                 Log.i(TAG, "Testing raw+JPEG ZSL capture for Camera " + mCameraIds[i]);
-                openDevice(mCameraIds[i]);
-                if (!mStaticInfo.isCapabilitySupported(
+                if (!mAllStaticInfo.get(mCameraIds[i]).isCapabilitySupported(
                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
                     Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
                             ". Skip the test.");
                     continue;
                 }
+                openDevice(mCameraIds[i]);
                 CaptureRequest.Builder stillRequest =
                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
                 stillRequest.set(CaptureRequest.CONTROL_ENABLE_ZSL, true);
@@ -280,20 +288,22 @@
      * converges in reasonable time.
      * </p>
      */
+    @Test
     public void testTouchForFocus() throws Exception {
         for (String id : mCameraIds) {
             try {
                 Log.i(TAG, "Testing touch for focus for Camera " + id);
-                openDevice(id);
-                int maxAfRegions = mStaticInfo.getAfMaxRegionsChecked();
-                if (!(mStaticInfo.hasFocuser() && maxAfRegions > 0)) {
+                StaticMetadata staticInfo = mAllStaticInfo.get(id);
+                int maxAfRegions = staticInfo.getAfMaxRegionsChecked();
+                if (!(staticInfo.hasFocuser() && maxAfRegions > 0)) {
                     continue;
                 }
                 // TODO: Relax test to use non-SurfaceView output for depth cases
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!staticInfo.isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(id);
                 touchForFocusTestByCamera();
             } finally {
                 closeDevice();
@@ -309,15 +319,16 @@
      * result validation is covered by {@link #jpegExifTestByCamera} test.
      * </p>
      */
+    @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
     public void testStillPreviewCombination() throws Exception {
         for (String id : mCameraIds) {
             try {
                 Log.i(TAG, "Testing Still preview capture combination for Camera " + id);
-                openDevice(id);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(id);
                 previewStillCombinationTestByCamera();
             } finally {
                 closeDevice();
@@ -336,20 +347,22 @@
      * compensation settings is changed, even when AE lock is ON.
      * </p>
      */
+    @Test
     public void testAeCompensation() throws Exception {
         for (String id : mCameraIds) {
             try {
                 Log.i(TAG, "Testing AE compensation for Camera " + id);
-                openDevice(id);
 
-                if (mStaticInfo.isHardwareLevelLegacy()) {
+                StaticMetadata staticInfo = mAllStaticInfo.get(id);
+                if (staticInfo.isHardwareLevelLegacy()) {
                     Log.i(TAG, "Skipping test on legacy devices");
                     continue;
                 }
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!staticInfo.isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(id);
                 aeCompensationTestByCamera();
             } finally {
                 closeDevice();
@@ -361,6 +374,7 @@
     /**
      * Test Ae region for still capture.
      */
+    @Test
     public void testAeRegions() throws Exception {
         for (String id : mCameraIds) {
             try {
@@ -386,6 +400,7 @@
     /**
      * Test AWB region for still capture.
      */
+    @Test
     public void testAwbRegions() throws Exception {
         for (String id : mCameraIds) {
             try {
@@ -411,6 +426,7 @@
     /**
      * Test Af region for still capture.
      */
+    @Test
     public void testAfRegions() throws Exception {
         for (String id : mCameraIds) {
             try {
@@ -436,15 +452,16 @@
     /**
      * Test preview is still running after a still request
      */
+    @Test
     public void testPreviewPersistence() throws Exception {
         for (String id : mCameraIds) {
             try {
                 Log.i(TAG, "Testing preview persistence for Camera " + id);
-                openDevice(id);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(id);
                 previewPersistenceTestByCamera();
             } finally {
                 closeDevice();
@@ -453,21 +470,23 @@
         }
     }
 
+    @Test
     public void testAePrecaptureTriggerCancelJpegCapture() throws Exception {
         for (String id : mCameraIds) {
             try {
                 Log.i(TAG, "Testing AE precapture cancel for jpeg capture for Camera " + id);
-                openDevice(id);
 
+                StaticMetadata staticInfo = mAllStaticInfo.get(id);
                 // Legacy device doesn't support AE precapture trigger
-                if (mStaticInfo.isHardwareLevelLegacy()) {
+                if (staticInfo.isHardwareLevelLegacy()) {
                     Log.i(TAG, "Skipping AE precapture trigger cancel test on legacy devices");
                     continue;
                 }
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!staticInfo.isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(id);
                 takePictureTestByCamera(/*aeRegions*/null, /*awbRegions*/null, /*afRegions*/null,
                         /*addAeTriggerCancel*/true, /*allocateBitmap*/false,
                         /*previewRequest*/null, /*stillRequest*/null);
@@ -486,15 +505,16 @@
      * the devices.
      * </p>
      */
+    @Test
     public void testAllocateBitmap() throws Exception {
         for (String id : mCameraIds) {
             try {
                 Log.i(TAG, "Testing bitmap allocations for Camera " + id);
-                openDevice(id);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(id);
                 takePictureTestByCamera(/*aeRegions*/null, /*awbRegions*/null, /*afRegions*/null,
                         /*addAeTriggerCancel*/false, /*allocateBitmap*/true,
                         /*previewRequest*/null, /*stillRequest*/null);
@@ -509,22 +529,24 @@
     /**
      * Test focal length controls.
      */
+    @Test
     public void testFocalLengths() throws Exception {
         for (String id : mCameraIds) {
             try {
-                openDevice(id);
-                if (mStaticInfo.isHardwareLevelLegacy()) {
+                StaticMetadata staticInfo = mAllStaticInfo.get(id);
+                if (staticInfo.isHardwareLevelLegacy()) {
                     Log.i(TAG, "Camera " + id + " is legacy, skipping");
                     continue;
                 }
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!staticInfo.isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
-                if (mStaticInfo.isExternalCamera()) {
+                if (staticInfo.isExternalCamera()) {
                     Log.i(TAG, "Camera " + id + " is external, skipping");
                     continue;
                 }
+                openDevice(id);
                 focalLengthTestByCamera();
             } finally {
                 closeDevice();
@@ -589,7 +611,7 @@
 
             validateJpegCapture(image, maxStillSz);
             verifyJpegKeys(image, result, maxStillSz, thumbnailSize, exifTestData,
-                    mStaticInfo, mCollector);
+                    mStaticInfo, mCollector, mDebugFileNameBase);
         }
     }
 
@@ -950,7 +972,7 @@
         validateRaw16Image(image, size);
         if (DEBUG) {
             byte[] rawBuffer = getDataFromImage(image);
-            String rawFileName = DEBUG_FILE_NAME_BASE + "/test" + "_" + size.toString() + "_cam" +
+            String rawFileName = mDebugFileNameBase + "/test" + "_" + size.toString() + "_cam" +
                     mCamera.getId() + ".raw16";
             Log.d(TAG, "Dump raw file into " + rawFileName);
             dumpFile(rawFileName, rawBuffer);
@@ -1038,13 +1060,13 @@
 
             if (DEBUG) {
                 byte[] rawBuffer = outputStream.toByteArray();
-                String rawFileName = DEBUG_FILE_NAME_BASE + "/raw16_" + TAG + size.toString() +
+                String rawFileName = mDebugFileNameBase + "/raw16_" + TAG + size.toString() +
                         "_cam_" + mCamera.getId() + ".dng";
                 Log.d(TAG, "Dump raw file into " + rawFileName);
                 dumpFile(rawFileName, rawBuffer);
 
                 byte[] jpegBuffer = getDataFromImage(jpegImage);
-                String jpegFileName = DEBUG_FILE_NAME_BASE + "/jpeg_" + TAG + size.toString() +
+                String jpegFileName = mDebugFileNameBase + "/jpeg_" + TAG + size.toString() +
                         "_cam_" + mCamera.getId() + ".jpg";
                 Log.d(TAG, "Dump jpeg file into " + rawFileName);
                 dumpFile(jpegFileName, jpegBuffer);
@@ -1188,7 +1210,7 @@
             Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
 
             verifyJpegKeys(image, stillResult, maxStillSz, testThumbnailSizes[i], EXIF_TEST_DATA[i],
-                    mStaticInfo, mCollector);
+                    mStaticInfo, mCollector, mDebugFileNameBase);
 
             // Free image resources
             image.close();
diff --git a/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java b/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
index a1a7e02..601ca03 100644
--- a/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
@@ -33,9 +33,9 @@
 import android.hardware.camera2.TotalCaptureResult;
 import android.util.Size;
 import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
+import android.hardware.camera2.cts.helpers.StaticMetadata;
 import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
 import android.hardware.camera2.params.OutputConfiguration;
-import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Range;
@@ -51,10 +51,11 @@
 import java.util.Arrays;
 import java.util.List;
 
+import org.junit.Test;
+
 /**
  * CameraDevice preview test by using SurfaceView.
  */
-@AppModeFull
 public class SurfaceViewPreviewTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "SurfaceViewPreviewTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -65,12 +66,12 @@
     private static final int PREPARE_TIMEOUT_MS = 10000; // 10 s
 
     @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();
     }
 
@@ -82,16 +83,17 @@
      * (monotonically increasing) ordering are verified.
      * </p>
      */
+    @Test
     public void testCameraPreview() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
                 Log.i(TAG, "Testing preview for Camera " + mCameraIds[i]);
-                openDevice(mCameraIds[i]);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(mCameraIds[i]);
                 previewTestByCamera();
             } finally {
                 closeDevice();
@@ -106,16 +108,17 @@
      * is not validated.
      * </p>
      */
+    @Test
     public void testBasicTestPatternPreview() throws Exception{
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
                 Log.i(TAG, "Testing preview for Camera " + mCameraIds[i]);
-                openDevice(mCameraIds[i]);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(mCameraIds[i]);
                 previewTestPatternTestByCamera();
             } finally {
                 closeDevice();
@@ -127,14 +130,15 @@
      * Test {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE} for preview, validate the preview
      * frame duration and exposure time.
      */
+    @Test
     public void testPreviewFpsRange() throws Exception {
         for (String id : mCameraIds) {
             try {
-                openDevice(id);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(id);
                 previewFpsRangeTestByCamera();
             } finally {
                 closeDevice();
@@ -152,14 +156,15 @@
      * also exercises the prepare API.
      * </p>
      */
+    @Test
     public void testSurfaceSet() throws Exception {
         for (String id : mCameraIds) {
             try {
-                openDevice(id);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(id);
                 surfaceSetTestByCamera(id);
             } finally {
                 closeDevice();
@@ -176,15 +181,16 @@
      * - Ensure that starting to use a newly-prepared output does not cause additional
      *   preview glitches to occur
      */
+    @Test
     public void testPreparePerformance() throws Throwable {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
-                openDevice(mCameraIds[i]);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(mCameraIds[i]);
                 preparePerformanceTestByCamera(mCameraIds[i]);
             }
             finally {
@@ -335,15 +341,16 @@
      * Test to verify correct behavior with the same Surface object being used repeatedly with
      * different native internals, and multiple Surfaces pointing to the same actual consumer object
      */
+    @Test
     public void testSurfaceEquality() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
-                openDevice(mCameraIds[i]);
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
+                openDevice(mCameraIds[i]);
                 surfaceEqualityTestByCamera(mCameraIds[i]);
             }
             finally {
@@ -425,20 +432,22 @@
     /*
      * Verify creation of deferred surface capture sessions
      */
+    @Test
     public void testDeferredSurfaces() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
-                openDevice(mCameraIds[i]);
-                if (mStaticInfo.isHardwareLevelLegacy()) {
+                StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIds[i]);
+                if (staticInfo.isHardwareLevelLegacy()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] + " is legacy, skipping");
                     continue;
                 }
-                if (!mStaticInfo.isColorOutputSupported()) {
+                if (!staticInfo.isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + mCameraIds[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
 
+                openDevice(mCameraIds[i]);
                 testDeferredSurfacesByCamera(mCameraIds[i]);
             }
             finally {
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 4513c5c..060cd5b 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
@@ -36,7 +36,6 @@
 import android.media.Image;
 import android.media.Image.Plane;
 import android.media.ImageReader;
-import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.test.AndroidTestCase;
@@ -57,8 +56,6 @@
     private static final String TAG = "Camera2AndroidTestCase";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
 
-    protected static final String DEBUG_FILE_NAME_BASE =
-            Environment.getExternalStorageDirectory().getPath();
     // 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;
@@ -79,6 +76,7 @@
     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;
 
@@ -113,6 +111,7 @@
         mHandler = new Handler(mHandlerThread.getLooper());
         mCameraListener = new BlockingStateCallback();
         mCollector = new CameraErrorCollector();
+        mDebugFileNameBase = getContext().getExternalFilesDir(null).getPath();
 
         mAllStaticInfo = new HashMap<String, StaticMetadata>();
         for (String cameraId : mCameraIds) {
@@ -210,8 +209,7 @@
         mCamera = CameraTestUtils.openCamera(
                 mCameraManager, cameraId, listener, mHandler);
         mCollector.setCameraId(cameraId);
-        mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId),
-                CheckLevel.ASSERT, /*collector*/null);
+        mStaticInfo = mAllStaticInfo.get(cameraId);
         if (mStaticInfo.isColorOutputSupported()) {
             mOrderedPreviewSizes = getSupportedPreviewSizes(
                     cameraId, mCameraManager,
@@ -394,6 +392,22 @@
         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}.
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 ae06f3d..773728d 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
@@ -20,6 +20,7 @@
 import static com.android.ex.camera2.blocking.BlockingSessionCallback.*;
 import static com.android.ex.camera2.blocking.BlockingStateCallback.*;
 
+import android.app.Activity;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Matrix;
@@ -39,7 +40,7 @@
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.SystemClock;
-import android.test.ActivityInstrumentationTestCase2;
+import android.support.test.rule.ActivityTestRule;
 import android.util.Log;
 import android.util.Size;
 import android.view.Surface;
@@ -56,11 +57,14 @@
 import java.util.List;
 import java.util.HashMap;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+
 /**
  * Camera2 test case base class by using mixed SurfaceView and TextureView as rendering target.
  */
-public class Camera2MultiViewTestCase extends
-        ActivityInstrumentationTestCase2<Camera2MultiViewCtsActivity> {
+public class Camera2MultiViewTestCase {
     private static final String TAG = "MultiViewTestCase";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
 
@@ -73,6 +77,7 @@
 
     private CameraManager mCameraManager;
     private HandlerThread mHandlerThread;
+    private Activity mActivity;
     private Context mContext;
 
     private CameraHolder[] mCameraHolders;
@@ -80,14 +85,14 @@
 
     protected WindowManager mWindowManager;
 
-    public Camera2MultiViewTestCase() {
-        super(Camera2MultiViewCtsActivity.class);
-    }
+    @Rule
+    public ActivityTestRule<Camera2MultiViewCtsActivity> mActivityRule =
+            new ActivityTestRule<>(Camera2MultiViewCtsActivity.class);
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mContext = getActivity();
+    @Before
+    public void setUp() throws Exception {
+        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);
@@ -96,7 +101,7 @@
         mHandlerThread = new HandlerThread(TAG);
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
-        Camera2MultiViewCtsActivity activity = (Camera2MultiViewCtsActivity) mContext;
+        Camera2MultiViewCtsActivity activity = (Camera2MultiViewCtsActivity) mActivity;
         for (int i = 0; i < Camera2MultiViewCtsActivity.MAX_TEXTURE_VIEWS; i++) {
             mTextureView[i] = activity.getTextureView(i);
         }
@@ -111,8 +116,8 @@
         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @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));
@@ -129,7 +134,6 @@
                 camera = null;
             }
         }
-        super.tearDown();
     }
 
     /**
@@ -146,7 +150,7 @@
      */
     protected void updatePreviewDisplayRotation(Size previewSize, TextureView textureView) {
         int rotationDegrees = 0;
-        Camera2MultiViewCtsActivity activity = (Camera2MultiViewCtsActivity) mContext;
+        Camera2MultiViewCtsActivity activity = (Camera2MultiViewCtsActivity) mActivity;
         int displayRotation = activity.getWindowManager().getDefaultDisplay().getRotation();
         Configuration config = activity.getResources().getConfiguration();
 
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 6697b75..3f3cfd3 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
@@ -21,11 +21,9 @@
 
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.media.ImageReader;
-import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
-import android.test.ActivityInstrumentationTestCase2;
 import android.util.Log;
 import android.view.Surface;
 import android.view.SurfaceHolder;
@@ -50,6 +48,7 @@
 import android.hardware.camera2.cts.helpers.CameraErrorCollector;
 import android.hardware.camera2.cts.helpers.StaticMetadata;
 import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
+import android.support.test.rule.ActivityTestRule;
 
 import com.android.ex.camera2.blocking.BlockingSessionCallback;
 import com.android.ex.camera2.blocking.BlockingStateCallback;
@@ -60,6 +59,10 @@
 import java.util.HashMap;
 import java.util.List;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+
 /**
  * Camera2 Preview test case base class by using SurfaceView as rendering target.
  *
@@ -70,15 +73,11 @@
  * </p>
  */
 
-public class Camera2SurfaceViewTestCase extends
-        ActivityInstrumentationTestCase2<Camera2SurfaceViewCtsActivity> {
+public class Camera2SurfaceViewTestCase {
     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;
 
-    // TODO: Use internal storage for this to make sure the file is only visible to test.
-    protected static final String DEBUG_FILE_NAME_BASE =
-            Environment.getExternalStorageDirectory().getPath();
     protected static final int WAIT_FOR_RESULT_TIMEOUT_MS = 3000;
     protected static final float FRAME_DURATION_ERROR_MARGIN = 0.01f; // 1 percent error margin.
     protected static final int NUM_RESULTS_WAIT_TIMEOUT = 100;
@@ -93,6 +92,7 @@
     protected BlockingStateCallback mCameraListener;
     protected BlockingSessionCallback mSessionListener;
     protected CameraErrorCollector mCollector;
+    protected HashMap<String, StaticMetadata> mAllStaticInfo;
     // Per device fields:
     protected StaticMetadata mStaticInfo;
     protected CameraDevice mCamera;
@@ -107,21 +107,17 @@
     protected List<Size> mOrderedVideoSizes; // In descending order.
     protected List<Size> mOrderedStillSizes; // In descending order.
     protected HashMap<Size, Long> mMinPreviewFrameDurationMap;
+    protected String mDebugFileNameBase;
 
     protected WindowManager mWindowManager;
 
-    public Camera2SurfaceViewTestCase() {
-        super(Camera2SurfaceViewCtsActivity.class);
-    }
+    @Rule
+    public ActivityTestRule<Camera2SurfaceViewCtsActivity> mActivityRule =
+            new ActivityTestRule<>(Camera2SurfaceViewCtsActivity.class);
 
-    @Override
-    protected void setUp() throws Exception {
-        /**
-         * Set up the camera preview required environments, including activity,
-         * CameraManager, HandlerThread, Camera IDs, and CameraStateCallback.
-         */
-        super.setUp();
-        mContext = getActivity();
+    @Before
+    public void setUp() throws Exception {
+        mContext = mActivityRule.getActivity().getApplicationContext();
         /**
          * Workaround for mockito and JB-MR2 incompatibility
          *
@@ -138,12 +134,21 @@
         mHandler = new Handler(mHandlerThread.getLooper());
         mCameraListener = new BlockingStateCallback();
         mCollector = new CameraErrorCollector();
+        mDebugFileNameBase = mContext.getExternalFilesDir(null).getPath();
+
+        mAllStaticInfo = new HashMap<String, StaticMetadata>();
+        for (String cameraId : mCameraIds) {
+            StaticMetadata staticMetadata = new StaticMetadata(
+                    mCameraManager.getCameraCharacteristics(cameraId),
+                    CheckLevel.ASSERT, /*collector*/null);
+            mAllStaticInfo.put(cameraId, staticMetadata);
+        }
 
         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @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));
@@ -162,8 +167,6 @@
         } catch (Throwable e) {
             // When new Exception(e) is used, exception info will be printed twice.
             throw new Exception(e.getMessage());
-        } finally {
-            super.tearDown();
         }
     }
 
@@ -636,7 +639,7 @@
         }
 
         mPreviewSize = size;
-        Camera2SurfaceViewCtsActivity ctsActivity = getActivity();
+        Camera2SurfaceViewCtsActivity ctsActivity = mActivityRule.getActivity();
         final SurfaceHolder holder = ctsActivity.getSurfaceView().getHolder();
         Handler handler = new Handler(Looper.getMainLooper());
         handler.post(new Runnable() {
@@ -663,7 +666,7 @@
      * recreated
      */
     protected void recreatePreviewSurface() {
-        Camera2SurfaceViewCtsActivity ctsActivity = getActivity();
+        Camera2SurfaceViewCtsActivity ctsActivity = mActivityRule.getActivity();
         setPreviewVisibility(View.GONE);
         boolean res = ctsActivity.waitForSurfaceState(
             WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, /*valid*/ false);
@@ -681,7 +684,7 @@
      * @param visibility the new new visibility to set, one of View.VISIBLE / INVISIBLE / GONE
      */
     protected void setPreviewVisibility(int visibility) {
-        final Camera2SurfaceViewCtsActivity ctsActivity = getActivity();
+        final Camera2SurfaceViewCtsActivity ctsActivity = mActivityRule.getActivity();
         Handler handler = new Handler(Looper.getMainLooper());
         handler.post(new Runnable() {
             @Override
diff --git a/tests/camera/src/android/hardware/cts/CameraGLTest.java b/tests/camera/src/android/hardware/cts/CameraGLTest.java
index 738c3b7..2cd95d6 100644
--- a/tests/camera/src/android/hardware/cts/CameraGLTest.java
+++ b/tests/camera/src/android/hardware/cts/CameraGLTest.java
@@ -32,9 +32,8 @@
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
-import android.platform.test.annotations.AppModeFull;
 
-import android.test.ActivityInstrumentationTestCase2;
+import android.support.test.rule.ActivityTestRule;
 import android.test.MoreAsserts;
 import android.test.UiThreadTest;
 import android.test.suitebuilder.annotation.LargeTest;
@@ -56,12 +55,22 @@
 import javax.microedition.khronos.egl.EGLDisplay;
 import javax.microedition.khronos.opengles.GL10;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+
 /**
  * This test case must run with hardware. It can't be tested in emulator
  */
-@AppModeFull
 @LargeTest
-public class CameraGLTest extends ActivityInstrumentationTestCase2<GLSurfaceViewCtsActivity> {
+public class CameraGLTest {
     private static final String TAG = "CameraGLTest";
     private static final String PACKAGE = "android.hardware.cts";
     private static final boolean LOGV = false;
@@ -89,27 +98,29 @@
     Renderer mRenderer;
     GLSurfaceView mGLView;
 
-    public CameraGLTest() {
-        super(PACKAGE, GLSurfaceViewCtsActivity.class);
-        if (LOGV) Log.v(TAG, "CameraGLTest Constructor");
-    }
+    @Rule
+    public ActivityTestRule<GLSurfaceViewCtsActivity> mActivityRule =
+            new ActivityTestRule<GLSurfaceViewCtsActivity>(GLSurfaceViewCtsActivity.class) {
+                @Override
+                protected void beforeActivityLaunched() {
+                    // Set up renderer instance
+                    mRenderer = new Renderer();
+                    GLSurfaceViewCtsActivity.setRenderer(mRenderer);
+                    GLSurfaceViewCtsActivity.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
+                    GLSurfaceViewCtsActivity.setGlVersion(2);
+                }
+            };
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        // Set up renderer instance
-        mRenderer = this.new Renderer();
-        GLSurfaceViewCtsActivity.setRenderer(mRenderer);
-        GLSurfaceViewCtsActivity.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
-        GLSurfaceViewCtsActivity.setGlVersion(2);
+    @Before
+    public void setUp() throws Exception {
         // Start CameraCtsActivity.
-        GLSurfaceViewCtsActivity ctsActivity = getActivity();
+        GLSurfaceViewCtsActivity ctsActivity = mActivityRule.getActivity();
         // Store a link to the view so we can redraw it when needed
         mGLView = ctsActivity.getView();
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void tearDown() throws Exception {
         if (mCamera != null) {
             terminateMessageLooper();
         }
@@ -117,8 +128,6 @@
         GLSurfaceViewCtsActivity.resetRenderMode();
         GLSurfaceViewCtsActivity.resetRenderer();
         GLSurfaceViewCtsActivity.resetGlVersion();
-
-        super.tearDown();
     }
 
     /**
@@ -141,7 +150,7 @@
                 mCamera = Camera.open(cameraId);
                 try {
                     mIsExternalCamera = CameraUtils.isExternal(
-                            getInstrumentation().getContext(), cameraId);
+                            mActivityRule.getActivity().getApplicationContext(), cameraId);
                 } catch (Exception e) {
                     Log.e(TAG, "Unable to query external camera!" + e);
                 }
@@ -321,7 +330,8 @@
         }
 
         /* Make sure the screen stays on while testing - otherwise the OpenGL context may disappear */
-        PowerManager pm = (PowerManager) getActivity().getSystemService(Context.POWER_SERVICE);
+        PowerManager pm = (PowerManager) mActivityRule.getActivity().getSystemService(
+                Context.POWER_SERVICE);
         PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "CameraGLTest");
         wl.acquire();
         try {
@@ -342,6 +352,7 @@
 
     /** Test Camera.setPreviewTexture in conjunction with the standard Camera preview callback */
     @UiThreadTest
+    @Test
     public void testSetPreviewTexturePreviewCallback() throws Exception {
         runForAllCameras(testSetPreviewTexturePreviewCallbackByCamera);
     }
@@ -385,6 +396,7 @@
     /** Test Camera.setPreviewTexture in conjunction with both the standard Camera preview callback,
         and the SurfaceTexture onFrameAvailable callback */
     @UiThreadTest
+    @Test
     public void testSetPreviewTextureBothCallbacks() throws Exception {
         runForAllCameras(testSetPreviewTextureBothCallbacksByCamera);
     }
@@ -443,6 +455,7 @@
 
     /** Test Camera.setPreviewTexture in conjunction with just the SurfaceTexture onFrameAvailable callback */
     @UiThreadTest
+    @Test
     public void testSetPreviewTextureTextureCallback() throws Exception {
         runForAllCameras(testSetPreviewTextureTextureCallbackByCamera);
     }
@@ -510,6 +523,7 @@
      * TODO: This should be made stricter once SurfaceTexture timestamps are generated by the drivers.
      */
     @UiThreadTest
+    @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
     public void testCameraToSurfaceTextureMetadata() throws Exception {
         runForAllCameras(testCameraToSurfaceTextureMetadataByCamera);
     }
diff --git a/tests/camera/src/android/hardware/cts/CameraTest.java b/tests/camera/src/android/hardware/cts/CameraTest.java
index 81b42ef..25722d6 100644
--- a/tests/camera/src/android/hardware/cts/CameraTest.java
+++ b/tests/camera/src/android/hardware/cts/CameraTest.java
@@ -16,6 +16,7 @@
 
 package android.hardware.cts;
 
+import android.app.Activity;
 import android.content.pm.PackageManager;
 import android.graphics.BitmapFactory;
 import android.graphics.ImageFormat;
@@ -36,11 +37,9 @@
 import android.media.MediaRecorder;
 import android.os.Build;
 import android.os.ConditionVariable;
-import android.os.Environment;
 import android.os.Looper;
 import android.os.SystemClock;
-import android.platform.test.annotations.AppModeFull;
-import android.test.ActivityInstrumentationTestCase2;
+import android.support.test.rule.ActivityTestRule;
 import android.test.MoreAsserts;
 import android.test.UiThreadTest;
 import android.test.suitebuilder.annotation.LargeTest;
@@ -60,19 +59,24 @@
 import java.util.List;
 import java.util.TimeZone;
 
+import junit.framework.Assert;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.Rule;
+
 import junit.framework.AssertionFailedError;
 
 /**
  * This test case must run with hardware. It can't be tested in emulator
  */
-@AppModeFull
 @LargeTest
-public class CameraTest extends ActivityInstrumentationTestCase2<CameraCtsActivity> {
+public class CameraTest extends Assert {
     private static final String TAG = "CameraTest";
     private static final String PACKAGE = "android.hardware.cts";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
-    private final String JPEG_PATH = Environment.getExternalStorageDirectory().getPath() +
-            "/test.jpg";
+    private String mJpegPath = null;
     private byte[] mJpegData;
 
     private static final int PREVIEW_CALLBACK_NOT_RECEIVED = 0;
@@ -125,25 +129,20 @@
     Camera mCamera;
     boolean mIsExternalCamera;
 
-    public CameraTest() {
-        super(PACKAGE, CameraCtsActivity.class);
-        if (VERBOSE) Log.v(TAG, "Camera Constructor");
+    @Rule
+    public ActivityTestRule<CameraCtsActivity> mActivityRule =
+            new ActivityTestRule<>(CameraCtsActivity.class);
+
+    @Before
+    public void setUp() throws Exception {
     }
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        // to starCtsActivity.
-        getActivity();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void tearDown() throws Exception {
         if (mCamera != null) {
             mCamera.release();
             mCamera = null;
         }
-        super.tearDown();
     }
 
     /*
@@ -163,7 +162,7 @@
                 mLooper = Looper.myLooper();
                 try {
                     mIsExternalCamera = CameraUtils.isExternal(
-                            getInstrumentation().getContext(), cameraId);
+                            mActivityRule.getActivity().getApplicationContext(), cameraId);
                 } catch (Exception e) {
                     Log.e(TAG, "Unable to query external camera!" + e);
                 }
@@ -187,7 +186,8 @@
             fail("initializeMessageLooper: start timeout");
         }
         assertNotNull("Fail to open camera.", mCamera);
-        mCamera.setPreviewDisplay(getActivity().getSurfaceView().getHolder());
+        mCamera.setPreviewDisplay(mActivityRule.getActivity().getSurfaceView().getHolder());
+        mJpegPath = mActivityRule.getActivity().getExternalFilesDir(null).getPath() + "/test.jpg";
     }
 
     /*
@@ -306,7 +306,7 @@
                 mJpegData = rawData;
                 if (rawData != null) {
                     // try to store the picture on the SD card
-                    File rawoutput = new File(JPEG_PATH);
+                    File rawoutput = new File(mJpegPath);
                     FileOutputStream outStream = new FileOutputStream(rawoutput);
                     outStream.write(rawData);
                     outStream.close();
@@ -401,6 +401,7 @@
      * functions are called properly.
      */
     @UiThreadTest
+    @Test
     public void testTakePicture() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -446,6 +447,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testPreviewCallback() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -490,6 +492,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testStabilizationOneShotPreviewCallback() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -514,6 +517,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testSetOneShotPreviewCallback() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -537,6 +541,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testSetPreviewDisplay() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -546,7 +551,7 @@
     }
 
     private void testSetPreviewDisplayByCamera(int cameraId) throws Exception {
-        SurfaceHolder holder = getActivity().getSurfaceView().getHolder();
+        SurfaceHolder holder = mActivityRule.getActivity().getSurfaceView().getHolder();
         initializeMessageLooper(cameraId);
 
         // Check the order: startPreview->setPreviewDisplay.
@@ -580,6 +585,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testDisplayOrientation() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -618,6 +624,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testParameters() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -841,6 +848,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testJpegThumbnailSize() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -868,7 +876,7 @@
         mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
         waitForSnapshotDone();
         assertTrue(mJpegPictureCallbackResult);
-        ExifInterface exif = new ExifInterface(JPEG_PATH);
+        ExifInterface exif = new ExifInterface(mJpegPath);
         assertTrue(exif.hasThumbnail());
         byte[] thumb = exif.getThumbnail();
         BitmapFactory.Options bmpOptions = new BitmapFactory.Options();
@@ -894,10 +902,10 @@
         mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
         waitForSnapshotDone();
         assertTrue(mJpegPictureCallbackResult);
-        exif = new ExifInterface(JPEG_PATH);
+        exif = new ExifInterface(mJpegPath);
         assertFalse(exif.hasThumbnail());
         // Primary image should still be valid for no thumbnail capture.
-        BitmapFactory.decodeFile(JPEG_PATH, bmpOptions);
+        BitmapFactory.decodeFile(mJpegPath, bmpOptions);
         if (!recording) {
             assertTrue("Jpeg primary image size should match requested size",
                     bmpOptions.outWidth == pictureSize.width &&
@@ -910,10 +918,11 @@
         }
 
         assertNotNull("Jpeg primary image data should be decodable",
-                BitmapFactory.decodeFile(JPEG_PATH));
+                BitmapFactory.decodeFile(mJpegPath));
     }
 
     @UiThreadTest
+    @Test
     public void testJpegExif() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -935,7 +944,7 @@
         double focalLength = parameters.getFocalLength();
 
         // Test various exif tags.
-        ExifInterface exif = new ExifInterface(JPEG_PATH);
+        ExifInterface exif = new ExifInterface(mJpegPath);
         StringBuffer failedCause = new StringBuffer("Jpeg exif test failed:\n");
         boolean extraExiftestPassed = checkExtraExifTagsSucceeds(failedCause, exif);
 
@@ -977,7 +986,7 @@
         mCamera.setParameters(parameters);
         mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
         waitForSnapshotDone();
-        exif = new ExifInterface(JPEG_PATH);
+        exif = new ExifInterface(mJpegPath);
         checkGpsDataNull(exif);
         assertBitmapAndJpegSizeEqual(mJpegData, exif);
         // Reset the rotation to prevent from affecting other tests.
@@ -1160,7 +1169,7 @@
         mCamera.setParameters(parameters);
         mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
         waitForSnapshotDone();
-        ExifInterface exif = new ExifInterface(JPEG_PATH);
+        ExifInterface exif = new ExifInterface(mJpegPath);
         assertNotNull(exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE));
         assertNotNull(exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE));
         assertNotNull(exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF));
@@ -1206,6 +1215,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testLockUnlock() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -1218,7 +1228,7 @@
         initializeMessageLooper(cameraId);
         Camera.Parameters parameters = mCamera.getParameters();
         SurfaceHolder surfaceHolder;
-        surfaceHolder = getActivity().getSurfaceView().getHolder();
+        surfaceHolder = mActivityRule.getActivity().getSurfaceView().getHolder();
         CamcorderProfile profile = CamcorderProfile.get(cameraId,
                 CamcorderProfile.QUALITY_LOW);
         Camera.Size videoSize = null; // Used for external camera
@@ -1398,6 +1408,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testPreviewCallbackWithBuffer() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -1409,7 +1420,7 @@
     private void testPreviewCallbackWithBufferByCamera(int cameraId) throws Exception {
         initializeMessageLooper(cameraId);
         SurfaceHolder surfaceHolder;
-        surfaceHolder = getActivity().getSurfaceView().getHolder();
+        surfaceHolder = mActivityRule.getActivity().getSurfaceView().getHolder();
         mCamera.setPreviewDisplay(surfaceHolder);
         Parameters parameters = mCamera.getParameters();
         PreviewCallbackWithBuffer callback = new PreviewCallbackWithBuffer();
@@ -1486,6 +1497,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testImmediateZoom() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -1548,6 +1560,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testSmoothZoom() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -1673,6 +1686,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testFocusDistances() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -1781,6 +1795,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testCancelAutofocus() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -1837,7 +1852,7 @@
 
         // Ensure the camera can be opened if release is called right after AF.
         mCamera = Camera.open(cameraId);
-        mCamera.setPreviewDisplay(getActivity().getSurfaceView().getHolder());
+        mCamera.setPreviewDisplay(mActivityRule.getActivity().getSurfaceView().getHolder());
         mCamera.startPreview();
         mCamera.autoFocus(mAutoFocusCallback);
         mCamera.release();
@@ -1862,6 +1877,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testMultipleCameras() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         Log.v(TAG, "total " + nCameras + " cameras");
@@ -1924,6 +1940,7 @@
     }
 
     @UiThreadTest
+    @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
     public void testPreviewPictureSizesCombination() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -2003,6 +2020,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testPreviewFpsRange() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -2231,6 +2249,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testSceneMode() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -2331,6 +2350,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testInvalidParameters() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -2385,6 +2405,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testGetParameterDuringFocus() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -2420,6 +2441,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testPreviewFormats() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -2444,6 +2466,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testMultiCameraRelease() throws Exception {
         // Verify that multiple cameras exist, and that they can be opened at the same time
         if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Checking pre-conditions.");
@@ -2532,6 +2555,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testFocusAreas() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -2551,6 +2575,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testMeteringAreas() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -2687,6 +2712,7 @@
 
     // Apps should be able to call startPreview in jpeg callback.
     @UiThreadTest
+    @Test
     public void testJpegCallbackStartPreview() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -2716,6 +2742,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testRecordingHint() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -2728,7 +2755,7 @@
         initializeMessageLooper(cameraId);
         Parameters parameters = mCamera.getParameters();
 
-        SurfaceHolder holder = getActivity().getSurfaceView().getHolder();
+        SurfaceHolder holder = mActivityRule.getActivity().getSurfaceView().getHolder();
         CamcorderProfile profile = CamcorderProfile.get(cameraId,
                 CamcorderProfile.QUALITY_LOW);
         Camera.Size videoSize = null; // for external camera
@@ -2811,6 +2838,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testAutoExposureLock() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -2827,6 +2855,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testAutoWhiteBalanceLock() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -2843,6 +2872,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void test3ALockInteraction() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -3067,6 +3097,7 @@
     }
 
     @UiThreadTest
+    @Test
     public void testFaceDetection() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -3193,6 +3224,7 @@
     }
 
     @UiThreadTest
+    @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
     public void testVideoSnapshot() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -3221,7 +3253,7 @@
             return;
         }
 
-        SurfaceHolder holder = getActivity().getSurfaceView().getHolder();
+        SurfaceHolder holder = mActivityRule.getActivity().getSurfaceView().getHolder();
 
         for (int profileId: mCamcorderProfileList) {
             if (!CamcorderProfile.hasProfile(cameraId, profileId)) {
@@ -3271,6 +3303,7 @@
         }
     }
 
+    @Test
     public void testPreviewCallbackWithPicture() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -3330,6 +3363,7 @@
         terminateMessageLooper();
     }
 
+    @Test
     public void testEnableShutterSound() throws Exception {
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
@@ -3356,8 +3390,9 @@
         terminateMessageLooper();
     }
 
+    @Test
     public void testCameraExternalConnected() {
-        if (getActivity().getPackageManager().
+        if (mActivityRule.getActivity().getPackageManager().
                 hasSystemFeature(PackageManager.FEATURE_CAMERA_EXTERNAL) ) {
             int nCameras = Camera.getNumberOfCameras();
             assertTrue("Devices with external camera support must have a camera connected for " +
diff --git a/tests/camera/src/android/hardware/cts/Camera_ParametersTest.java b/tests/camera/src/android/hardware/cts/Camera_ParametersTest.java
index a614982..b37959e 100644
--- a/tests/camera/src/android/hardware/cts/Camera_ParametersTest.java
+++ b/tests/camera/src/android/hardware/cts/Camera_ParametersTest.java
@@ -18,9 +18,7 @@
 
 import junit.framework.TestCase;
 import android.hardware.Camera.Parameters;
-import android.platform.test.annotations.AppModeFull;
 
-@AppModeFull
 public class Camera_ParametersTest extends TestCase {
 
     public void testAccessMethods() {
diff --git a/tests/camera/src/android/hardware/cts/Camera_SizeTest.java b/tests/camera/src/android/hardware/cts/Camera_SizeTest.java
index ace3cf7..77e75dd 100644
--- a/tests/camera/src/android/hardware/cts/Camera_SizeTest.java
+++ b/tests/camera/src/android/hardware/cts/Camera_SizeTest.java
@@ -19,7 +19,6 @@
 import android.hardware.Camera;
 import android.hardware.Camera.Parameters;
 import android.hardware.cts.helpers.CameraUtils;
-import android.platform.test.annotations.AppModeFull;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
 
@@ -31,7 +30,6 @@
 import java.util.Collections;
 import java.util.List;
 
-@AppModeFull
 @LargeTest
 public class Camera_SizeTest extends CtsAndroidTestCase {
 
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 6ad8b2e..0f48364 100644
--- a/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
+++ b/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
@@ -26,7 +26,6 @@
 import android.hardware.camera2.CameraManager;
 import android.hardware.cts.CameraCtsActivity;
 import android.os.Handler;
-import android.platform.test.annotations.AppModeFull;
 import android.test.ActivityInstrumentationTestCase2;
 import android.util.Log;
 
@@ -41,7 +40,6 @@
 /**
  * Tests for multi-process camera usage behavior.
  */
-@AppModeFull
 public class CameraEvictionTest extends ActivityInstrumentationTestCase2<CameraCtsActivity> {
 
     public static final String TAG = "CameraEvictionTest";
diff --git a/tests/camera/src/android/hardware/multiprocess/camera/cts/MediaRecorderCameraActivity.java b/tests/camera/src/android/hardware/multiprocess/camera/cts/MediaRecorderCameraActivity.java
index 0558800..1f12cea 100644
--- a/tests/camera/src/android/hardware/multiprocess/camera/cts/MediaRecorderCameraActivity.java
+++ b/tests/camera/src/android/hardware/multiprocess/camera/cts/MediaRecorderCameraActivity.java
@@ -20,7 +20,6 @@
 import android.camera.cts.R;
 import android.media.MediaRecorder;
 import android.os.Bundle;
-import android.os.Environment;
 import android.util.Log;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
@@ -42,9 +41,7 @@
     private static final int LAYOUT_WIDTH = VIDEO_WIDTH;
     private static final int LAYOUT_HEIGHT = VIDEO_HEIGHT;
 
-    private final String OUTPUT_PATH = new File(Environment.getExternalStorageDirectory(),
-                "record.out").getAbsolutePath();
-
+    private String mOutputPath;
     private File mOutFile;
     private SurfaceView mSurfaceView;
     private ErrorLoggingService.ErrorServiceConnection mErrorServiceConnection;
@@ -61,6 +58,7 @@
         mErrorServiceConnection.start();
 
         mMediaRecorder = new MediaRecorder();
+        mOutputPath = new File(getExternalFilesDir(null), "record.out").getAbsolutePath();
     }
 
     @Override
@@ -118,13 +116,13 @@
     @Override
     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
         try {
-            mOutFile = new File(OUTPUT_PATH);
+            mOutFile = new File(mOutputPath);
             mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
             mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
             mMediaRecorder.setPreviewDisplay(mSurfaceView.getHolder().getSurface());
             mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
             mMediaRecorder.setVideoSize(VIDEO_WIDTH, VIDEO_HEIGHT);
-            mMediaRecorder.setOutputFile(OUTPUT_PATH);
+            mMediaRecorder.setOutputFile(mOutputPath);
             mMediaRecorder.prepare();
             mMediaRecorder.start();
 
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 f0714e1..31867a5 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -47,7 +47,6 @@
 import android.media.ImageWriter;
 import android.media.Image.Plane;
 import android.os.Build;
-import android.os.Environment;
 import android.os.Handler;
 import android.util.Log;
 import android.util.Pair;
@@ -126,9 +125,6 @@
     private static final Location sTestLocation1 = new Location(LocationManager.GPS_PROVIDER);
     private static final Location sTestLocation2 = new Location(LocationManager.NETWORK_PROVIDER);
 
-    protected static final String DEBUG_FILE_NAME_BASE =
-            Environment.getExternalStorageDirectory().getPath();
-
     static {
         sTestLocation0.setTime(1199145600000L);
         sTestLocation0.setLatitude(37.736071);
@@ -2048,13 +2044,13 @@
      */
     public static void verifyJpegKeys(Image image, CaptureResult captureResult, Size expectedSize,
             Size expectedThumbnailSize, ExifTestData expectedExifData, StaticMetadata staticInfo,
-            CameraErrorCollector collector) throws Exception {
+            CameraErrorCollector collector, String debugFileNameBase) throws Exception {
 
         basicValidateJpegImage(image, expectedSize);
 
         byte[] jpegBuffer = getDataFromImage(image);
         // Have to dump into a file to be able to use ExifInterface
-        String jpegFilename = DEBUG_FILE_NAME_BASE + "/verifyJpegKeys.jpeg";
+        String jpegFilename = debugFileNameBase + "/verifyJpegKeys.jpeg";
         dumpFile(jpegFilename, jpegBuffer);
         ExifInterface exif = new ExifInterface(jpegFilename);
 
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
index 646dfe7..dcec5bb 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -113,6 +113,20 @@
         "AF_STATE_PASSIVE_UNFOCUSED"
     };
 
+    // Index with android.control.aePrecaptureTrigger
+    public static final String[] AE_TRIGGER_NAMES = new String[] {
+        "AE_TRIGGER_IDLE",
+        "AE_TRIGGER_START",
+        "AE_TRIGGER_CANCEL"
+    };
+
+    // Index with android.control.afTrigger
+    public static final String[] AF_TRIGGER_NAMES = new String[] {
+        "AF_TRIGGER_IDLE",
+        "AF_TRIGGER_START",
+        "AF_TRIGGER_CANCEL"
+    };
+
     public enum CheckLevel {
         /** Only log warnings for metadata check failures. Execution continues. */
         WARN,
diff --git a/tests/filesystem/AndroidTest.xml b/tests/filesystem/AndroidTest.xml
index fed22df..bb1aa98 100644
--- a/tests/filesystem/AndroidTest.xml
+++ b/tests/filesystem/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS File System 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="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsFileSystemTestCases.apk" />
diff --git a/tests/fragment/AndroidTest.xml b/tests/fragment/AndroidTest.xml
index f352ef2..ed97400 100644
--- a/tests/fragment/AndroidTest.xml
+++ b/tests/fragment/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Configuration for app.usage Tests">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="uitoolkit" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsFragmentTestCases.apk" />
diff --git a/tests/fragment/sdk26/AndroidTest.xml b/tests/fragment/sdk26/AndroidTest.xml
index 84fab15..16ffd27 100644
--- a/tests/fragment/sdk26/AndroidTest.xml
+++ b/tests/fragment/sdk26/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Configuration for app.usage Tests">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="uitoolkit" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsFragmentTestCasesSdk26.apk" />
diff --git a/tests/framework/base/OWNERS b/tests/framework/base/OWNERS
new file mode 100644
index 0000000..fe4ebd7
--- /dev/null
+++ b/tests/framework/base/OWNERS
@@ -0,0 +1,9 @@
+# Windows & Activities
+ogunwale@google.com
+jjaggi@google.com
+racarr@google.com
+chaviw@google.com
+brycelee@google.com
+akulian@google.com
+roosa@google.com
+takaoka@google.com
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
index 1ea220a..9e2f492 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
@@ -16,7 +16,7 @@
 
 package android.server.am;
 
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
@@ -30,8 +30,6 @@
 import static android.server.am.Components.ALT_LAUNCHING_ACTIVITY;
 import static android.server.am.Components.ALWAYS_FOCUSABLE_PIP_ACTIVITY;
 import static android.server.am.Components.BROADCAST_RECEIVER_ACTIVITY;
-import static android.server.am.Components.BroadcastReceiverActivity.ACTION_TRIGGER_BROADCAST;
-import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_FINISH_BROADCAST;
 import static android.server.am.Components.DOCKED_ACTIVITY;
 import static android.server.am.Components.LAUNCHING_ACTIVITY;
 import static android.server.am.Components.LAUNCH_PIP_ON_PIP_ACTIVITY;
@@ -54,9 +52,9 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
 
 import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -72,6 +70,7 @@
 
     @Presubmit
     @Test
+    @FlakyTest(bugId = 110276714)
     public void testTranslucentActivityOnTopOfPinnedStack() throws Exception {
         if (!supportsPip()) {
             return;
@@ -175,6 +174,7 @@
 
     @Presubmit
     @Test
+    @FlakyTest(bugId = 110276714)
     public void testTurnScreenOnActivity() throws Exception {
         try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
             lockScreenSession.sleepDevice();
@@ -187,6 +187,7 @@
 
     @Presubmit
     @Test
+    @FlakyTest(bugId = 110276714)
     public void testFinishActivityInNonFocusedStack() throws Exception {
         if (!supportsSplitScreenMultiWindow()) {
             // Skipping test: no multi-window support
@@ -204,14 +205,14 @@
         launchActivity(TEST_ACTIVITY, WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
         mAmWmState.assertVisibility(TEST_ACTIVITY, true);
         // Finish activity in non-focused (docked) stack.
-        executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+        mBroadcastActionTrigger.finishBroadcastReceiverActivity();
 
         mAmWmState.waitForActivityState(LAUNCHING_ACTIVITY, STATE_PAUSED);
         mAmWmState.waitForAllExitingWindows();
 
         mAmWmState.computeState(LAUNCHING_ACTIVITY);
         mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
-        mAmWmState.assertVisibility(BROADCAST_RECEIVER_ACTIVITY, false);
+        mAmWmState.assertNotExist(BROADCAST_RECEIVER_ACTIVITY);
     }
 
     @Test
@@ -244,7 +245,7 @@
         mAmWmState.assertVisibility(BROADCAST_RECEIVER_ACTIVITY, true);
 
         // Finish the top-most activity.
-        executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+        mBroadcastActionTrigger.finishBroadcastReceiverActivity();
         //TODO: BUG: MoveTaskToBackActivity returns to the top of the stack when
         // BroadcastActivity finishes, so homeActivity is not visible afterwards
 
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
index 44281b2..38c01c7 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
@@ -26,9 +26,6 @@
 import static android.server.am.ActivityManagerState.STATE_RESUMED;
 import static android.server.am.ComponentNameUtils.getWindowName;
 import static android.server.am.Components.BROADCAST_RECEIVER_ACTIVITY;
-import static android.server.am.Components.BroadcastReceiverActivity.ACTION_TRIGGER_BROADCAST;
-import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_BROADCAST_ORIENTATION;
-import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_MOVE_BROADCAST_TO_BACK;
 import static android.server.am.Components.DIALOG_WHEN_LARGE_ACTIVITY;
 import static android.server.am.Components.LANDSCAPE_ORIENTATION_ACTIVITY;
 import static android.server.am.Components.LAUNCHING_ACTIVITY;
@@ -72,16 +69,6 @@
  */
 public class ActivityManagerAppConfigurationTests extends ActivityManagerTestBase {
 
-    // TODO(b/70247058): Use {@link Context#sendBroadcast(Intent).
-    // Shell command to move {@link #BROADCAST_RECEIVER_ACTIVITY} task to back.
-    private static final String MOVE_TASK_TO_BACK_BROADCAST = "am broadcast -a "
-            + ACTION_TRIGGER_BROADCAST + " --ez " + EXTRA_MOVE_BROADCAST_TO_BACK + " true";
-    // Shell command to request portrait orientation to {@link #BROADCAST_RECEIVER_ACTIVITY}.
-    private static final String REQUEST_PORTRAIT_BROADCAST = "am broadcast -a "
-            + ACTION_TRIGGER_BROADCAST + " --ei " + EXTRA_BROADCAST_ORIENTATION + " 1";
-    private static final String REQUEST_LANDSCAPE_BROADCAST = "am broadcast -a "
-            + ACTION_TRIGGER_BROADCAST + " --ei " + EXTRA_BROADCAST_ORIENTATION + " 0";
-
     private static final int SMALL_WIDTH_DP = 426;
     private static final int SMALL_HEIGHT_DP = 320;
 
@@ -282,7 +269,7 @@
         // Resize docked stack to fullscreen size. This will trigger activity relaunch with
         // non-empty override configuration corresponding to fullscreen size.
         logSeparator = separateLogs();
-        mAm.resizeStack(stack.mStackId, displayRect);
+        mAtm.resizeStack(stack.mStackId, displayRect);
 
         // Move activity back to fullscreen stack.
         setActivityTaskWindowingMode(activityName,
@@ -317,6 +304,7 @@
      */
     @Presubmit
     @Test
+    @FlakyTest(bugId = 110276714)
     public void testDialogWhenLargeSplitSmall() {
         assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
 
@@ -329,7 +317,7 @@
         final int smallWidthPx = dpToPx(SMALL_WIDTH_DP, density);
         final int smallHeightPx = dpToPx(SMALL_HEIGHT_DP, density);
 
-        mAm.resizeStack(stack.mStackId, new Rect(0, 0, smallWidthPx, smallHeightPx));
+        mAtm.resizeStack(stack.mStackId, new Rect(0, 0, smallWidthPx, smallHeightPx));
         mAmWmState.waitForValidState(
                 new WaitForValidActivityState.Builder(DIALOG_WHEN_LARGE_ACTIVITY)
                         .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
@@ -524,11 +512,11 @@
         launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY);
 
         // Request portrait
-        executeShellCommand(REQUEST_PORTRAIT_BROADCAST);
+        mBroadcastActionTrigger.requestOrientation(SCREEN_ORIENTATION_PORTRAIT);
         mAmWmState.waitForLastOrientation(SCREEN_ORIENTATION_PORTRAIT);
 
         // Finish activity
-        executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+        mBroadcastActionTrigger.finishBroadcastReceiverActivity();
 
         // Verify that activity brought to front is in originally requested orientation.
         mAmWmState.computeState(LANDSCAPE_ORIENTATION_ACTIVITY);
@@ -551,9 +539,9 @@
         // Verify fixed-landscape
         LogSeparator logSeparator = separateLogs();
         launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY);
-        executeShellCommand(REQUEST_LANDSCAPE_BROADCAST);
+        mBroadcastActionTrigger.requestOrientation(SCREEN_ORIENTATION_LANDSCAPE);
         mAmWmState.waitForLastOrientation(SCREEN_ORIENTATION_LANDSCAPE);
-        executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+        mBroadcastActionTrigger.finishBroadcastReceiverActivity();
 
         // Verify that activity brought to front is in originally requested orientation.
         mAmWmState.waitForActivityState(RESIZEABLE_ACTIVITY, STATE_RESUMED);
@@ -568,9 +556,9 @@
         // Verify fixed-portrait
         logSeparator = separateLogs();
         launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY);
-        executeShellCommand(REQUEST_PORTRAIT_BROADCAST);
+        mBroadcastActionTrigger.requestOrientation(SCREEN_ORIENTATION_PORTRAIT);
         mAmWmState.waitForLastOrientation(SCREEN_ORIENTATION_PORTRAIT);
-        executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+        mBroadcastActionTrigger.finishBroadcastReceiverActivity();
 
         // Verify that activity brought to front is in originally requested orientation.
         mAmWmState.waitForActivityState(RESIZEABLE_ACTIVITY, STATE_RESUMED);
@@ -692,11 +680,11 @@
         launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY);
 
         // Request portrait
-        executeShellCommand(REQUEST_PORTRAIT_BROADCAST);
+        mBroadcastActionTrigger.requestOrientation(SCREEN_ORIENTATION_PORTRAIT);
         mAmWmState.waitForLastOrientation(SCREEN_ORIENTATION_PORTRAIT);
 
         // Finish activity
-        executeShellCommand(MOVE_TASK_TO_BACK_BROADCAST);
+        mBroadcastActionTrigger.moveTopTaskToBack();
 
         // Verify that activity brought to front is in originally requested orientation.
         mAmWmState.waitForValidState(LANDSCAPE_ORIENTATION_ACTIVITY);
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAssistantStackTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAssistantStackTests.java
index 7873dbe..4602dda 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAssistantStackTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAssistantStackTests.java
@@ -185,7 +185,7 @@
                 expectedWindowingMode, ACTIVITY_TYPE_STANDARD);
 
         // Now, tell it to finish itself and ensure that the assistant stack is brought back forward
-        executeShellCommand("am broadcast -a " + TEST_ACTIVITY_ACTION_FINISH_SELF);
+        mBroadcastActionTrigger.doAction(TEST_ACTIVITY_ACTION_FINISH_SELF);
         mAmWmState.waitForFocusedStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
         mAmWmState.assertFrontStackActivityType(
                 "Assistant stack should be on top.", ACTIVITY_TYPE_ASSISTANT);
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java
index 90658e5..5b9ca14 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java
@@ -16,12 +16,17 @@
 
 package android.server.am;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.server.am.ActivityLauncher.KEY_LAUNCH_ACTIVITY;
 import static android.server.am.ActivityLauncher.KEY_NEW_TASK;
-import static android.server.am.ActivityLauncher.KEY_TARGET_COMPONENT;
 import static android.server.am.ActivityManagerDisplayTestBase.ReportedDisplayMetrics.getDisplayMetrics;
 import static android.server.am.ActivityManagerState.STATE_RESUMED;
 import static android.server.am.ActivityManagerState.STATE_STOPPED;
@@ -29,8 +34,6 @@
 import static android.server.am.ComponentNameUtils.getWindowName;
 import static android.server.am.Components.ALT_LAUNCHING_ACTIVITY;
 import static android.server.am.Components.BROADCAST_RECEIVER_ACTIVITY;
-import static android.server.am.Components.BroadcastReceiverActivity.ACTION_TRIGGER_BROADCAST;
-import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_FINISH_BROADCAST;
 import static android.server.am.Components.LAUNCHING_ACTIVITY;
 import static android.server.am.Components.LAUNCH_BROADCAST_ACTION;
 import static android.server.am.Components.LAUNCH_BROADCAST_RECEIVER;
@@ -52,20 +55,29 @@
 
 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.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
 
+import android.app.ActivityOptions;
 import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Bundle;
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 import android.server.am.ActivityManagerState.ActivityDisplay;
+import android.server.am.ActivityManagerState.ActivityStack;
 import android.support.test.filters.FlakyTest;
+import android.util.SparseArray;
 
 import androidx.annotation.Nullable;
 
+import com.android.compatibility.common.util.SystemUtil;
+
 import org.junit.Before;
 import org.junit.Test;
 
@@ -79,15 +91,7 @@
  *     atest CtsActivityManagerDeviceTestCases:ActivityManagerMultiDisplayTests
  */
 @Presubmit
-@FlakyTest(bugId = 77652261)
 public class ActivityManagerMultiDisplayTests extends ActivityManagerDisplayTestBase {
-
-    // TODO(b/70247058): Use {@link Context#sendBroadcast(Intent).
-    // Shell command to launch activity via {@link #BROADCAST_RECEIVER_ACTIVITY}.
-    private static final String LAUNCH_ACTIVITY_BROADCAST = "am broadcast -a "
-            + ACTION_TRIGGER_BROADCAST + " --ez " + KEY_LAUNCH_ACTIVITY + " true --ez "
-            + KEY_NEW_TASK + " true --es " + KEY_TARGET_COMPONENT + " ";
-
     @Before
     @Override
     public void setUp() throws Exception {
@@ -100,27 +104,30 @@
      * Tests launching an activity on virtual display.
      */
     @Test
+    @FlakyTest(bugId = 77270929)
     public void testLaunchActivityOnSecondaryDisplay() throws Exception {
+        validateActivityLaunchOnNewDisplay(ACTIVITY_TYPE_STANDARD);
+        // TODO(b/111363427) Enable the tests cases once we have properly handled non-standard
+        // type activities
+        //validateActivityLaunchOnNewDisplay(ACTIVITY_TYPE_HOME);
+        //validateActivityLaunchOnNewDisplay(ACTIVITY_TYPE_RECENTS);
+        //validateActivityLaunchOnNewDisplay(ACTIVITY_TYPE_ASSISTANT);
+    }
+
+    private void validateActivityLaunchOnNewDisplay(int activityType) throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             // Create new virtual display.
             final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
 
             // Launch activity on new secondary display.
             final LogSeparator logSeparator = separateLogs();
-            launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
-            mAmWmState.computeState(TEST_ACTIVITY);
-
-            mAmWmState.assertFocusedActivity(
-                    "Activity launched on secondary display must be focused",
-                    TEST_ACTIVITY);
-
-            // Check that activity is on the right display.
-            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-            final ActivityManagerState.ActivityStack frontStack =
-                    mAmWmState.getAmState().getStackById(frontStackId);
-            assertEquals("Launched activity must be on the secondary display and resumed",
-                    getActivityName(TEST_ACTIVITY), frontStack.mResumedActivity);
-            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+            SystemUtil.runWithShellPermissionIdentity(
+                    () -> getLaunchActivityBuilder().setUseInstrumentation()
+                            .setTargetActivity(TEST_ACTIVITY).setNewTask(true)
+                            .setMultipleTask(true).setActivityType(activityType)
+                            .setDisplayId(newDisplay.mId).execute());
+            waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
+                    "Activity launched on secondary display must be focused and on top");
 
             // Check that activity config corresponds to display config.
             final ReportedSizes reportedSizes = getLastReportedSizesForActivity(TEST_ACTIVITY,
@@ -136,34 +143,18 @@
     @Test
     public void testLaunchActivityOnPrimaryDisplay() throws Exception {
         // Launch activity on primary display explicitly.
-        launchActivityOnDisplay(LAUNCHING_ACTIVITY, 0);
-        mAmWmState.computeState(LAUNCHING_ACTIVITY);
+        launchActivityOnDisplay(LAUNCHING_ACTIVITY, DEFAULT_DISPLAY);
 
-        mAmWmState.assertFocusedActivity("Activity launched on primary display must be focused",
-                LAUNCHING_ACTIVITY);
-
-        // Check that activity is on the right display.
-        int frontStackId = mAmWmState.getAmState().getFrontStackId(0 /* displayId */);
-        ActivityManagerState.ActivityStack frontStack
-                = mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be on the primary display and resumed",
-                getActivityName(LAUNCHING_ACTIVITY), frontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on primary display", frontStackId);
+        waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, DEFAULT_DISPLAY,
+                "Activity launched on primary display must be focused and on top");
 
         // Launch another activity on primary display using the first one
         getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY).setNewTask(true)
-                .setMultipleTask(true).setDisplayId(0).execute();
+                .setMultipleTask(true).setDisplayId(DEFAULT_DISPLAY).execute();
         mAmWmState.computeState(TEST_ACTIVITY);
 
-        mAmWmState.assertFocusedActivity("Activity launched on primary display must be focused",
-                TEST_ACTIVITY);
-
-        // Check that activity is on the right display.
-        frontStackId = mAmWmState.getAmState().getFrontStackId(0 /* displayId */);
-        frontStack = mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals("Launched activity must be on the primary display and resumed",
-                getActivityName(TEST_ACTIVITY), frontStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on primary display", frontStackId);
+        waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
+                "Activity launched on primary display must be focused");
     }
 
     /**
@@ -171,6 +162,7 @@
      * default display.
      */
     @Test
+    @FlakyTest(bugId = 112055644)
     public void testLaunchNonResizeableActivityOnSecondaryDisplay() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             // Create new virtual display.
@@ -178,20 +170,9 @@
 
             // Launch activity on new secondary display.
             launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY, newDisplay.mId);
-            mAmWmState.computeState(NON_RESIZEABLE_ACTIVITY);
 
-            mAmWmState.assertFocusedActivity(
-                    "Activity launched on secondary display must be focused",
-                    NON_RESIZEABLE_ACTIVITY);
-
-            // Check that activity is on the right display.
-            final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY);
-            final ActivityManagerState.ActivityStack frontStack =
-                    mAmWmState.getAmState().getStackById(frontStackId);
-            assertEquals("Launched activity must be on the primary display and resumed",
-                    getActivityName(NON_RESIZEABLE_ACTIVITY),
-                    frontStack.mResumedActivity);
-            mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
+            waitAndAssertTopResumedActivity(NON_RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
+                    "Activity requested to launch on secondary display must be focused");
         }
     }
 
@@ -200,6 +181,7 @@
      * on the primary display. It should land on the primary display and dismiss docked stack.
      */
     @Test
+    @FlakyTest(bugId = 112055644)
     public void testLaunchNonResizeableActivityWithSplitScreen() throws Exception {
         assumeTrue(supportsSplitScreenMultiWindow());
 
@@ -215,20 +197,9 @@
 
             // Launch activity on new secondary display.
             launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY, newDisplay.mId);
-            mAmWmState.computeState(NON_RESIZEABLE_ACTIVITY);
 
-            mAmWmState.assertFocusedActivity(
-                    "Activity launched on secondary display must be focused",
-                    NON_RESIZEABLE_ACTIVITY);
-
-            // Check that activity is on the right display.
-            final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY);
-            final ActivityManagerState.ActivityStack frontStack =
-                    mAmWmState.getAmState().getStackById(frontStackId);
-            assertEquals("Launched activity must be on the primary display and resumed",
-                    getActivityName(NON_RESIZEABLE_ACTIVITY),
-                    frontStack.mResumedActivity);
-            mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
+            waitAndAssertTopResumedActivity(NON_RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
+                    "Activity requested to launch on secondary display must be focused");
             mAmWmState.assertDoesNotContainStack("Must not contain docked stack.",
                     WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
         }
@@ -252,22 +223,22 @@
 
             // Try to move the non-resizeable activity to new secondary display.
             moveActivityToStack(NON_RESIZEABLE_ACTIVITY, externalFrontStackId);
-            mAmWmState.computeState(NON_RESIZEABLE_ACTIVITY);
+            // Wait for a while to check that it won't move
+            mAmWmState.waitForWithAmState(state ->
+                    newDisplay.mId == state.getDisplayByActivity(NON_RESIZEABLE_ACTIVITY),
+                    "Waiting to see if activity won't be moved");
+            assertNotEquals("Non-resizeable activity must not be moved",
+                    newDisplay.mId,
+                    mAmWmState.getAmState().getDisplayByActivity(NON_RESIZEABLE_ACTIVITY));
 
-            mAmWmState.assertFocusedActivity(
-                    "Activity launched on secondary display must be focused",
-                    RESIZEABLE_ACTIVITY);
-
-            // Check that activity is in the same stack
-            final int defaultFrontStackId = mAmWmState.getAmState().getFrontStackId(
-                    DEFAULT_DISPLAY);
-            final ActivityManagerState.ActivityStack defaultFrontStack =
-                    mAmWmState.getAmState().getStackById(defaultFrontStackId);
-            assertEquals("Launched activity must be on the primary display and resumed",
-                    getActivityName(NON_RESIZEABLE_ACTIVITY),
-                    defaultFrontStack.getTopTask().mRealActivity);
-            mAmWmState.assertFocusedStack("Focus must remain on the secondary display",
-                    externalFrontStackId);
+            waitAndAssertTopResumedActivity(RESIZEABLE_ACTIVITY, newDisplay.mId,
+                    "Last launched activity must be focused");
+            mAmWmState.assertResumedActivities("Resumed activity states mustn't change",
+                    new SparseArray<ComponentName>(){{
+                        put(DEFAULT_DISPLAY, NON_RESIZEABLE_ACTIVITY);
+                        put(newDisplay.mId, RESIZEABLE_ACTIVITY);
+                    }}
+            );
         }
     }
 
@@ -276,6 +247,7 @@
      * land on the secondary display based on the resizeability of the root activity of the task.
      */
     @Test
+    @FlakyTest(bugId = 112055644)
     public void testLaunchNonResizeableActivityFromSecondaryDisplaySameTask() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             // Create new simulated display.
@@ -284,40 +256,22 @@
 
             // Launch activity on new secondary display.
             launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
-            mAmWmState.assertFocusedActivity(
-                    "Activity launched on secondary display must be focused",
-                    BROADCAST_RECEIVER_ACTIVITY);
-
-            // Check that launching activity is on the secondary display.
-            int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-            ActivityManagerState.ActivityStack frontStack =
-                    mAmWmState.getAmState().getStackById(frontStackId);
-            assertEquals("Launched activity must be on the secondary display and resumed",
-                    getActivityName(BROADCAST_RECEIVER_ACTIVITY),
-                    frontStack.mResumedActivity);
-            mAmWmState.assertFocusedStack("Focus must be on the secondary display", frontStackId);
+            waitAndAssertTopResumedActivity(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId,
+                    "Activity launched on secondary display must be focused");
 
             // Launch non-resizeable activity from secondary display.
-            executeShellCommand(
-                    LAUNCH_ACTIVITY_BROADCAST + getActivityName(NON_RESIZEABLE_ACTIVITY));
-            mAmWmState.computeState(NON_RESIZEABLE_ACTIVITY);
-
-            // Check that non-resizeable activity is on the secondary display, because of the
-            // resizeable root of the task.
-            frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-            frontStack = mAmWmState.getAmState().getStackById(frontStackId);
-            assertEquals("Launched activity must be on the primary display and resumed",
-                    getActivityName(NON_RESIZEABLE_ACTIVITY),
-                    frontStack.mResumedActivity);
-            mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
+            mBroadcastActionTrigger.launchActivityNewTask(getActivityName(NON_RESIZEABLE_ACTIVITY));
+            waitAndAssertTopResumedActivity(NON_RESIZEABLE_ACTIVITY, newDisplay.mId,
+                    "Launched activity must be on the secondary display and resumed");
         }
     }
 
     /**
-     * Tests launching a non-resizeable activity on virtual display from activity there. It should
-     * land on some different suitable display (usually - on the default one).
+     * Tests launching a non-resizeable activity on virtual display in a new task from activity
+     * there. It must land on some different suitable display (usually - on the default one).
      */
     @Test
+    @FlakyTest(bugId = 112055644)
     public void testLaunchNonResizeableActivityFromSecondaryDisplayNewTask() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             // Create new virtual display.
@@ -325,33 +279,31 @@
 
             // Launch activity on new secondary display.
             launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
-            mAmWmState.assertFocusedActivity(
-                    "Activity launched on secondary display must be focused",
-                    LAUNCHING_ACTIVITY);
+            waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, newDisplay.mId,
+                    "Activity launched on secondary display must be focused");
 
-            // Check that launching activity is on the secondary display.
-            int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-            ActivityManagerState.ActivityStack frontStack =
-                    mAmWmState.getAmState().getStackById(frontStackId);
-            assertEquals("Launched activity must be on the secondary display and resumed",
-                    getActivityName(LAUNCHING_ACTIVITY),
-                    frontStack.mResumedActivity);
-            mAmWmState.assertFocusedStack("Focus must be on the secondary display", frontStackId);
-
-            // Launch non-resizeable activity from secondary display.
+            // Launch non-resizeable activity from secondary display in a new task.
             getLaunchActivityBuilder().setTargetActivity(NON_RESIZEABLE_ACTIVITY)
                     .setNewTask(true).setMultipleTask(true).execute();
 
-            // Check that non-resizeable activity is on the primary display.
-            frontStackId = mAmWmState.getAmState().getFocusedStackId();
-            frontStack = mAmWmState.getAmState().getStackById(frontStackId);
+            // Check that non-resizeable activity is on a different display.
+            final int newFrontStackId = mAmWmState.getAmState().getFocusedStackId();
+            final ActivityStack newFrontStack =
+                    mAmWmState.getAmState().getStackById(newFrontStackId);
             assertFalse("Launched activity must be on a different display",
-                    newDisplay.mId == frontStack.mDisplayId);
+                    newDisplay.mId == newFrontStack.mDisplayId);
             assertEquals("Launched activity must be resumed",
                     getActivityName(NON_RESIZEABLE_ACTIVITY),
-                    frontStack.mResumedActivity);
-            mAmWmState.assertFocusedStack("Focus must be on a just launched activity",
-                    frontStackId);
+                    newFrontStack.mResumedActivity);
+            mAmWmState.assertFocusedStack(
+                    "Top stack must be the one with just launched activity",
+                    newFrontStackId);
+            mAmWmState.assertResumedActivities("Both displays must have resumed activities",
+                    new SparseArray<ComponentName>(){{
+                        put(newDisplay.mId, LAUNCHING_ACTIVITY);
+                        put(newFrontStack.mDisplayId, NON_RESIZEABLE_ACTIVITY);
+                    }}
+            );
         }
     }
 
@@ -388,6 +340,7 @@
      * for activities with same UID.
      */
     @Test
+    @FlakyTest(bugId = 112055644)
     public void testLaunchWithoutPermissionOnVirtualDisplayByOwner() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             // Create new virtual display.
@@ -403,7 +356,7 @@
             mAmWmState.waitForValidState(TEST_ACTIVITY);
 
             final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-            final ActivityManagerState.ActivityStack focusedStack =
+            final ActivityStack focusedStack =
                     mAmWmState.getAmState().getStackById(externalFocusedStackId);
             assertEquals("Focused stack must be on secondary display", newDisplay.mId,
                     focusedStack.mDisplayId);
@@ -421,6 +374,7 @@
      * primary display.
      */
     @Test
+    @FlakyTest(bugId = 77469851)
     public void testConsequentLaunchActivity() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             // Create new virtual display.
@@ -428,26 +382,22 @@
 
             // Launch activity on new secondary display.
             launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
-            mAmWmState.computeState(TEST_ACTIVITY);
 
-            mAmWmState.assertFocusedActivity(
-                    "Activity launched on secondary display must be focused",
-                    TEST_ACTIVITY);
+            waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
+                    "Activity launched on secondary display must be on top");
 
             // Launch second activity without specifying display.
             launchActivity(LAUNCHING_ACTIVITY);
-            mAmWmState.computeState(LAUNCHING_ACTIVITY);
 
             // Check that activity is launched in focused stack on primary display.
-            mAmWmState.assertFocusedActivity("Launched activity must be focused",
-                    LAUNCHING_ACTIVITY);
-            final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY);
-            final ActivityManagerState.ActivityStack frontStack =
-                    mAmWmState.getAmState().getStackById(frontStackId);
-            assertEquals("Launched activity must be resumed in front stack",
-                    getActivityName(LAUNCHING_ACTIVITY), frontStack.mResumedActivity);
-            assertEquals("Front stack must be on primary display",
-                    DEFAULT_DISPLAY, frontStack.mDisplayId);
+            waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, DEFAULT_DISPLAY,
+                    "Launched activity must be focused");
+            mAmWmState.assertResumedActivities("Both displays must have resumed activities",
+                    new SparseArray<ComponentName>(){{
+                        put(newDisplay.mId, TEST_ACTIVITY);
+                        put(DEFAULT_DISPLAY, LAUNCHING_ACTIVITY);
+                    }}
+            );
         }
     }
 
@@ -456,6 +406,7 @@
      * first one - it must appear on the secondary display, because it was launched from there.
      */
     @Test
+    @FlakyTest(bugId = 112055644)
     public void testConsequentLaunchActivityFromSecondaryDisplay() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             // Create new simulated display.
@@ -464,24 +415,16 @@
 
             // Launch activity on new secondary display.
             launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
-            mAmWmState.computeState(new WaitForValidActivityState(LAUNCHING_ACTIVITY));
 
-            mAmWmState.assertFocusedActivity(
-                    "Activity launched on secondary display must be resumed",
-                    LAUNCHING_ACTIVITY);
+            waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, newDisplay.mId,
+                    "Activity launched on secondary display must be on top");
 
             // Launch second activity from app on secondary display without specifying display id.
             getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY).execute();
-            mAmWmState.computeState(TEST_ACTIVITY);
 
             // Check that activity is launched in focused stack on external display.
-            mAmWmState.assertFocusedActivity("Launched activity must be focused",
-                    TEST_ACTIVITY);
-            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-            final ActivityManagerState.ActivityStack frontStack =
-                    mAmWmState.getAmState().getStackById(frontStackId);
-            assertEquals("Launched activity must be resumed in front stack",
-                    getActivityName(TEST_ACTIVITY), frontStack.mResumedActivity);
+            waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
+                    "Launched activity must be on top");
         }
     }
 
@@ -497,24 +440,17 @@
 
             // Launch activity on new secondary display.
             launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
-            mAmWmState.computeState(LAUNCHING_ACTIVITY);
 
-            mAmWmState.assertFocusedActivity(
-                    "Activity launched on secondary display must be resumed",
-                    LAUNCHING_ACTIVITY);
+            waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, newDisplay.mId,
+                    "Activity launched on secondary display must be on top");
 
             // Launch second activity from app on secondary display without specifying display id.
             getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY).execute();
             mAmWmState.computeState(TEST_ACTIVITY);
 
             // Check that activity is launched in focused stack on external display.
-            mAmWmState.assertFocusedActivity("Launched activity must be focused",
-                    TEST_ACTIVITY);
-            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-            final ActivityManagerState.ActivityStack frontStack = mAmWmState.getAmState()
-                    .getStackById(frontStackId);
-            assertEquals("Launched activity must be resumed in front stack",
-                    getActivityName(TEST_ACTIVITY), frontStack.mResumedActivity);
+            waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
+                    "Launched activity must be on top");
         }
     }
 
@@ -523,6 +459,7 @@
      * first one with specifying the target display - it must appear on the secondary display.
      */
     @Test
+    @FlakyTest(bugId = 112055644)
     public void testConsequentLaunchActivityFromVirtualDisplayToTargetDisplay() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             // Create new virtual display.
@@ -530,26 +467,19 @@
 
             // Launch activity on new secondary display.
             launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
-            mAmWmState.computeState(LAUNCHING_ACTIVITY);
 
-            mAmWmState.assertFocusedActivity(
-                    "Activity launched on secondary display must be resumed",
-                    LAUNCHING_ACTIVITY);
+            waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, newDisplay.mId,
+                    "Activity launched on secondary display must be on top");
 
             // Launch second activity from app on secondary display specifying same display id.
             getLaunchActivityBuilder()
                     .setTargetActivity(SECOND_ACTIVITY)
                     .setDisplayId(newDisplay.mId)
                     .execute();
-            mAmWmState.computeState(TEST_ACTIVITY);
 
             // Check that activity is launched in focused stack on external display.
-            mAmWmState.assertFocusedActivity("Launched activity must be focused", SECOND_ACTIVITY);
-            int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-            ActivityManagerState.ActivityStack frontStack =
-                    mAmWmState.getAmState().getStackById(frontStackId);
-            assertEquals("Launched activity must be resumed in front stack",
-                    getActivityName(SECOND_ACTIVITY), frontStack.mResumedActivity);
+            waitAndAssertTopResumedActivity(SECOND_ACTIVITY, newDisplay.mId,
+                    "Launched activity must be on top");
 
             // Launch other activity with different uid and check if it has launched successfully.
             getLaunchActivityBuilder()
@@ -558,14 +488,10 @@
                     .setDisplayId(newDisplay.mId)
                     .setTargetActivity(THIRD_ACTIVITY)
                     .execute();
-            mAmWmState.waitForValidState(THIRD_ACTIVITY);
 
             // Check that activity is launched in focused stack on external display.
-            mAmWmState.assertFocusedActivity("Launched activity must be focused", THIRD_ACTIVITY);
-            frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-            frontStack = mAmWmState.getAmState().getStackById(frontStackId);
-            assertEquals("Launched activity must be resumed in front stack",
-                    getActivityName(THIRD_ACTIVITY), frontStack.mResumedActivity);
+            waitAndAssertTopResumedActivity(THIRD_ACTIVITY, newDisplay.mId,
+                    "Launched activity must be on top");
         }
     }
 
@@ -574,6 +500,7 @@
      * doesn't allow embedding - it should fail with security exception.
      */
     @Test
+    @FlakyTest(bugId = 112055644)
     public void testConsequentLaunchActivityFromVirtualDisplayNoEmbedding() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             // Create new virtual display.
@@ -581,11 +508,9 @@
 
             // Launch activity on new secondary display.
             launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
-            mAmWmState.computeState(LAUNCHING_ACTIVITY);
 
-            mAmWmState.assertFocusedActivity(
-                    "Activity launched on secondary display must be resumed",
-                    LAUNCHING_ACTIVITY);
+            waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, newDisplay.mId,
+                    "Activity launched on secondary display must be resumed");
 
             final LogSeparator logSeparator = separateLogs();
 
@@ -603,6 +528,7 @@
      * Tests launching an activity to secondary display from activity on primary display.
      */
     @Test
+    @FlakyTest(bugId = 112055644)
     public void testLaunchActivityFromAppToSecondaryDisplay() throws Exception {
         // Start launching activity.
         launchActivity(LAUNCHING_ACTIVITY);
@@ -617,15 +543,14 @@
                     .setDisplayId(newDisplay.mId).execute();
 
             // Check that activity is launched on external display.
-            mAmWmState.computeState(TEST_ACTIVITY);
-            mAmWmState.assertFocusedActivity(
-                    "Activity launched on secondary display must be focused",
-                    TEST_ACTIVITY);
-            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-            final ActivityManagerState.ActivityStack frontStack =
-                    mAmWmState.getAmState().getStackById(frontStackId);
-            assertEquals("Launched activity must be resumed in front stack",
-                    getActivityName(TEST_ACTIVITY), frontStack.mResumedActivity);
+            waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
+                    "Activity launched on secondary display must be focused");
+            mAmWmState.assertResumedActivities("Both displays must have resumed activities",
+                    new SparseArray<ComponentName>(){{
+                        put(DEFAULT_DISPLAY, LAUNCHING_ACTIVITY);
+                        put(newDisplay.mId, TEST_ACTIVITY);
+                    }}
+            );
         }
     }
 
@@ -654,6 +579,12 @@
             mAmWmState.waitForValidState(RESIZEABLE_ACTIVITY);
             mAmWmState.assertVisibility(TEST_ACTIVITY, true /* visible */);
             mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */);
+            mAmWmState.assertResumedActivities("Both displays must have resumed activities",
+                    new SparseArray<ComponentName>(){{
+                        put(DEFAULT_DISPLAY, RESIZEABLE_ACTIVITY);
+                        put(newDisplay.mId, TEST_ACTIVITY);
+                    }}
+            );
         }
     }
 
@@ -666,31 +597,30 @@
             // Create new virtual display.
             final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
             mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-            mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
+            mAmWmState.assertFocusedActivity("Virtual display activity must be on top",
                     VIRTUAL_DISPLAY_ACTIVITY);
             final int defaultDisplayStackId = mAmWmState.getAmState().getFocusedStackId();
-            ActivityManagerState.ActivityStack focusedStack = mAmWmState.getAmState().getStackById(
+            ActivityStack frontStack = mAmWmState.getAmState().getStackById(
                     defaultDisplayStackId);
-            assertEquals("Focus must remain on primary display",
-                    DEFAULT_DISPLAY, focusedStack.mDisplayId);
+            assertEquals("Top stack must remain on primary display",
+                    DEFAULT_DISPLAY, frontStack.mDisplayId);
 
             // Launch activity on new secondary display.
             launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
-            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
-                    TEST_ACTIVITY);
-            int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
-            focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
-            assertEquals("Focused stack must be on secondary display",
-                    newDisplay.mId, focusedStack.mDisplayId);
+
+            waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
+                    "Top activity must be on secondary display");
+            mAmWmState.assertResumedActivities("Both displays must have resumed activities",
+                    new SparseArray<ComponentName>(){{
+                        put(DEFAULT_DISPLAY, VIRTUAL_DISPLAY_ACTIVITY);
+                        put(newDisplay.mId, TEST_ACTIVITY);
+                    }}
+            );
 
             // Move activity from secondary display to primary.
             moveActivityToStack(TEST_ACTIVITY, defaultDisplayStackId);
-            mAmWmState.waitForFocusedStack(defaultDisplayStackId);
-            mAmWmState.assertFocusedActivity("Focus must be on moved activity", TEST_ACTIVITY);
-            focusedStackId = mAmWmState.getAmState().getFocusedStackId();
-            focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
-            assertEquals("Focus must return to primary display",
-                    DEFAULT_DISPLAY, focusedStack.mDisplayId);
+            waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
+                    "Moved activity must be on top");
         }
     }
 
@@ -700,6 +630,7 @@
      * This version launches virtual display creator to fullscreen stack in split-screen.
      */
     @Test
+    @FlakyTest(bugId = 77207453)
     public void testStackFocusSwitchOnDisplayRemoved() throws Exception {
         assumeTrue(supportsSplitScreenMultiWindow());
 
@@ -719,6 +650,7 @@
      * This version launches virtual display creator to docked stack in split-screen.
      */
     @Test
+    @FlakyTest(bugId = 77207453)
     public void testStackFocusSwitchOnDisplayRemoved2() throws Exception {
         assumeTrue(supportsSplitScreenMultiWindow());
 
@@ -744,7 +676,7 @@
         final int focusedStackWindowingMode = mAmWmState.getAmState().getFrontStackWindowingMode(
                 DEFAULT_DISPLAY);
         // Finish probing activity.
-        executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+        mBroadcastActionTrigger.finishBroadcastReceiverActivity();
 
         tryCreatingAndRemovingDisplayWithActivity(false /* splitScreen */,
                 focusedStackWindowingMode);
@@ -770,10 +702,10 @@
 
             // Launch activity on new secondary display.
             launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId);
-            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
-                    RESIZEABLE_ACTIVITY);
+            waitAndAssertTopResumedActivity(RESIZEABLE_ACTIVITY, newDisplay.mId,
+                    "Top activity must be on secondary display");
             final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+            mAmWmState.assertFocusedStack("Top stack must be on secondary display", frontStackId);
 
             // Destroy virtual display.
             logSeparator = separateLogs();
@@ -788,7 +720,7 @@
         mAmWmState.assertSanity();
         mAmWmState.assertValidBounds(true /* compareTaskAndStackBounds */);
 
-        // Check if the focus is switched back to primary display.
+        // Check if the top activity is now back on primary display.
         mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */);
         mAmWmState.assertFocusedStack(
                 "Default stack on primary display must be focused after display removed",
@@ -803,64 +735,88 @@
      * is moved correctly.
      */
     @Test
-    public void testStackFocusSwitchOnStackEmptied() throws Exception {
+    @FlakyTest(bugId = 112055644)
+    public void testStackFocusSwitchOnStackEmptiedInSleeping() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession();
              final LockScreenSession lockScreenSession = new LockScreenSession()) {
-            // Create new virtual display.
-            final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
-            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-            final int focusedStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY);
+            validateStackFocusSwitchOnStackEmptied(virtualDisplaySession, lockScreenSession);
+        }
+    }
 
-            // Launch activity on new secondary display.
-            launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
-            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
-                    BROADCAST_RECEIVER_ACTIVITY);
+    /**
+     * Tests launching activities on secondary display and then finishing it to see if stack focus
+     * is moved correctly.
+     */
+    @Test
+    @FlakyTest(bugId = 112055644)
+    public void testStackFocusSwitchOnStackEmptied() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            validateStackFocusSwitchOnStackEmptied(virtualDisplaySession,
+                    null /* lockScreenSession */);
+        }
+    }
 
+    private void validateStackFocusSwitchOnStackEmptied(VirtualDisplaySession virtualDisplaySession,
+            LockScreenSession lockScreenSession) throws Exception {
+        // Create new virtual display.
+        final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+        final int focusedStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY);
+
+        // Launch activity on new secondary display.
+        launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
+        waitAndAssertTopResumedActivity(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId,
+                "Top activity must be on secondary display");
+
+        if (lockScreenSession != null) {
             // Lock the device, so that activity containers will be detached.
             lockScreenSession.sleepDevice();
-
-            // Finish activity on secondary display.
-            executeShellCommand(FINISH_ACTIVITY_BROADCAST);
-
-            // Unlock and check if the focus is switched back to primary display.
-            lockScreenSession.wakeUpDevice()
-                    .unlockDevice();
-            mAmWmState.waitForFocusedStack(focusedStackId);
-            mAmWmState.waitForValidState(VIRTUAL_DISPLAY_ACTIVITY);
-            mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-            mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
-                    VIRTUAL_DISPLAY_ACTIVITY);
         }
+
+        // Finish activity on secondary display.
+        mBroadcastActionTrigger.finishBroadcastReceiverActivity();
+
+        if (lockScreenSession != null) {
+            // Unlock and check if the focus is switched back to primary display.
+            lockScreenSession.wakeUpDevice().unlockDevice();
+        }
+
+        waitAndAssertTopResumedActivity(VIRTUAL_DISPLAY_ACTIVITY, DEFAULT_DISPLAY,
+                "Top activity must be switched back to primary display");
     }
 
     /**
      * Tests that input events on the primary display take focus from the virtual display.
      */
     @Test
+    @FlakyTest(bugId = 112055644)
     public void testStackFocusSwitchOnTouchEvent() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             // Create new virtual display.
             final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
 
             mAmWmState.computeState(VIRTUAL_DISPLAY_ACTIVITY);
-            mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
+            mAmWmState.assertFocusedActivity("Top activity must be the latest launched one",
                     VIRTUAL_DISPLAY_ACTIVITY);
 
             launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
 
-            mAmWmState.computeState(TEST_ACTIVITY);
-            mAmWmState.assertFocusedActivity(
-                    "Activity launched on secondary display must be focused",
-                    TEST_ACTIVITY);
+            waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
+                    "Activity launched on secondary display must be on top");
 
             final ReportedDisplayMetrics displayMetrics = getDisplayMetrics();
             final int width = displayMetrics.getSize().getWidth();
             final int height = displayMetrics.getSize().getHeight();
             executeShellCommand("input tap " + (width / 2) + " " + (height / 2));
 
-            mAmWmState.computeState(VIRTUAL_DISPLAY_ACTIVITY);
-            mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
-                    VIRTUAL_DISPLAY_ACTIVITY);
+            waitAndAssertTopResumedActivity(VIRTUAL_DISPLAY_ACTIVITY, DEFAULT_DISPLAY,
+                    "Top activity must be on the primary display");
+            mAmWmState.assertResumedActivities("Both displays must have resumed activities",
+                    new SparseArray<ComponentName>(){{
+                        put(DEFAULT_DISPLAY, VIRTUAL_DISPLAY_ACTIVITY);
+                        put(newDisplay.mId, TEST_ACTIVITY);
+                    }}
+            );
         }
     }
 
@@ -871,22 +827,25 @@
             // Create new virtual display.
             final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
             mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-            mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
+            mAmWmState.assertFocusedActivity("Virtual display activity must be on top",
                     VIRTUAL_DISPLAY_ACTIVITY);
             final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-            ActivityManagerState.ActivityStack focusedStack = mAmWmState.getAmState().getStackById(
+            ActivityStack frontStack = mAmWmState.getAmState().getStackById(
                     defaultDisplayFocusedStackId);
-            assertEquals("Focus must remain on primary display",
-                    DEFAULT_DISPLAY, focusedStack.mDisplayId);
+            assertEquals("Top stack must remain on primary display",
+                    DEFAULT_DISPLAY, frontStack.mDisplayId);
 
             // Launch activity on new secondary display.
             launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
-            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
-                    TEST_ACTIVITY);
-            final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-            focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
-            assertEquals("Focused stack must be on secondary display", newDisplay.mId,
-                    focusedStack.mDisplayId);
+
+            waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
+                    "Front activity must be on secondary display");
+            mAmWmState.assertResumedActivities("Both displays must have resumed activities",
+                    new SparseArray<ComponentName>(){{
+                        put(DEFAULT_DISPLAY, VIRTUAL_DISPLAY_ACTIVITY);
+                        put(newDisplay.mId, TEST_ACTIVITY);
+                    }}
+            );
 
             // Launch other activity with different uid and check it is launched on dynamic stack on
             // secondary display.
@@ -894,16 +853,60 @@
                             + " --display " + newDisplay.mId;
             executeShellCommand(startCmd);
 
+            waitAndAssertTopResumedActivity(SECOND_ACTIVITY, newDisplay.mId,
+                    "Focus must be on newly launched app");
+            mAmWmState.assertResumedActivities("Both displays must have resumed activities",
+                    new SparseArray<ComponentName>(){{
+                        put(DEFAULT_DISPLAY, VIRTUAL_DISPLAY_ACTIVITY);
+                        put(newDisplay.mId, SECOND_ACTIVITY);
+                    }}
+            );
+        }
+    }
+
+    /** Test that launching app from pending activity queue on external display is allowed. */
+    @Test
+    public void testLaunchPendingActivityOnSecondaryDisplay() throws Exception {
+        try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+            // Create new simulated display.
+            final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
+                    .createDisplay();
+            final Bundle bundle = ActivityOptions.makeBasic().
+                    setLaunchDisplayId(newDisplay.mId).toBundle();
+            final Intent intent = new Intent(Intent.ACTION_VIEW)
+                    .setComponent(SECOND_ACTIVITY)
+                    .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
+                    .putExtra(KEY_LAUNCH_ACTIVITY, true)
+                    .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.
+            // 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
+            // ActivityTaskManagerService.APP_SWITCH_DELAY_TIME
+            mAmWmState.waitForPendingActivityContain(SECOND_ACTIVITY);
+            // If the activity is not pending, skip this test.
+            mAmWmState.assumePendingActivityContain(SECOND_ACTIVITY);
+            // In order to speed up test case without waiting for APP_SWITCH_DELAY_TIME, we launch
+            // another activity with LaunchActivityBuilder, in this way the activity can be start
+            // directly and also trigger pending activity to be launched.
+            getLaunchActivityBuilder()
+                    .setTargetActivity(THIRD_ACTIVITY)
+                    .execute();
             mAmWmState.waitForValidState(SECOND_ACTIVITY);
-            mAmWmState.assertFocusedActivity(
-                    "Focus must be on newly launched app", SECOND_ACTIVITY);
-            assertEquals("Activity launched by system must be on external display",
-                    externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
+            waitAndAssertTopResumedActivity(THIRD_ACTIVITY, DEFAULT_DISPLAY,
+                    "Top activity must be the newly launched one");
+            mAmWmState.assertVisibility(SECOND_ACTIVITY, true);
+            assertEquals("Activity launched by app on secondary display must be on that display",
+                    newDisplay.mId, mAmWmState.getAmState().getDisplayByActivity(SECOND_ACTIVITY));
         }
     }
 
     /** Test that launching from app that is on external display is allowed. */
     @Test
+    @FlakyTest(bugId = 112055644)
     public void testPermissionLaunchFromAppOnSecondary() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             // Create new simulated display.
@@ -915,14 +918,8 @@
                     + " --display " + newDisplay.mId;
             executeShellCommand(startCmd);
 
-            mAmWmState.waitForValidState(SECOND_ACTIVITY);
-            mAmWmState.assertFocusedActivity(
-                    "Focus must be on newly launched app", SECOND_ACTIVITY);
-            final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-            ActivityManagerState.ActivityStack focusedStack =
-                    mAmWmState.getAmState().getStackById(externalFocusedStackId);
-            assertEquals("Focused stack must be on secondary display", newDisplay.mId,
-                    focusedStack.mDisplayId);
+            waitAndAssertTopResumedActivity(SECOND_ACTIVITY, newDisplay.mId,
+                    "Top activity must be the newly launched one");
 
             // Launch another activity with third different uid from app on secondary display and
             // check it is launched on secondary display.
@@ -933,10 +930,8 @@
                     .setTargetActivity(THIRD_ACTIVITY)
                     .execute();
 
-            mAmWmState.waitForValidState(THIRD_ACTIVITY);
-            mAmWmState.assertFocusedActivity("Focus must be on newly launched app", THIRD_ACTIVITY);
-            assertEquals("Activity launched by app on secondary display must be on that display",
-                    externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
+            waitAndAssertTopResumedActivity(THIRD_ACTIVITY, newDisplay.mId,
+                    "Top activity must be the newly launched one");
         }
     }
 
@@ -952,20 +947,20 @@
 
             // Check that the first activity is launched onto the secondary display
             final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-            ActivityManagerState.ActivityStack frontStack = mAmWmState.getAmState().getStackById(
+            ActivityStack frontStack = mAmWmState.getAmState().getStackById(
                     frontStackId);
             assertEquals("Activity launched on secondary display must be resumed",
                     getActivityName(LAUNCHING_ACTIVITY),
                     frontStack.mResumedActivity);
-            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+            mAmWmState.assertFocusedStack("Top stack must be on secondary display",
+                    frontStackId);
 
             // Launch an activity from a different UID into the first activity's task
             getLaunchActivityBuilder().setTargetActivity(SECOND_ACTIVITY).execute();
 
-            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+            waitAndAssertTopResumedActivity(SECOND_ACTIVITY, newDisplay.mId,
+                    "Top activity must be the newly launched one");
             frontStack = mAmWmState.getAmState().getStackById(frontStackId);
-            mAmWmState.assertFocusedActivity(
-                    "Focus must be on newly launched app", SECOND_ACTIVITY);
             assertEquals("Secondary display must contain 1 task", 1, frontStack.getTasks().size());
         }
     }
@@ -975,6 +970,7 @@
      * doesn't have anything on the display.
      */
     @Test
+    @FlakyTest(bugId = 112055644)
     public void testPermissionLaunchFromOwner() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             // Create new virtual display.
@@ -983,23 +979,18 @@
             mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
                     VIRTUAL_DISPLAY_ACTIVITY);
             final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-            ActivityManagerState.ActivityStack focusedStack =
+            ActivityStack frontStack =
                     mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
-            assertEquals("Focus must remain on primary display",
-                    DEFAULT_DISPLAY, focusedStack.mDisplayId);
+            assertEquals("Top stack must remain on primary display",
+                    DEFAULT_DISPLAY, frontStack.mDisplayId);
 
             // Launch other activity with different uid on secondary display.
             final String startCmd = "am start -n " + getActivityName(SECOND_ACTIVITY)
                     + " --display " + newDisplay.mId;
             executeShellCommand(startCmd);
 
-            mAmWmState.waitForValidState(SECOND_ACTIVITY);
-            mAmWmState.assertFocusedActivity(
-                    "Focus must be on newly launched app", SECOND_ACTIVITY);
-            final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-            focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
-            assertEquals("Focused stack must be on secondary display", newDisplay.mId,
-                    focusedStack.mDisplayId);
+            waitAndAssertTopResumedActivity(SECOND_ACTIVITY, newDisplay.mId,
+                    "Top activity must be the newly launched one");
 
             // Check that owner uid can launch its own activity on secondary display.
             getLaunchActivityBuilder()
@@ -1009,11 +1000,8 @@
                     .setDisplayId(newDisplay.mId)
                     .execute();
 
-            mAmWmState.waitForValidState(TEST_ACTIVITY);
-            mAmWmState.assertFocusedActivity("Focus must be on newly launched app",
-                    TEST_ACTIVITY);
-            assertEquals("Activity launched by owner must be on external display",
-                    externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
+            waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
+                    "Top activity must be the newly launched one");
         }
     }
 
@@ -1030,19 +1018,15 @@
             mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
                     VIRTUAL_DISPLAY_ACTIVITY);
             final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-            ActivityManagerState.ActivityStack focusedStack = mAmWmState.getAmState().getStackById(
+            ActivityStack frontStack = mAmWmState.getAmState().getStackById(
                     defaultDisplayFocusedStackId);
-            assertEquals("Focus must remain on primary display",
-                    DEFAULT_DISPLAY, focusedStack.mDisplayId);
+            assertEquals("Top stack must remain on primary display",
+                    DEFAULT_DISPLAY, frontStack.mDisplayId);
 
             // Launch activity on new secondary display.
             launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
-            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
-                    TEST_ACTIVITY);
-            final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-            focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
-            assertEquals("Focused stack must be on secondary display", newDisplay.mId,
-                    focusedStack.mDisplayId);
+            waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
+                    "Top activity must be the newly launched one");
 
             final LogSeparator logSeparator = separateLogs();
 
@@ -1057,9 +1041,8 @@
             assertSecurityException("ActivityLauncher", logSeparator);
 
             mAmWmState.waitForValidState(TEST_ACTIVITY);
-            mAmWmState.assertFocusedActivity("Focus must be on first activity", TEST_ACTIVITY);
-            assertEquals("Focused stack must be on secondary display's stack",
-                    externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
+            mAmWmState.assertFocusedActivity("Top activity must be the first one launched",
+                    TEST_ACTIVITY);
         }
     }
 
@@ -1087,6 +1070,7 @@
      * Test that only private virtual display can show content with insecure keyguard.
      */
     @Test
+    @FlakyTest(bugId = 112055644)
     public void testFlagShowWithInsecureKeyguardOnPublicVirtualDisplay() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             // Try to create new show-with-insecure-keyguard public virtual display.
@@ -1114,13 +1098,12 @@
 
             // Launch activities on new secondary display.
             launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
-            mAmWmState.assertVisibility(TEST_ACTIVITY, true /* visible */);
-            mAmWmState.assertFocusedActivity("Launched activity must be focused",
-                    TEST_ACTIVITY);
+            waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
+                    "Launched activity must be on top");
+
             launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId);
-            mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */);
-            mAmWmState.assertFocusedActivity("Launched activity must be focused",
-                    RESIZEABLE_ACTIVITY);
+            waitAndAssertTopResumedActivity(RESIZEABLE_ACTIVITY, newDisplay.mId,
+                    "Launched activity must be on top");
 
             // Destroy the display and check if activities are removed from system.
             logSeparator = separateLogs();
@@ -1163,9 +1146,8 @@
             // Launch a resizeable activity on new secondary display.
             final LogSeparator initialLogSeparator = separateLogs();
             launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId);
-            mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */);
-            mAmWmState.assertFocusedActivity("Launched activity must be focused",
-                    RESIZEABLE_ACTIVITY);
+            waitAndAssertTopResumedActivity(RESIZEABLE_ACTIVITY, newDisplay.mId,
+                    "Launched activity must be on top");
 
             // Grab reported sizes and compute new with slight size change.
             final ReportedSizes initialSize = getLastReportedSizesForActivity(
@@ -1215,6 +1197,7 @@
      * matching task on some other display - that task will moved to the target display.
      */
     @Test
+    @FlakyTest(bugId = 112055644)
     public void testMoveToDisplayOnLaunch() throws Exception {
         // Launch activity with unique affinity, so it will the only one in its task.
         launchActivity(LAUNCHING_ACTIVITY);
@@ -1230,9 +1213,8 @@
 
             final int stackNum = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY)
                     .mStacks.size();
-            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-            final int taskNumOnSecondary = mAmWmState.getAmState().getStackById(frontStackId)
-                    .getTasks().size();
+            final int stackNumOnSecondary = mAmWmState.getAmState()
+                    .getDisplay(newDisplay.mId).mStacks.size();
 
             // Launch activity on new secondary display.
             // Using custom command here, because normally we add flags
@@ -1242,30 +1224,22 @@
             final String launchCommand = "am start -n " + getActivityName(LAUNCHING_ACTIVITY)
                     + " --display " + newDisplay.mId;
             executeShellCommand(launchCommand);
-            mAmWmState.waitForActivityState(LAUNCHING_ACTIVITY, STATE_RESUMED);
 
             // Check that activity is brought to front.
-            mAmWmState.assertFocusedActivity("Existing task must be brought to front",
-                    LAUNCHING_ACTIVITY);
-            mAmWmState.assertResumedActivity("Existing task must be resumed",
-                    LAUNCHING_ACTIVITY);
-
-            // Check that activity is on the right display.
-            final ActivityManagerState.ActivityStack firstFrontStack =
-                    mAmWmState.getAmState().getStackById(frontStackId);
-            assertEquals("Activity must be moved to the secondary display",
-                    getActivityName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
-            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+            waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, newDisplay.mId,
+                    "Existing task must be brought to front");
 
             // Check that task has moved from primary display to secondary.
+            // Since it is 1-to-1 relationship between task and stack for standard type &
+            // fullscreen activity, we check the number of stacks here
             final int stackNumFinal = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY)
                     .mStacks.size();
             assertEquals("Stack number in default stack must be decremented.", stackNum - 1,
                     stackNumFinal);
-            final int taskNumFinalOnSecondary = mAmWmState.getAmState().getStackById(frontStackId)
-                    .getTasks().size();
-            assertEquals("Task number in stack on external display must be incremented.",
-                    taskNumOnSecondary + 1, taskNumFinalOnSecondary);
+            final int stackNumFinalOnSecondary = mAmWmState.getAmState()
+                    .getDisplay(newDisplay.mId).mStacks.size();
+            assertEquals("Stack number on external display must be incremented.",
+                    stackNumOnSecondary + 1, stackNumFinalOnSecondary);
         }
     }
 
@@ -1293,21 +1267,10 @@
             final String launchCommand = "am start -n " + getActivityName(LAUNCHING_ACTIVITY)
                     + " --display " + newDisplay.mId;
             executeShellCommand(launchCommand);
-            mAmWmState.waitForActivityState(LAUNCHING_ACTIVITY, STATE_RESUMED);
 
             // Check that activity is brought to front.
-            mAmWmState.assertFocusedActivity("Existing task must be brought to front",
-                    LAUNCHING_ACTIVITY);
-            mAmWmState.assertResumedActivity("Existing task must be resumed",
-                    LAUNCHING_ACTIVITY);
-
-            // Check that activity is on the right display.
-            final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-            final ActivityManagerState.ActivityStack firstFrontStack =
-                    mAmWmState.getAmState().getStackById(frontStackId);
-            assertEquals("Activity must be moved to the secondary display",
-                    getActivityName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
-            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+            waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, newDisplay.mId,
+                    "Existing task must be brought to front");
 
             // Check that task has moved from primary display to secondary.
             final int stackNumFinal = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY)
@@ -1331,8 +1294,8 @@
             // Launch activity on new secondary display.
             LogSeparator logSeparator = separateLogs();
             launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId);
-            mAmWmState.assertFocusedActivity("Focus must be on secondary display",
-                    RESIZEABLE_ACTIVITY);
+            waitAndAssertTopResumedActivity(RESIZEABLE_ACTIVITY, newDisplay.mId,
+                    "Top activity must be on secondary display");
             final ReportedSizes initialSizes = getLastReportedSizesForActivity(
                     RESIZEABLE_ACTIVITY, logSeparator);
             assertNotNull("Test activity must have reported initial sizes on launch", initialSizes);
@@ -1349,9 +1312,8 @@
 
                 logSeparator = separateLogs();
                 launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
-                mAmWmState.waitForActivityState(TEST_ACTIVITY, STATE_RESUMED);
-                mAmWmState.assertFocusedActivity("Focus must be on secondary display",
-                        TEST_ACTIVITY);
+                waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
+                        "Top activity must be on secondary display");
                 final ReportedSizes testActivitySizes = getLastReportedSizesForActivity(
                         TEST_ACTIVITY, logSeparator);
                 assertEquals(
@@ -1378,6 +1340,7 @@
      * matching the task component root does.
      */
     @Test
+    @FlakyTest(bugId = 112055644)
     public void testTaskMatchAcrossDisplays() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
@@ -1387,11 +1350,12 @@
 
             // Check that activity is on the secondary display.
             final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-            final ActivityManagerState.ActivityStack firstFrontStack =
+            final ActivityStack firstFrontStack =
                     mAmWmState.getAmState().getStackById(frontStackId);
             assertEquals("Activity launched on secondary display must be resumed",
                     getActivityName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
-            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+            mAmWmState.assertFocusedStack("Top stack must be on secondary display",
+                    frontStackId);
 
             executeShellCommand("am start -n " + getActivityName(ALT_LAUNCHING_ACTIVITY));
             mAmWmState.waitForValidState(ALT_LAUNCHING_ACTIVITY);
@@ -1400,12 +1364,12 @@
             // the affinity match on the secondary display.
             final int defaultDisplayFrontStackId = mAmWmState.getAmState().getFrontStackId(
                     DEFAULT_DISPLAY);
-            final ActivityManagerState.ActivityStack defaultDisplayFrontStack =
+            final ActivityStack defaultDisplayFrontStack =
                     mAmWmState.getAmState().getStackById(defaultDisplayFrontStackId);
             assertEquals("Activity launched on default display must be resumed",
                     getActivityName(ALT_LAUNCHING_ACTIVITY),
                     defaultDisplayFrontStack.mResumedActivity);
-            mAmWmState.assertFocusedStack("Focus must be on primary display",
+            mAmWmState.assertFocusedStack("Top stack must be on primary display",
                     defaultDisplayFrontStackId);
 
             executeShellCommand("am start -n " + getActivityName(LAUNCHING_ACTIVITY));
@@ -1413,14 +1377,14 @@
 
             // Check that the third intent is redirected to the first task due to the root
             // component match on the secondary display.
-            final ActivityManagerState.ActivityStack secondFrontStack =
+            final ActivityStack secondFrontStack =
                     mAmWmState.getAmState().getStackById(frontStackId);
             assertEquals("Activity launched on secondary display must be resumed",
                     getActivityName(LAUNCHING_ACTIVITY), secondFrontStack.mResumedActivity);
-            mAmWmState.assertFocusedStack("Focus must be on primary display", frontStackId);
-            assertEquals("Focused stack must only contain 1 task",
+            mAmWmState.assertFocusedStack("Top stack must be on primary display", frontStackId);
+            assertEquals("Top stack must only contain 1 task",
                     1, secondFrontStack.getTasks().size());
-            assertEquals("Focused task must only contain 1 activity",
+            assertEquals("Top task must only contain 1 activity",
                     1, secondFrontStack.getTasks().get(0).mActivities.size());
         }
     }
@@ -1429,6 +1393,7 @@
      * Tests that the task affinity search respects the launch display id.
      */
     @Test
+    @FlakyTest(bugId = 112055644)
     public void testLaunchDisplayAffinityMatch() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
@@ -1437,7 +1402,7 @@
 
             // Check that activity is on the secondary display.
             final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-            final ActivityManagerState.ActivityStack firstFrontStack =
+            final ActivityStack firstFrontStack =
                     mAmWmState.getAmState().getStackById(frontStackId);
             assertEquals("Activity launched on secondary display must be resumed",
                     getActivityName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
@@ -1453,25 +1418,26 @@
             // task on the secondary display
             final int secondFrontStackId =
                     mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-            final ActivityManagerState.ActivityStack secondFrontStack =
+            final ActivityStack secondFrontStack =
                     mAmWmState.getAmState().getStackById(secondFrontStackId);
             assertEquals("Activity launched on secondary display must be resumed",
                     getActivityName(ALT_LAUNCHING_ACTIVITY),
                     secondFrontStack.mResumedActivity);
-            mAmWmState.assertFocusedStack("Focus must be on secondary display",
+            mAmWmState.assertFocusedStack("Top stack must be on secondary display",
                     secondFrontStackId);
-            assertEquals("Focused stack must only contain 1 task",
+            assertEquals("Top stack must only contain 1 task",
                     1, secondFrontStack.getTasks().size());
-            assertEquals("Focused task must contain 2 activities",
+            assertEquals("Top stack task must contain 2 activities",
                     2, secondFrontStack.getTasks().get(0).mActivities.size());
         }
     }
 
     /**
-     * Tests than a new task launched by an activity will end up on that activity's display
+     * Tests that a new task launched by an activity will end up on that activity's display
      * even if the focused stack is not on that activity's display.
      */
     @Test
+    @FlakyTest(bugId = 112055644)
     public void testNewTaskSameDisplay() throws Exception {
         try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
             final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
@@ -1482,37 +1448,48 @@
 
             // Check that the first activity is launched onto the secondary display
             final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-            final ActivityManagerState.ActivityStack firstFrontStack =
+            final ActivityStack firstFrontStack =
                     mAmWmState.getAmState().getStackById(frontStackId);
             assertEquals("Activity launched on secondary display must be resumed",
                     getActivityName(BROADCAST_RECEIVER_ACTIVITY),
                     firstFrontStack.mResumedActivity);
-            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+            mAmWmState.assertFocusedStack("Top stack must be on secondary display",
+                    frontStackId);
 
             executeShellCommand("am start -n " + getActivityName(TEST_ACTIVITY));
             mAmWmState.waitForValidState(TEST_ACTIVITY);
 
             // Check that the second activity is launched on the default display
             final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
-            final ActivityManagerState.ActivityStack focusedStack =
+            final ActivityStack focusedStack =
                     mAmWmState.getAmState().getStackById(focusedStackId);
             assertEquals("Activity launched on default display must be resumed",
                     getActivityName(TEST_ACTIVITY), focusedStack.mResumedActivity);
-            assertEquals("Focus must be on primary display",
+            assertEquals("Top stack must be on primary display",
                     DEFAULT_DISPLAY, focusedStack.mDisplayId);
+            mAmWmState.assertResumedActivities("Both displays should have resumed activities",
+                    new SparseArray<ComponentName>(){{
+                        put(DEFAULT_DISPLAY, TEST_ACTIVITY);
+                        put(newDisplay.mId, BROADCAST_RECEIVER_ACTIVITY);
+                    }}
+            );
 
-            executeShellCommand(LAUNCH_ACTIVITY_BROADCAST + getActivityName(LAUNCHING_ACTIVITY));
+            mBroadcastActionTrigger.launchActivityNewTask(getActivityName(LAUNCHING_ACTIVITY));
 
-            // Check that the third activity ends up in a new task in the same stack as the
-            // first activity
+            // Check that the third activity ends up in a new stack in the same display where the
+            // first activity lands
             mAmWmState.waitForValidState(LAUNCHING_ACTIVITY);
-            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-            final ActivityManagerState.ActivityStack secondFrontStack =
-                    mAmWmState.getAmState().getStackById(frontStackId);
             assertEquals("Activity must be launched on secondary display",
-                    getActivityName(LAUNCHING_ACTIVITY), secondFrontStack.mResumedActivity);
-            assertEquals("Secondary display must contain 2 tasks",
-                    2, secondFrontStack.getTasks().size());
+                    getActivityName(LAUNCHING_ACTIVITY),
+                    mAmWmState.getAmState().getResumedActivityOnDisplay(newDisplay.mId));
+            assertEquals("Secondary display must contain 2 stacks", 2,
+                    mAmWmState.getAmState().getDisplay(newDisplay.mId).mStacks.size());
+            mAmWmState.assertResumedActivities("Both displays should have resumed activities",
+                    new SparseArray<ComponentName>(){{
+                        put(DEFAULT_DISPLAY, TEST_ACTIVITY);
+                        put(newDisplay.mId, LAUNCHING_ACTIVITY);
+                    }}
+            );
         }
     }
 
@@ -1529,14 +1506,14 @@
 
             // Check that activity is launched and placed correctly.
             mAmWmState.waitForActivityState(TEST_ACTIVITY, STATE_RESUMED);
-            mAmWmState.assertResumedActivity("Test activity must be launched on a new display",
-                    TEST_ACTIVITY);
+            mAmWmState.assertResumedActivity("Test activity must be on top", TEST_ACTIVITY);
             final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-            final ActivityManagerState.ActivityStack firstFrontStack =
+            final ActivityStack firstFrontStack =
                     mAmWmState.getAmState().getStackById(frontStackId);
             assertEquals("Activity launched on secondary display must be resumed",
                     getActivityName(TEST_ACTIVITY), firstFrontStack.mResumedActivity);
-            mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+            mAmWmState.assertFocusedStack("Top stack must be on secondary display",
+                    frontStackId);
         }
     }
 
@@ -1548,7 +1525,7 @@
     public void testExternalDisplayActivityTurnPrimaryOff() throws Exception {
         // Launch something on the primary display so we know there is a resumed activity there
         launchActivity(RESIZEABLE_ACTIVITY);
-        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
+        waitAndAssertTopResumedActivity(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
                 "Activity launched on primary display must be resumed");
 
         try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession();
@@ -1560,7 +1537,7 @@
             launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
 
             // Check that the activity is launched onto the external display
-            waitAndAssertActivityResumed(TEST_ACTIVITY, newDisplay.mId,
+            waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
                     "Activity launched on external display must be resumed");
 
             final LogSeparator logSeparator = separateLogs();
@@ -1584,7 +1561,7 @@
                 fail(RESIZEABLE_ACTIVITY + " has received " + lifecycleCounts.mStopCount
                         + " onStop() calls, expecting 1");
             }
-            waitAndAssertActivityResumed(TEST_ACTIVITY, newDisplay.mId,
+            waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
                     "Activity launched on external display must be resumed");
         }
     }
@@ -1594,10 +1571,11 @@
      * display is off.
      */
     @Test
+    @FlakyTest(bugId = 112055644)
     public void testLaunchExternalDisplayActivityWhilePrimaryOff() throws Exception {
         // Launch something on the primary display so we know there is a resumed activity there
         launchActivity(RESIZEABLE_ACTIVITY);
-        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
+        waitAndAssertTopResumedActivity(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
                 "Activity launched on primary display must be resumed");
 
         try (final PrimaryDisplayStateSession displayStateSession =
@@ -1617,7 +1595,7 @@
             launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
 
             // Check that the test activity is resumed on the external display
-            waitAndAssertActivityResumed(TEST_ACTIVITY, newDisplay.mId,
+            waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
                     "Activity launched on external display must be resumed");
         }
     }
@@ -1634,7 +1612,7 @@
             launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
 
             // Check that the test activity is resumed on the external display
-            waitAndAssertActivityResumed(TEST_ACTIVITY, newDisplay.mId,
+            waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
                     "Activity launched on external display must be resumed");
 
             externalDisplaySession.turnDisplayOff();
@@ -1646,7 +1624,7 @@
             externalDisplaySession.turnDisplayOn();
 
             // Check that turning on the external display resumes the activity
-            waitAndAssertActivityResumed(TEST_ACTIVITY, newDisplay.mId,
+            waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
                     "Activity launched on external display must be resumed");
         }
     }
@@ -1656,10 +1634,11 @@
      * activity on the primary display.
      */
     @Test
+    @FlakyTest(bugId = 112055644)
     public void testStackFocusSwitchOnTouchEventAfterKeyguard() throws Exception {
         // Launch something on the primary display so we know there is a resumed activity there
         launchActivity(RESIZEABLE_ACTIVITY);
-        waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
+        waitAndAssertTopResumedActivity(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
                 "Activity launched on primary display must be resumed");
 
         try (final LockScreenSession lockScreenSession = new LockScreenSession();
@@ -1677,9 +1656,15 @@
 
             launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
 
-            // Check that the test activity is resumed on the external display
-            waitAndAssertActivityResumed(TEST_ACTIVITY, newDisplay.mId,
-                    "Activity launched on external display must be resumed");
+            // Check that the test activity is resumed on the external display and is on top
+            waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId,
+                    "Activity on external display must be resumed and on top");
+            mAmWmState.assertResumedActivities("Both displays should have resumed activities",
+                    new SparseArray<ComponentName>(){{
+                        put(DEFAULT_DISPLAY, RESIZEABLE_ACTIVITY);
+                        put(newDisplay.mId, TEST_ACTIVITY);
+                    }}
+            );
 
             // Unlock the device and tap on the middle of the primary display
             lockScreenSession.wakeUpDevice();
@@ -1691,28 +1676,37 @@
             final int height = displayMetrics.getSize().getHeight();
             executeShellCommand("input tap " + (width / 2) + " " + (height / 2));
 
-            // Check that the activity on the primary display is resumed
-            waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
-                    "Activity launched on primary display must be resumed");
-            assertEquals("Unexpected resumed activity",
-                    1, mAmWmState.getAmState().getResumedActivitiesCount());
+            // Check that the activity on the primary display is the topmost resumed
+            waitAndAssertTopResumedActivity(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
+                    "Activity on primary display must be resumed and on top");
+            mAmWmState.assertResumedActivities("Both displays should have resumed activities",
+                    new SparseArray<ComponentName>(){{
+                        put(DEFAULT_DISPLAY, RESIZEABLE_ACTIVITY);
+                        put(newDisplay.mId, TEST_ACTIVITY);
+                    }}
+            );
         }
     }
 
-    private void waitAndAssertActivityResumed(
-            ComponentName activityName, int displayId, String message) throws Exception {
+    private void waitAndAssertTopResumedActivity(ComponentName activityName, int displayId,
+            String message) throws Exception {
+        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");
 
-        assertEquals(message,
-                getActivityName(activityName), mAmWmState.getAmState().getResumedActivity());
+        mAmWmState.assertSanity();
+        mAmWmState.assertFocusedActivity(message, activityName);
         final int frontStackId = mAmWmState.getAmState().getFrontStackId(displayId);
-        ActivityManagerState.ActivityStack firstFrontStack =
+        ActivityStack frontStackOnDisplay =
                 mAmWmState.getAmState().getStackById(frontStackId);
-        assertEquals(message,
-                getActivityName(activityName), firstFrontStack.mResumedActivity);
-        assertTrue(message,
+        assertEquals("Resumed activity of front stack of the target display must match. " + message,
+                activityClassName, frontStackOnDisplay.mResumedActivity);
+        assertTrue("Activity must be resumed. " + message,
                 mAmWmState.getAmState().hasActivityState(activityName, STATE_RESUMED));
-        mAmWmState.assertFocusedStack("Focus must be on external display", frontStackId);
+        mAmWmState.assertFocusedStack("Top activity's stack must also be on top", frontStackId);
         mAmWmState.assertVisibility(activityName, true /* visible */);
     }
 
@@ -1750,28 +1744,6 @@
         }
     }
 
-    /** Assert that component received onMovedToDisplay and onConfigurationChanged callbacks. */
-    private void assertMovedToDisplay(ComponentName componentName, LogSeparator logSeparator)
-            throws Exception {
-        final ActivityLifecycleCounts lifecycleCounts =
-                new ActivityLifecycleCounts(componentName, logSeparator);
-        if (lifecycleCounts.mDestroyCount != 0) {
-            fail(componentName + " has been destroyed " + lifecycleCounts.mDestroyCount
-                    + " time(s), wasn't expecting any");
-        } else if (lifecycleCounts.mCreateCount != 0) {
-            fail(componentName + " has been (re)created " + lifecycleCounts.mCreateCount
-                    + " time(s), wasn't expecting any");
-        } else if (lifecycleCounts.mConfigurationChangedCount != 1) {
-            fail(componentName + " has received "
-                    + lifecycleCounts.mConfigurationChangedCount
-                    + " onConfigurationChanged() calls, expecting " + 1);
-        } else if (lifecycleCounts.mMovedToDisplayCount != 1) {
-            fail(componentName + " has received "
-                    + lifecycleCounts.mMovedToDisplayCount
-                    + " onMovedToDisplay() calls, expecting " + 1);
-        }
-    }
-
     private class ExternalDisplaySession implements AutoCloseable {
 
         @Nullable
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
index a873c09..0d100d6 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
@@ -16,7 +16,7 @@
 
 package android.server.am;
 
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -41,7 +41,6 @@
 import static android.server.am.Components.PipActivity.ACTION_EXPAND_PIP;
 import static android.server.am.Components.PipActivity.ACTION_FINISH;
 import static android.server.am.Components.PipActivity.ACTION_MOVE_TO_BACK;
-import static android.server.am.Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION;
 import static android.server.am.Components.PipActivity.EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP;
 import static android.server.am.Components.PipActivity.EXTRA_ENTER_PIP;
 import static android.server.am.Components.PipActivity.EXTRA_ENTER_PIP_ASPECT_RATIO_DENOMINATOR;
@@ -53,8 +52,6 @@
 import static android.server.am.Components.PipActivity.EXTRA_REENTER_PIP_ON_EXIT;
 import static android.server.am.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_DENOMINATOR;
 import static android.server.am.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_NUMERATOR;
-import static android.server.am.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR;
-import static android.server.am.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR;
 import static android.server.am.Components.PipActivity.EXTRA_START_ACTIVITY;
 import static android.server.am.Components.PipActivity.EXTRA_TAP_TO_FINISH;
 import static android.server.am.Components.RESUME_WHILE_PAUSING_ACTIVITY;
@@ -322,7 +319,7 @@
                 EXTRA_PIP_ORIENTATION, String.valueOf(ORIENTATION_LANDSCAPE));
         // Enter PiP, and assert that the PiP is within bounds now that the device is back in
         // portrait
-        executeShellCommand("am broadcast -a " + ACTION_ENTER_PIP);
+        mBroadcastActionTrigger.doAction(ACTION_ENTER_PIP);
         // Wait for animation complete since we are comparing bounds
         waitForEnterPipAnimationComplete(PIP_ACTIVITY);
         assertPinnedStackExists();
@@ -695,7 +692,7 @@
 
         // Remove the stack and ensure that the task is now in the fullscreen stack (when no
         // fullscreen stack existed before)
-        executeShellCommand("am broadcast -a " + ACTION_MOVE_TO_BACK);
+        mBroadcastActionTrigger.doAction(ACTION_MOVE_TO_BACK);
         assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY,
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
     }
@@ -714,7 +711,7 @@
 
         // Remove the stack and ensure that the task is placed in the fullscreen stack, behind the
         // top fullscreen activity
-        executeShellCommand("am broadcast -a " + ACTION_MOVE_TO_BACK);
+        mBroadcastActionTrigger.doAction(ACTION_MOVE_TO_BACK);
         assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
     }
@@ -735,7 +732,7 @@
 
         // Remove the stack and ensure that the task is placed on top of the hidden fullscreen
         // stack, but that the home stack is still focused
-        executeShellCommand("am broadcast -a " + ACTION_MOVE_TO_BACK);
+        mBroadcastActionTrigger.doAction(ACTION_MOVE_TO_BACK);
         assertPinnedStackStateOnMoveToFullscreen(
                 PIP_ACTIVITY, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
     }
@@ -804,13 +801,11 @@
          * but since we can't launch into the home stack directly, we have a workaround.
          *
          * 1) Launch an activity in a new dynamic stack
-         * 2) Resize the dynamic stack to non-fullscreen bounds
-         * 3) Start the PiP activity that will enter picture-in-picture when paused in the
+         * 2) Start the PiP activity that will enter picture-in-picture when paused in the
          *    fullscreen stack
-         * 4) Bring the activity in the dynamic stack forward to trigger PiP
+         * 3) Bring the activity in the dynamic stack forward to trigger PiP
          */
-        int stackId = launchActivityInNewDynamicStack(RESUME_WHILE_PAUSING_ACTIVITY);
-        resizeStack(stackId, 0, 0, 500, 500);
+        launchActivity(RESUME_WHILE_PAUSING_ACTIVITY);
         // Launch an activity that will enter PiP when it is paused with a delay that is long enough
         // for the next resumeWhilePausing activity to finish resuming, but slow enough to not
         // trigger the current system pause timeout (currently 500ms)
@@ -833,7 +828,7 @@
         // Lock the task and ensure that we can't enter picture-in-picture both explicitly and
         // when paused
         executeShellCommand("am task lock " + task.mTaskId);
-        executeShellCommand("am broadcast -a " + ACTION_ENTER_PIP);
+        mBroadcastActionTrigger.doAction(ACTION_ENTER_PIP);
         waitForEnterPip(PIP_ACTIVITY);
         assertPinnedStackDoesNotExist();
         launchHomeActivity();
@@ -851,7 +846,7 @@
         // configuration change happened after the picture-in-picture and multi-window callbacks
         launchActivity(PIP_ACTIVITY);
         LogSeparator logSeparator = separateLogs();
-        executeShellCommand("am broadcast -a " + ACTION_ENTER_PIP);
+        mBroadcastActionTrigger.doAction(ACTION_ENTER_PIP);
         waitForEnterPip(PIP_ACTIVITY);
         assertPinnedStackExists();
         waitForValidPictureInPictureCallbacks(PIP_ACTIVITY, logSeparator);
@@ -935,7 +930,9 @@
 
         // Launch a PiP activity
         launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
-        waitForEnterPip(PIP_ACTIVITY);
+        // Wait for animation complete so that system has reported pip mode change event to
+        // client and the last reported pip mode has updated.
+        waitForEnterPipAnimationComplete(PIP_ACTIVITY);
         assertPinnedStackExists();
 
         // Dismiss it
@@ -970,9 +967,7 @@
 
         // Trigger it to go back to fullscreen and try to set the aspect ratio, and ensure that the
         // call to set the aspect ratio did not prevent the PiP from returning to fullscreen
-        executeShellCommand("am broadcast -a " + ACTION_EXPAND_PIP
-                + " -e " + EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR + " 123456789"
-                + " -e " + EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR + " 100000000");
+        mBroadcastActionTrigger.expandPipWithAspectRatio("123456789", "100000000");
         waitForExitPipToFullscreen(PIP_ACTIVITY);
         assertPinnedStackDoesNotExist();
     }
@@ -989,10 +984,7 @@
         assertPinnedStackExists();
 
         // Request that the orientation is set to landscape
-        executeShellCommand("am broadcast -a "
-                + ACTION_SET_REQUESTED_ORIENTATION + " -e "
-                + EXTRA_PIP_ORIENTATION + " "
-                + String.valueOf(ORIENTATION_LANDSCAPE));
+        mBroadcastActionTrigger.requestOrientationForPip(ORIENTATION_LANDSCAPE);
 
         // Launch the activity back into fullscreen and ensure that it is now in landscape
         launchActivity(PIP_ACTIVITY);
@@ -1031,7 +1023,7 @@
         launchPinnedActivityAsTaskOverlay(TRANSLUCENT_TEST_ACTIVITY, taskId);
 
         // Finish the PiP activity and ensure that there is no pinned stack
-        executeShellCommand("am broadcast -a " + ACTION_FINISH);
+        mBroadcastActionTrigger.doAction(ACTION_FINISH);
         waitForPinnedStackRemoved();
         assertPinnedStackDoesNotExist();
     }
@@ -1056,7 +1048,7 @@
         // got resumed
         LogSeparator logSeparator = separateLogs();
         executeShellCommand("am stack resize-animated " + stackId + " 20 20 500 500");
-        executeShellCommand("am broadcast -a " + TEST_ACTIVITY_ACTION_FINISH_SELF);
+        mBroadcastActionTrigger.doAction(TEST_ACTIVITY_ACTION_FINISH_SELF);
         mAmWmState.waitFor((amState, wmState) ->
                         !amState.containsActivity(TRANSLUCENT_TEST_ACTIVITY),
                 "Waiting for test activity to finish...");
@@ -1249,9 +1241,9 @@
 
         // Expand the PiP back to fullscreen and back into PiP and ensure that it is in the same
         // position as before we expanded (and that the default bounds reflect that)
-        executeShellCommand("am broadcast -a " + ACTION_EXPAND_PIP);
+        mBroadcastActionTrigger.doAction(ACTION_EXPAND_PIP);
         waitForExitPipToFullscreen(PIP_ACTIVITY);
-        executeShellCommand("am broadcast -a " + ACTION_ENTER_PIP);
+        mBroadcastActionTrigger.doAction(ACTION_ENTER_PIP);
         waitForEnterPipAnimationComplete(PIP_ACTIVITY);
         mAmWmState.computeState(true);
         // Due to rounding in how we save and apply the snap fraction we may be a pixel off, so just
@@ -1262,13 +1254,13 @@
 
         // Expand the PiP, then launch an activity in a new task, and ensure that the PiP goes back
         // to the default position (and not the saved position) the next time it is launched
-        executeShellCommand("am broadcast -a " + ACTION_EXPAND_PIP);
+        mBroadcastActionTrigger.doAction(ACTION_EXPAND_PIP);
         waitForExitPipToFullscreen(PIP_ACTIVITY);
         launchActivity(TEST_ACTIVITY);
-        executeShellCommand("am broadcast -a " + TEST_ACTIVITY_ACTION_FINISH_SELF);
+        mBroadcastActionTrigger.doAction(TEST_ACTIVITY_ACTION_FINISH_SELF);
         mAmWmState.waitForActivityState(PIP_ACTIVITY, STATE_RESUMED);
         mAmWmState.waitForAppTransitionIdle();
-        executeShellCommand("am broadcast -a " + ACTION_ENTER_PIP);
+        mBroadcastActionTrigger.doAction(ACTION_ENTER_PIP);
         waitForEnterPipAnimationComplete(PIP_ACTIVITY);
         assertPinnedStackExists();
         assertTrue("Expected initialBounds=" + initialBounds + " to equal bounds="
@@ -1299,7 +1291,7 @@
         offsetPipWithinMovementBounds(100 /* offsetY */, initialBounds, offsetBounds);
 
         // Finish the activity
-        executeShellCommand("am broadcast -a " + ACTION_FINISH);
+        mBroadcastActionTrigger.doAction(ACTION_FINISH);
         waitForPinnedStackRemoved();
         assertPinnedStackDoesNotExist();
 
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
index ae483d8..93ee4fe 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
@@ -16,8 +16,8 @@
 
 package android.server.am;
 
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -26,7 +26,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.server.am.Components.ALT_LAUNCHING_ACTIVITY;
 import static android.server.am.Components.DOCKED_ACTIVITY;
 import static android.server.am.Components.LAUNCHING_ACTIVITY;
 import static android.server.am.Components.NON_RESIZEABLE_ACTIVITY;
@@ -277,7 +276,7 @@
 
         // Move to split-screen primary
         final int taskId = mAmWmState.getAmState().getTaskByActivity(LAUNCHING_ACTIVITY).mTaskId;
-        moveTaskToPrimarySplitScreen(taskId, true /* launchSideActivityIfNeeded */);
+        moveTaskToPrimarySplitScreen(taskId, true /* showRecents */);
 
         // Launch target to side
         final LaunchActivityBuilder targetActivityLauncher = getLaunchActivityBuilder()
@@ -504,9 +503,9 @@
     public void testFinishDockActivityWhileMinimized() throws Exception {
         launchActivityInDockStackAndMinimize(TEST_ACTIVITY);
 
-        executeShellCommand("am broadcast -a " + TEST_ACTIVITY_ACTION_FINISH_SELF);
+        mBroadcastActionTrigger.doAction(TEST_ACTIVITY_ACTION_FINISH_SELF);
         waitForDockNotMinimized();
-        mAmWmState.assertVisibility(TEST_ACTIVITY, false);
+        mAmWmState.assertNotExist(TEST_ACTIVITY);
         assertDockNotMinimized();
     }
 
@@ -545,8 +544,12 @@
         }
     }
 
+    /**
+     * Verify split screen mode visibility after stack resize occurs.
+     */
     @Test
     @Presubmit
+    @FlakyTest(bugId = 110276714)
     public void testResizeDockedStack() throws Exception {
         launchActivitiesInSplitScreen(
                 getLaunchActivityBuilder().setTargetActivity(DOCKED_ACTIVITY),
@@ -559,10 +562,6 @@
                 WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD);
         mAmWmState.assertContainsStack("Must contain primary split-screen stack.",
                 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
-        assertEquals(new Rect(0, 0, STACK_SIZE, STACK_SIZE),
-                mAmWmState.getAmState().getStandardStackByWindowingMode(
-                        WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).getBounds());
-        mAmWmState.assertDockedTaskBounds(TASK_SIZE, TASK_SIZE, DOCKED_ACTIVITY);
         mAmWmState.assertVisibility(DOCKED_ACTIVITY, true);
         mAmWmState.assertVisibility(TEST_ACTIVITY, true);
     }
diff --git a/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
index 5d2d82b..84040a6 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
@@ -18,8 +18,6 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.server.am.Components.BroadcastReceiverActivity.ACTION_TRIGGER_BROADCAST;
-import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_DISMISS_KEYGUARD;
 import static android.server.am.Components.DISMISS_KEYGUARD_ACTIVITY;
 import static android.server.am.Components.DISMISS_KEYGUARD_METHOD_ACTIVITY;
 import static android.server.am.Components.PIP_ACTIVITY;
@@ -44,12 +42,6 @@
  *     atest CtsActivityManagerDeviceTestCases:KeyguardLockedTests
  */
 public class KeyguardLockedTests extends KeyguardTestBase {
-
-    // TODO(b/70247058): Use {@link Context#sendBroadcast(Intent).
-    // Shell command to dismiss keyguard via {@link #BROADCAST_RECEIVER_ACTIVITY}.
-    private static final String DISMISS_KEYGUARD_BROADCAST = "am broadcast -a "
-            + ACTION_TRIGGER_BROADCAST + " --ez " + EXTRA_DISMISS_KEYGUARD + " true";
-
     @Before
     @Override
     public void setUp() throws Exception {
@@ -119,7 +111,7 @@
             launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
             mAmWmState.computeState(SHOW_WHEN_LOCKED_ACTIVITY);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
-            executeShellCommand(DISMISS_KEYGUARD_BROADCAST);
+            mBroadcastActionTrigger.dismissKeyguardByFlag();
             lockScreenSession.enterAndConfirmLockCredential();
 
             // Make sure we stay on Keyguard.
@@ -195,7 +187,7 @@
             mAmWmState.assertKeyguardShowingAndOccluded();
 
             // Request that the PiP activity enter picture-in-picture mode (ensure it does not)
-            executeShellCommand("am broadcast -a " + ACTION_ENTER_PIP);
+            mBroadcastActionTrigger.doAction(ACTION_ENTER_PIP);
             waitForEnterPip(PIP_ACTIVITY);
             mAmWmState.assertDoesNotContainStack("Must not contain pinned stack.",
                     WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
@@ -232,7 +224,7 @@
 
             // Lock the screen and ensure that the fullscreen activity showing over the lockscreen
             // is visible, but not the PiP activity
-            lockScreenSession.gotoKeyguard();
+            lockScreenSession.gotoKeyguard(SHOW_WHEN_LOCKED_ACTIVITY);
             mAmWmState.computeState(true);
             mAmWmState.assertKeyguardShowingAndOccluded();
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
diff --git a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
index a53993b..d1b4204 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
@@ -20,10 +20,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.server.am.ComponentNameUtils.getWindowName;
 import static android.server.am.Components.BROADCAST_RECEIVER_ACTIVITY;
-import static android.server.am.Components.BroadcastReceiverActivity.ACTION_TRIGGER_BROADCAST;
-import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_DISMISS_KEYGUARD;
-import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_DISMISS_KEYGUARD_METHOD;
-import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_FINISH_BROADCAST;
 import static android.server.am.Components.DISMISS_KEYGUARD_ACTIVITY;
 import static android.server.am.Components.DISMISS_KEYGUARD_METHOD_ACTIVITY;
 import static android.server.am.Components.KEYGUARD_LOCK_ACTIVITY;
@@ -48,6 +44,7 @@
 
 import android.platform.test.annotations.Presubmit;
 import android.server.am.WindowManagerState.WindowState;
+import android.support.test.filters.FlakyTest;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -57,15 +54,6 @@
  *     atest CtsActivityManagerDeviceTestCases:KeyguardTests
  */
 public class KeyguardTests extends KeyguardTestBase {
-
-    // TODO(b/70247058): Use {@link Context#sendBroadcast(Intent).
-    // Shell command to dismiss keyguard via {@link #BROADCAST_RECEIVER_ACTIVITY}.
-    private static final String DISMISS_KEYGUARD_BROADCAST = "am broadcast -a "
-            + ACTION_TRIGGER_BROADCAST + " --ez " + EXTRA_DISMISS_KEYGUARD + " true";
-    // Shell command to dismiss keyguard via {@link #BROADCAST_RECEIVER_ACTIVITY} method.
-    private static final String DISMISS_KEYGUARD_METHOD_BROADCAST = "am broadcast -a "
-            + ACTION_TRIGGER_BROADCAST + " --ez " + EXTRA_DISMISS_KEYGUARD_METHOD + " true";
-
     @Before
     @Override
     public void setUp() throws Exception {
@@ -91,12 +79,13 @@
     }
 
     @Test
+    @FlakyTest(bugId = 110276714)
     public void testShowWhenLockedActivity() throws Exception {
         try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
             launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
             mAmWmState.computeState(SHOW_WHEN_LOCKED_ACTIVITY);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
-            lockScreenSession.gotoKeyguard();
+            lockScreenSession.gotoKeyguard(SHOW_WHEN_LOCKED_ACTIVITY);
             mAmWmState.computeState(true);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
             mAmWmState.assertKeyguardShowingAndOccluded();
@@ -113,7 +102,7 @@
             launchActivity(SHOW_WHEN_LOCKED_WITH_DIALOG_ACTIVITY);
             mAmWmState.computeState(SHOW_WHEN_LOCKED_WITH_DIALOG_ACTIVITY);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_WITH_DIALOG_ACTIVITY, true);
-            lockScreenSession.gotoKeyguard();
+            lockScreenSession.gotoKeyguard(SHOW_WHEN_LOCKED_WITH_DIALOG_ACTIVITY);
             mAmWmState.computeState(true);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_WITH_DIALOG_ACTIVITY, true);
             assertTrue(mAmWmState.getWmState().allWindowsVisible(
@@ -134,7 +123,8 @@
                     SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY, true);
-            lockScreenSession.gotoKeyguard();
+            lockScreenSession.gotoKeyguard(
+                    SHOW_WHEN_LOCKED_ACTIVITY, SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY);
             mAmWmState.computeState(true);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY, true);
@@ -151,7 +141,7 @@
             launchActivity(SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY);
             mAmWmState.computeState(SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY, true);
-            lockScreenSession.gotoKeyguard();
+            lockScreenSession.gotoKeyguard(SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY);
             mAmWmState.computeState(true);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY, true);
             assertWallpaperShowing();
@@ -170,7 +160,7 @@
             mAmWmState.computeState(TEST_ACTIVITY, SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY);
             mAmWmState.assertVisibility(TEST_ACTIVITY, true);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY, true);
-            lockScreenSession.gotoKeyguard();
+            lockScreenSession.gotoKeyguard(SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY);
             mAmWmState.computeState(true);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY, true);
             mAmWmState.assertVisibility(TEST_ACTIVITY, false);
@@ -208,7 +198,7 @@
                             .setMultipleTask(false)
             );
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
-            lockScreenSession.gotoKeyguard();
+            lockScreenSession.gotoKeyguard(SHOW_WHEN_LOCKED_ACTIVITY);
             mAmWmState.computeState(SHOW_WHEN_LOCKED_ACTIVITY);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
             mAmWmState.assertKeyguardShowingAndOccluded();
@@ -259,7 +249,7 @@
             assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
             launchActivity(BROADCAST_RECEIVER_ACTIVITY);
             launchActivity(TEST_ACTIVITY);
-            executeShellCommand(DISMISS_KEYGUARD_METHOD_BROADCAST);
+            mBroadcastActionTrigger.dismissKeyguardByMethod();
             assertOnDismissErrorInLogcat(logSeparator);
         }
     }
@@ -290,7 +280,7 @@
             mAmWmState.computeState(SHOW_WHEN_LOCKED_ACTIVITY);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
             mAmWmState.assertKeyguardShowingAndOccluded();
-            executeShellCommand(DISMISS_KEYGUARD_BROADCAST);
+            mBroadcastActionTrigger.dismissKeyguardByFlag();
             mAmWmState.assertKeyguardShowingAndOccluded();
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
         }
@@ -304,7 +294,7 @@
             launchActivity(KEYGUARD_LOCK_ACTIVITY);
             mAmWmState.computeState(KEYGUARD_LOCK_ACTIVITY);
             mAmWmState.assertVisibility(KEYGUARD_LOCK_ACTIVITY, true);
-            executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+            mBroadcastActionTrigger.finishBroadcastReceiverActivity();
             mAmWmState.waitForKeyguardShowingAndNotOccluded();
             mAmWmState.assertKeyguardShowingAndNotOccluded();
         }
@@ -330,7 +320,8 @@
             mAmWmState.assertSanity();
             mAmWmState.assertHomeActivityVisible(false);
             mAmWmState.assertKeyguardShowingAndNotOccluded();
-            mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, false);
+            // The {@link SHOW_WHEN_LOCKED_ACTIVITY} has gone because of {@link pressBackButton()}.
+            mAmWmState.assertNotExist(SHOW_WHEN_LOCKED_ACTIVITY);
         }
     }
 
diff --git a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTransitionTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTransitionTests.java
index 105121b..cdc115a 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTransitionTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTransitionTests.java
@@ -16,6 +16,7 @@
 
 package android.server.am;
 
+import static android.server.am.ActivityManagerState.STATE_STOPPED;
 import static android.server.am.Components.SHOW_WHEN_LOCKED_ACTIVITY;
 import static android.server.am.Components.SHOW_WHEN_LOCKED_ATTR_ACTIVITY;
 import static android.server.am.Components.SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY;
@@ -102,7 +103,7 @@
     public void testNewActivityDuringOccluded() throws Exception {
         try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
             launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
-            lockScreenSession.gotoKeyguard();
+            lockScreenSession.gotoKeyguard(SHOW_WHEN_LOCKED_ACTIVITY);
             launchActivity(SHOW_WHEN_LOCKED_WITH_DIALOG_ACTIVITY);
             mAmWmState.computeState(SHOW_WHEN_LOCKED_WITH_DIALOG_ACTIVITY);
             assertEquals("Picked wrong transition", TRANSIT_ACTIVITY_OPEN,
@@ -134,10 +135,16 @@
                     mAmWmState.getWmState().getLastTransition());
             assertSingleLaunch(SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY, logSeparator);
 
+            // Waiting for the standard keyguard since
+            // {@link SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY} called
+            // {@link Activity#showWhenLocked(boolean)} and removed the attribute.
             lockScreenSession.gotoKeyguard();
             logSeparator = separateLogs();
-            launchActivity(SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY);
-            mAmWmState.computeState(SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY);
+            // Waiting for {@link SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY} stopped since it
+            // already lost show-when-locked attribute.
+            launchActivityNoWait(SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY);
+            mAmWmState.waitForActivityState(
+                    SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY, STATE_STOPPED);
             assertSingleStartAndStop(SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY, logSeparator);
         }
     }
@@ -146,7 +153,7 @@
     public void testNewActivityDuringOccludedWithAttr() throws Exception {
         try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
             launchActivity(SHOW_WHEN_LOCKED_ATTR_ACTIVITY);
-            lockScreenSession.gotoKeyguard();
+            lockScreenSession.gotoKeyguard(SHOW_WHEN_LOCKED_ATTR_ACTIVITY);
             launchActivity(SHOW_WHEN_LOCKED_WITH_DIALOG_ACTIVITY);
             mAmWmState.computeState(SHOW_WHEN_LOCKED_WITH_DIALOG_ACTIVITY);
             assertEquals("Picked wrong transition", TRANSIT_ACTIVITY_OPEN,
diff --git a/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleTests.java b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleTests.java
index a381d04..00dcc2b 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleTests.java
@@ -316,8 +316,7 @@
         waitAndAssertActivityStates(state(callbackTrackingActivity, ON_RESUME));
 
         // Enter split screen
-        moveTaskToPrimarySplitScreen(callbackTrackingActivity.getTaskId(),
-                true /* launchSideActivityIfNeeded */);
+        moveTaskToPrimarySplitScreen(callbackTrackingActivity.getTaskId(), true /* showRecents */);
 
         // Launch second activity to pause first
         // Create an ActivityMonitor that catch ChildActivity and return mock ActivityResult:
@@ -376,8 +375,7 @@
         waitAndAssertActivityStates(state(firstActivity, ON_RESUME));
 
         // Enter split screen
-        moveTaskToPrimarySplitScreen(firstActivity.getTaskId(),
-                true /* launchSideActivityIfNeeded */);
+        moveTaskToPrimarySplitScreen(firstActivity.getTaskId(), true /* showRecents */);
 
         // Launch second activity to pause first
         final Activity secondActivity =
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java
index fe1f588..a1a9a844 100644
--- a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java
@@ -16,7 +16,7 @@
 
 package android.server.am;
 
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
@@ -32,6 +32,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
 import static org.hamcrest.Matchers.lessThan;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -41,6 +42,7 @@
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
 
 import android.content.ComponentName;
 import android.graphics.Rect;
@@ -51,6 +53,7 @@
 import android.server.am.WindowManagerState.WindowStack;
 import android.server.am.WindowManagerState.WindowState;
 import android.server.am.WindowManagerState.WindowTask;
+import android.util.SparseArray;
 
 import java.util.Arrays;
 import java.util.List;
@@ -211,7 +214,7 @@
         logE("***Waiting for debugger window failed");
     }
 
-    boolean waitForHomeActivityVisible() {
+    void waitForHomeActivityVisible() {
         ComponentName homeActivity = mAmState.getHomeActivityName();
         // Sometimes this function is called before we know what Home Activity is
         if (homeActivity == null) {
@@ -221,20 +224,15 @@
         }
         assertNotNull("homeActivity should not be null", homeActivity);
         waitForValidState(homeActivity);
-        return mAmState.isHomeActivityVisible();
     }
 
-    /**
-     * @return true if recents activity is visible. Devices without recents will return false
-     */
-    boolean waitForRecentsActivityVisible() {
+    void waitForRecentsActivityVisible() {
         if (mAmState.isHomeRecentsComponent()) {
-            return waitForHomeActivityVisible();
+            waitForHomeActivityVisible();
+        } else {
+            waitForWithAmState(ActivityManagerState::isRecentsActivityVisible,
+                    "***Waiting for recents activity to be visible...");
         }
-
-        waitForWithAmState(ActivityManagerState::isRecentsActivityVisible,
-                "***Waiting for recents activity to be visible...");
-        return mAmState.isRecentsActivityVisible();
     }
 
     void waitForKeyguardShowingAndNotOccluded() {
@@ -294,6 +292,11 @@
                 "***Waiting for focused stack...");
     }
 
+    void waitForPendingActivityContain(ComponentName activity) {
+        waitForWithAmState(state -> state.pendingActivityContain(activity),
+                "***Waiting for activity in pending list...");
+    }
+
     void waitForAppTransitionIdle() {
         waitForWithWmState(
                 state -> WindowManagerState.APP_STATE_IDLE.equals(state.getAppTransitionState()),
@@ -358,7 +361,7 @@
             return true;
         }
         final int resumedActivitiesCount = mAmState.getResumedActivitiesCount();
-        if (!mAmState.getKeyguardControllerState().keyguardShowing && resumedActivitiesCount != 1) {
+        if (!mAmState.getKeyguardControllerState().keyguardShowing && resumedActivitiesCount < 1) {
             logAlways("***resumedActivitiesCount=" + resumedActivitiesCount);
             return true;
         }
@@ -490,9 +493,10 @@
 
     void assertSanity() {
         assertThat("Must have stacks", mAmState.getStackCount(), greaterThan(0));
+        // TODO: Update when keyguard will be shown on multiple displays
         if (!mAmState.getKeyguardControllerState().keyguardShowing) {
-            assertEquals("There should be one and only one resumed activity in the system.",
-                    1, mAmState.getResumedActivitiesCount());
+            assertThat("There should be at least one resumed activity in the system.",
+                    mAmState.getResumedActivitiesCount(), greaterThanOrEqualTo(1));
         }
         assertNotNull("Must have focus activity.", mAmState.getFocusedActivity());
 
@@ -518,11 +522,6 @@
         assertFalse(msg, mWmState.containsStack(windowingMode, activityType));
     }
 
-    void assertFrontStack(String msg, int stackId) {
-        assertEquals(msg, stackId, mAmState.getFrontStackId(DEFAULT_DISPLAY));
-        assertEquals(msg, stackId, mWmState.getFrontStackId(DEFAULT_DISPLAY));
-    }
-
     void assertFrontStack(String msg, int windowingMode, int activityType) {
         if (windowingMode != WINDOWING_MODE_UNDEFINED) {
             assertEquals(msg, windowingMode,
@@ -538,7 +537,6 @@
         assertEquals(msg, activityType, mWmState.getFrontStackActivityType(DEFAULT_DISPLAY));
     }
 
-    @Deprecated
     void assertFocusedStack(String msg, int stackId) {
         assertEquals(msg, stackId, mAmState.getFocusedStackId());
     }
@@ -564,11 +562,24 @@
     }
 
     public void assertResumedActivity(final String msg, final ComponentName activityName) {
-        assertEquals(msg, getActivityName(activityName), mAmState.getResumedActivity());
+        assertEquals(msg, getActivityName(activityName),
+                mAmState.getFocusedActivity());
+    }
+
+    /** Asserts that each display has correct resumed activity. */
+    public void assertResumedActivities(final String msg,
+            SparseArray<ComponentName> resumedActivities) {
+        for (int i = 0; i < resumedActivities.size(); i++) {
+            final int displayId = resumedActivities.keyAt(i);
+            final ComponentName activityComponent = resumedActivities.valueAt(i);
+            assertEquals("Error asserting resumed activity on display " + displayId + ": " + msg,
+                    activityComponent != null ? getActivityName(activityComponent) : null,
+                    mAmState.getResumedActivityOnDisplay(displayId));
+        }
     }
 
     void assertNotResumedActivity(String msg, ComponentName activityName) {
-        assertNotEquals(msg, mAmState.getResumedActivity(), getActivityName(activityName));
+        assertNotEquals(msg, mAmState.getFocusedActivity(), getActivityName(activityName));
     }
 
     void assertFocusedWindow(String msg, String windowName) {
@@ -579,19 +590,26 @@
         assertNotEquals(msg, mWmState.getFocusedWindow(), windowName);
     }
 
-    void assertFrontWindow(String msg, String windowName) {
-        assertEquals(msg, windowName, mWmState.getFrontWindow());
+    void assertNotExist(final ComponentName activityName) {
+        final String windowName = getWindowName(activityName);
+        assertFalse("Activity=" + getActivityName(activityName) + " must NOT exist.",
+                mAmState.containsActivity(activityName));
+        assertFalse("Window=" + windowName + " must NOT exits.",
+                mWmState.containsWindow(windowName));
     }
 
     public void assertVisibility(final ComponentName activityName, final boolean visible) {
         final String windowName = getWindowName(activityName);
-        final boolean activityVisible = mAmState.isActivityVisible(activityName);
-        final boolean windowVisible = mWmState.isWindowVisible(windowName);
+        // Check existence of activity and window.
+        assertTrue("Activity=" + getActivityName(activityName) + " must exist.",
+                mAmState.containsActivity(activityName));
+        assertTrue("Window=" + windowName + " must exist.", mWmState.containsWindow(windowName));
 
+        // Check visibility of activity and window.
         assertEquals("Activity=" + getActivityName(activityName) + " must" + (visible ? "" : " NOT")
-                + " be visible.", visible, activityVisible);
+                + " be visible.", visible, mAmState.isActivityVisible(activityName));
         assertEquals("Window=" + windowName + " must" + (visible ? "" : " NOT") + " be visible.",
-                visible, windowVisible);
+                visible, mWmState.isWindowVisible(windowName));
     }
 
     void assertHomeActivityVisible(boolean visible) {
@@ -632,6 +650,10 @@
                 getAmState().getKeyguardControllerState().keyguardShowing);
     }
 
+    public void assumePendingActivityContain(ComponentName activity) {
+        assumeTrue(getAmState().pendingActivityContain(activity));
+    }
+
     boolean taskListsInAmAndWmAreEqual() {
         for (ActivityStack aStack : mAmState.getStacks()) {
             final int stackId = aStack.mStackId;
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityLauncher.java b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityLauncher.java
index 9542dbb..a115b77 100644
--- a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityLauncher.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityLauncher.java
@@ -90,6 +90,10 @@
      * it's always written to logs.
      */
     public static final String KEY_SUPPRESS_EXCEPTIONS = "suppress_exceptions";
+    /**
+     * Key for int extra with target activity type where activity should be launched as.
+     */
+    public static final String KEY_ACTIVITY_TYPE = "activity_type";
 
 
     /** Perform an activity launch configured by provided extras. */
@@ -131,6 +135,13 @@
             options.setLaunchDisplayId(displayId);
             newIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK);
         }
+        final int activityType = extras.getInt(KEY_ACTIVITY_TYPE, -1);
+        if (activityType != -1) {
+            if (options == null) {
+                options = ActivityOptions.makeBasic();
+            }
+            options.setLaunchActivityType(activityType);
+        }
         final Bundle optionsBundle = options != null ? options.toBundle() : null;
 
         final Context launchContext = extras.getBoolean(KEY_USE_APPLICATION_CONTEXT) ?
@@ -141,8 +152,12 @@
                 // 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, 0, optionsBundle);
+                        newIntent, PendingIntent.FLAG_CANCEL_CURRENT, optionsBundle);
                 pendingIntent.send();
             } else {
                 launchContext.startActivity(newIntent, optionsBundle);
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerState.java b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerState.java
index d50e622..7fc080d 100644
--- a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerState.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerState.java
@@ -16,7 +16,7 @@
 
 package android.server.am;
 
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -67,10 +67,12 @@
     // Stacks in z-order with the top most at the front of the list, starting with primary display.
     private final List<ActivityStack> mStacks = new ArrayList<>();
     private KeyguardControllerState mKeyguardControllerState;
-    private int mFocusedStackId = -1;
+    private final List<String> mPendingActivities = new ArrayList<>();
+    private int mTopFocusedStackId = -1;
     private Boolean mIsHomeRecentsComponent;
-    private String mResumedActivityRecord = null;
-    private final List<String> mResumedActivities = new ArrayList<>();
+    private String mTopResumedActivityRecord = null;
+    final List<String> mResumedActivitiesInStacks = new ArrayList<>();
+    final List<String> mResumedActivitiesInDisplays = new ArrayList<>();
 
     void computeState() {
         computeState(DUMP_MODE_ACTIVITIES);
@@ -109,20 +111,21 @@
                         + new String(dump, StandardCharsets.UTF_8), ex);
             }
 
-            retry = mStacks.isEmpty() || mFocusedStackId == -1 || (mResumedActivityRecord == null
-                    || mResumedActivities.isEmpty()) && !mKeyguardControllerState.keyguardShowing;
+            retry = mStacks.isEmpty() || mTopFocusedStackId == -1
+                    || (mTopResumedActivityRecord == null || mResumedActivitiesInStacks.isEmpty())
+                    && !mKeyguardControllerState.keyguardShowing;
         } while (retry && retriesLeft-- > 0);
 
         if (mStacks.isEmpty()) {
             logE("No stacks found...");
         }
-        if (mFocusedStackId == -1) {
+        if (mTopFocusedStackId == -1) {
             logE("No focused stack found...");
         }
-        if (mResumedActivityRecord == null) {
+        if (mTopResumedActivityRecord == null) {
             logE("No focused activity found...");
         }
-        if (mResumedActivities.isEmpty()) {
+        if (mResumedActivitiesInStacks.isEmpty()) {
             logE("No resumed activities found...");
         }
     }
@@ -156,21 +159,27 @@
             mDisplays.add(new ActivityDisplay(activityDisplay, this));
         }
         mKeyguardControllerState = new KeyguardControllerState(state.keyguardController);
-        mFocusedStackId = state.focusedStackId;
+        mTopFocusedStackId = state.focusedStackId;
         if (state.resumedActivity != null) {
-            mResumedActivityRecord = state.resumedActivity.title;
+            mTopResumedActivityRecord = state.resumedActivity.title;
         }
         mIsHomeRecentsComponent = new Boolean(state.isHomeRecentsComponent);
+
+        for (int i = 0; i < state.pendingActivities.length; i++) {
+            mPendingActivities.add(state.pendingActivities[i].title);
+        }
     }
 
     private void reset() {
         mDisplays.clear();
         mStacks.clear();
-        mFocusedStackId = -1;
-        mResumedActivityRecord = null;
-        mResumedActivities.clear();
+        mTopFocusedStackId = -1;
+        mTopResumedActivityRecord = null;
+        mResumedActivitiesInStacks.clear();
+        mResumedActivitiesInDisplays.clear();
         mKeyguardControllerState = null;
         mIsHomeRecentsComponent = null;
+        mPendingActivities.clear();
     }
 
     /**
@@ -205,29 +214,29 @@
     }
 
     int getFocusedStackId() {
-        return mFocusedStackId;
+        return mTopFocusedStackId;
     }
 
     int getFocusedStackActivityType() {
-        final ActivityStack stack = getStackById(mFocusedStackId);
+        final ActivityStack stack = getStackById(mTopFocusedStackId);
         return stack != null ? stack.getActivityType() : ACTIVITY_TYPE_UNDEFINED;
     }
 
     int getFocusedStackWindowingMode() {
-        final ActivityStack stack = getStackById(mFocusedStackId);
+        final ActivityStack stack = getStackById(mTopFocusedStackId);
         return stack != null ? stack.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
     }
 
     String getFocusedActivity() {
-        return mResumedActivityRecord;
-    }
-
-    String getResumedActivity() {
-        return mResumedActivities.get(0);
+        return mTopResumedActivityRecord;
     }
 
     int getResumedActivitiesCount() {
-        return mResumedActivities.size();
+        return mResumedActivitiesInStacks.size();
+    }
+
+    String getResumedActivityOnDisplay(int displayId) {
+        return getDisplay(displayId).mResumedActivity;
     }
 
     public KeyguardControllerState getKeyguardControllerState() {
@@ -539,21 +548,32 @@
         return null;
     }
 
+    boolean pendingActivityContain(ComponentName activityName) {
+        return mPendingActivities.contains(getActivityName(activityName));
+    }
+
     static class ActivityDisplay extends ActivityContainer {
 
         int mId;
         ArrayList<ActivityStack> mStacks = new ArrayList<>();
+        int mFocusedStackId;
+        String mResumedActivity;
 
         ActivityDisplay(ActivityDisplayProto proto, ActivityManagerState amState) {
             super(proto.configurationContainer);
             mId = proto.id;
+            mFocusedStackId = proto.focusedStackId;
+            if (proto.resumedActivity != null) {
+                mResumedActivity = proto.resumedActivity.title;
+                amState.mResumedActivitiesInDisplays.add(mResumedActivity);
+            }
             for (int i = 0; i < proto.stacks.length; i++) {
                 ActivityStack activityStack = new ActivityStack(proto.stacks[i]);
                 mStacks.add(activityStack);
                 // Also update activity manager state
                 amState.mStacks.add(activityStack);
                 if (activityStack.mResumedActivity != null) {
-                    amState.mResumedActivities.add(activityStack.mResumedActivity);
+                    amState.mResumedActivitiesInStacks.add(activityStack.mResumedActivity);
                 }
             }
         }
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
index 59ea4ab..8ab05e0 100644
--- a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
@@ -16,8 +16,8 @@
 
 package android.server.am;
 
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -33,6 +33,7 @@
 import static android.content.pm.PackageManager.FEATURE_SCREEN_PORTRAIT;
 import static android.content.pm.PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
+import static android.server.am.ActivityLauncher.KEY_ACTIVITY_TYPE;
 import static android.server.am.ActivityLauncher.KEY_DISPLAY_ID;
 import static android.server.am.ActivityLauncher.KEY_LAUNCH_ACTIVITY;
 import static android.server.am.ActivityLauncher.KEY_LAUNCH_TO_SIDE;
@@ -50,8 +51,17 @@
 import static android.server.am.ComponentNameUtils.getLogTag;
 import static android.server.am.Components.BROADCAST_RECEIVER_ACTIVITY;
 import static android.server.am.Components.BroadcastReceiverActivity.ACTION_TRIGGER_BROADCAST;
+import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_BROADCAST_ORIENTATION;
+import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_DISMISS_KEYGUARD;
+import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_DISMISS_KEYGUARD_METHOD;
 import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_FINISH_BROADCAST;
+import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_MOVE_BROADCAST_TO_BACK;
 import static android.server.am.Components.LAUNCHING_ACTIVITY;
+import static android.server.am.Components.PipActivity.ACTION_EXPAND_PIP;
+import static android.server.am.Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION;
+import static android.server.am.Components.PipActivity.EXTRA_PIP_ORIENTATION;
+import static android.server.am.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR;
+import static android.server.am.Components.PipActivity.EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR;
 import static android.server.am.Components.TEST_ACTIVITY;
 import static android.server.am.StateLogger.log;
 import static android.server.am.StateLogger.logAlways;
@@ -76,16 +86,16 @@
 import android.accessibilityservice.AccessibilityService;
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.content.ComponentName;
 import android.content.Context;
-import android.graphics.Bitmap;
 import android.content.Intent;
+import android.graphics.Bitmap;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.server.am.settings.SettingsSession;
 import android.support.test.InstrumentationRegistry;
-
 import android.support.test.rule.ActivityTestRule;
 
 import com.android.compatibility.common.util.SystemUtil;
@@ -148,11 +158,6 @@
 
     private static final String LOCK_CREDENTIAL = "1234";
 
-    // TODO(b/70247058): Use {@link Context#sendBroadcast(Intent).
-    // Shell command to finish {@link #BROADCAST_RECEIVER_ACTIVITY}.
-    static final String FINISH_ACTIVITY_BROADCAST = "am broadcast -a "
-            + ACTION_TRIGGER_BROADCAST + " --ez " + EXTRA_FINISH_BROADCAST + " true";
-
     private static final int UI_MODE_TYPE_MASK = 0x0f;
     private static final int UI_MODE_TYPE_VR_HEADSET = 0x07;
 
@@ -162,6 +167,7 @@
 
     protected Context mContext;
     protected ActivityManager mAm;
+    protected ActivityTaskManager mAtm;
 
     @Rule
     public final ActivityTestRule<SideActivity> mSideActivityRule =
@@ -232,10 +238,71 @@
 
     protected ActivityAndWindowManagersState mAmWmState = new ActivityAndWindowManagersState();
 
+    protected BroadcastActionTrigger mBroadcastActionTrigger = new BroadcastActionTrigger();
+
+    /**
+     * Helper class to process test actions by broadcast.
+     */
+    protected class BroadcastActionTrigger {
+
+        private Intent createIntentWithAction(String broadcastAction) {
+            return new Intent(broadcastAction)
+                    .setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        }
+
+        void doAction(String broadcastAction) {
+            mContext.sendBroadcast(createIntentWithAction(broadcastAction));
+        }
+
+        void finishBroadcastReceiverActivity() {
+            mContext.sendBroadcast(createIntentWithAction(ACTION_TRIGGER_BROADCAST)
+                    .putExtra(EXTRA_FINISH_BROADCAST, true));
+        }
+
+        void launchActivityNewTask(String launchComponent) {
+            mContext.sendBroadcast(createIntentWithAction(ACTION_TRIGGER_BROADCAST)
+                    .putExtra(KEY_LAUNCH_ACTIVITY, true)
+                    .putExtra(KEY_NEW_TASK, true)
+                    .putExtra(KEY_TARGET_COMPONENT, launchComponent));
+        }
+
+        void moveTopTaskToBack() {
+            mContext.sendBroadcast(createIntentWithAction(ACTION_TRIGGER_BROADCAST)
+                    .putExtra(EXTRA_MOVE_BROADCAST_TO_BACK, true));
+        }
+
+        void requestOrientation(int orientation) {
+            mContext.sendBroadcast(createIntentWithAction(ACTION_TRIGGER_BROADCAST)
+                    .putExtra(EXTRA_BROADCAST_ORIENTATION, orientation));
+        }
+
+        void dismissKeyguardByFlag() {
+            mContext.sendBroadcast(createIntentWithAction(ACTION_TRIGGER_BROADCAST)
+                    .putExtra(EXTRA_DISMISS_KEYGUARD, true));
+        }
+
+        void dismissKeyguardByMethod() {
+            mContext.sendBroadcast(createIntentWithAction(ACTION_TRIGGER_BROADCAST)
+                    .putExtra(EXTRA_DISMISS_KEYGUARD_METHOD, true));
+        }
+
+        void expandPipWithAspectRatio(String extraNum, String extraDenom) {
+            mContext.sendBroadcast(createIntentWithAction(ACTION_EXPAND_PIP)
+                    .putExtra(EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR, extraNum)
+                    .putExtra(EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR, extraDenom));
+        }
+
+        void requestOrientationForPip(int orientation) {
+            mContext.sendBroadcast(createIntentWithAction(ACTION_SET_REQUESTED_ORIENTATION)
+                    .putExtra(EXTRA_PIP_ORIENTATION, String.valueOf(orientation)));
+        }
+    }
+
     @Before
     public void setUp() throws Exception {
         mContext = InstrumentationRegistry.getContext();
         mAm = mContext.getSystemService(ActivityManager.class);
+        mAtm = mContext.getSystemService(ActivityTaskManager.class);
 
         InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
                 mContext.getPackageName(), android.Manifest.permission.MANAGE_ACTIVITY_STACKS);
@@ -261,12 +328,12 @@
     }
 
     protected void removeStacksWithActivityTypes(int... activityTypes) {
-        mAm.removeStacksWithActivityTypes(activityTypes);
+        mAtm.removeStacksWithActivityTypes(activityTypes);
         waitForIdle();
     }
 
     protected void removeStacksInWindowingModes(int... windowingModes) {
-        mAm.removeStacksInWindowingModes(windowingModes);
+        mAtm.removeStacksInWindowingModes(windowingModes);
         waitForIdle();
     }
 
@@ -301,25 +368,6 @@
         mAmWmState.waitForValidState(activityName);
     }
 
-    /**
-     * Starts an activity in a new stack.
-     * @return the stack id of the newly created stack.
-     */
-    @Deprecated
-    protected int launchActivityInNewDynamicStack(ComponentName activityName) {
-        HashSet<Integer> stackIds = getStackIds();
-        executeShellCommand("am stack start " + DEFAULT_DISPLAY + " "
-                + getActivityName(activityName));
-        HashSet<Integer> newStackIds = getStackIds();
-        newStackIds.removeAll(stackIds);
-        if (newStackIds.isEmpty()) {
-            return INVALID_STACK_ID;
-        } else {
-            assertTrue(newStackIds.size() == 1);
-            return newStackIds.iterator().next();
-        }
-    }
-
     private static void waitForIdle() {
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     }
@@ -375,7 +423,7 @@
             int createMode) {
         launchActivity(activityName);
         final int taskId = mAmWmState.getAmState().getTaskByActivity(activityName).mTaskId;
-        mAm.setTaskWindowingModeSplitScreenPrimary(taskId, createMode, true /* onTop */,
+        mAtm.setTaskWindowingModeSplitScreenPrimary(taskId, createMode, true /* onTop */,
                 false /* animate */, null /* initialBounds */, true /* showRecents */);
 
         mAmWmState.waitForValidState(
@@ -387,22 +435,21 @@
     }
 
     public void moveTaskToPrimarySplitScreen(int taskId) {
-        moveTaskToPrimarySplitScreen(taskId, false /* launchSideActivityIfNeeded */);
+        moveTaskToPrimarySplitScreen(taskId, false /* showRecents */);
     }
 
     /**
      * Moves the device into split-screen with the specified task into the primary stack.
-     * @param taskId                        The id of the task to move into the primary stack.
-     * @param launchSideActivityIfNeeded    Whether a placeholder activity should be launched if no
-     *                                      recents activity is available.
+     * @param taskId        The id of the task to move into the primary stack.
+     * @param showRecents   Whether to show the recents activity (or a placeholder activity in
+     *                      place of the Recents activity if home is the recents component)
      */
-    public void moveTaskToPrimarySplitScreen(int taskId, boolean launchSideActivityIfNeeded) {
-        mAm.setTaskWindowingModeSplitScreenPrimary(taskId, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
-                true /* onTop */, false /* animate */, null /* initialBounds */,
-                true /* showRecents */);
+    public void moveTaskToPrimarySplitScreen(int taskId, boolean showRecents) {
+        mAtm.setTaskWindowingModeSplitScreenPrimary(taskId, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
+                true /* onTop */, false /* animate */, null /* initialBounds */, showRecents);
         mAmWmState.waitForRecentsActivityVisible();
 
-        if (mAmWmState.getAmState().isHomeRecentsComponent() && launchSideActivityIfNeeded) {
+        if (mAmWmState.getAmState().isHomeRecentsComponent() && showRecents) {
             // Launch Placeholder Recents
             final Activity recentsActivity = mSideActivityRule.launchActivity(new Intent());
             mAmWmState.waitForActivityState(recentsActivity.getComponentName(), STATE_RESUMED);
@@ -438,7 +485,7 @@
     protected void setActivityTaskWindowingMode(ComponentName activityName, int windowingMode) {
         mAmWmState.computeState(activityName);
         final int taskId = mAmWmState.getAmState().getTaskByActivity(activityName).mTaskId;
-        mAm.setTaskWindowingMode(taskId, windowingMode, true /* toTop */);
+        mAtm.setTaskWindowingMode(taskId, windowingMode, true /* toTop */);
         mAmWmState.waitForValidState(new WaitForValidActivityState.Builder(activityName)
                 .setActivityType(ACTIVITY_TYPE_STANDARD)
                 .setWindowingMode(windowingMode)
@@ -557,7 +604,7 @@
     }
 
     protected boolean supportsSplitScreenMultiWindow() {
-        return ActivityManager.supportsSplitScreenMultiWindow(mContext);
+        return ActivityTaskManager.supportsSplitScreenMultiWindow(mContext);
     }
 
     protected boolean hasHomeScreen() {
@@ -698,13 +745,17 @@
             return this;
         }
 
-        public LockScreenSession gotoKeyguard() {
+        public LockScreenSession gotoKeyguard(ComponentName... showWhenLockedActivities) {
             if (DEBUG && isLockDisabled()) {
                 logE("LockScreenSession.gotoKeyguard() is called without lock enabled.");
             }
             sleepDevice();
             wakeUpDevice();
-            mAmWmState.waitForKeyguardShowingAndNotOccluded();
+            if (showWhenLockedActivities.length == 0) {
+                mAmWmState.waitForKeyguardShowingAndNotOccluded();
+            } else {
+                mAmWmState.waitForValidState(showWhenLockedActivities);
+            }
             return this;
         }
 
@@ -717,7 +768,15 @@
 
             // Dismiss active keyguard after credential is cleared, so keyguard doesn't ask for
             // the stale credential.
+            // TODO (b/112015010) If keyguard is occluded, credential cannot be removed as expected.
+            // LockScreenSession#close is always calls before stop all test activities,
+            // which could cause keyguard stay at occluded after wakeup.
+            // If Keyguard is occluded, press back key can close ShowWhenLocked activity.
             pressBackButton();
+
+            // If device is unlocked, there might have ShowWhenLocked activity runs on,
+            // use home key to clear all activity at foreground.
+            pressHomeButton();
             sleepDevice();
             wakeUpDevice();
             unlockDevice();
@@ -1230,7 +1289,7 @@
         assertNotNull("The activity should report cutout state", displayCutoutPresent);
 
         // Finish activity
-        executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+        mBroadcastActionTrigger.finishBroadcastReceiverActivity();
         mAmWmState.waitForWithAmState(
                 (state) -> !state.containsActivity(BROADCAST_RECEIVER_ACTIVITY),
                 "Waiting for activity to be removed");
@@ -1412,6 +1471,7 @@
         private boolean mNewTask;
         private boolean mMultipleTask;
         private int mDisplayId = INVALID_DISPLAY;
+        private int mActivityType = ACTIVITY_TYPE_UNDEFINED;
         // A proxy activity that launches other activities including mTargetActivityName
         private ComponentName mLaunchingActivity = LAUNCHING_ACTIVITY;
         private boolean mReorderToFront;
@@ -1480,6 +1540,11 @@
             return this;
         }
 
+        public LaunchActivityBuilder setActivityType(int type) {
+            mActivityType = type;
+            return this;
+        }
+
         public LaunchActivityBuilder setLaunchingActivity(ComponentName launchingActivity) {
             mLaunchingActivity = launchingActivity;
             mLauncherType = LauncherType.LAUNCHING_ACTIVITY;
@@ -1540,6 +1605,7 @@
             b.putBoolean(KEY_MULTIPLE_TASK, mMultipleTask);
             b.putBoolean(KEY_REORDER_TO_FRONT, mReorderToFront);
             b.putInt(KEY_DISPLAY_ID, mDisplayId);
+            b.putInt(KEY_ACTIVITY_TYPE, mActivityType);
             b.putBoolean(KEY_USE_APPLICATION_CONTEXT, mUseApplicationContext);
             b.putString(KEY_TARGET_COMPONENT, getActivityName(mTargetActivity));
             b.putBoolean(KEY_SUPPRESS_EXCEPTIONS, mSuppressExceptions);
@@ -1583,6 +1649,9 @@
             if (mDisplayId != INVALID_DISPLAY) {
                 commandBuilder.append(" --ei " + KEY_DISPLAY_ID + " ").append(mDisplayId);
             }
+            if (mActivityType != ACTIVITY_TYPE_UNDEFINED) {
+                commandBuilder.append(" --ei " + KEY_ACTIVITY_TYPE + " ").append(mActivityType);
+            }
 
             if (mUseApplicationContext) {
                 commandBuilder.append(" --ez " + KEY_USE_APPLICATION_CONTEXT + " true");
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/WaitForValidActivityState.java b/tests/framework/base/activitymanager/util/src/android/server/am/WaitForValidActivityState.java
index 2a9b351..17ca60b 100644
--- a/tests/framework/base/activitymanager/util/src/android/server/am/WaitForValidActivityState.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/WaitForValidActivityState.java
@@ -16,7 +16,7 @@
 
 package android.server.am;
 
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/WindowManagerState.java b/tests/framework/base/activitymanager/util/src/android/server/am/WindowManagerState.java
index 3d30f66..b987a4d 100644
--- a/tests/framework/base/activitymanager/util/src/android/server/am/WindowManagerState.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/WindowManagerState.java
@@ -34,6 +34,7 @@
 import androidx.annotation.Nullable;
 import android.support.test.InstrumentationRegistry;
 import android.view.nano.DisplayInfoProto;
+import android.view.nano.ViewProtoEnums;
 
 import com.android.server.wm.nano.AppTransitionProto;
 import com.android.server.wm.nano.AppWindowTokenProto;
@@ -45,6 +46,7 @@
 import com.android.server.wm.nano.StackProto;
 import com.android.server.wm.nano.TaskProto;
 import com.android.server.wm.nano.WindowContainerProto;
+import com.android.server.wm.nano.WindowFramesProto;
 import com.android.server.wm.nano.WindowManagerServiceDumpProto;
 import com.android.server.wm.nano.WindowStateAnimatorProto;
 import com.android.server.wm.nano.WindowStateProto;
@@ -255,69 +257,72 @@
 
     static String appTransitionToString(int transition) {
         switch (transition) {
-            case AppTransitionProto.TRANSIT_UNSET: {
+            case ViewProtoEnums.TRANSIT_UNSET: {
                 return "TRANSIT_UNSET";
             }
-            case AppTransitionProto.TRANSIT_NONE: {
+            case ViewProtoEnums.TRANSIT_NONE: {
                 return "TRANSIT_NONE";
             }
-            case AppTransitionProto.TRANSIT_ACTIVITY_OPEN: {
+            case ViewProtoEnums.TRANSIT_ACTIVITY_OPEN: {
                 return TRANSIT_ACTIVITY_OPEN;
             }
-            case AppTransitionProto.TRANSIT_ACTIVITY_CLOSE: {
+            case ViewProtoEnums.TRANSIT_ACTIVITY_CLOSE: {
                 return TRANSIT_ACTIVITY_CLOSE;
             }
-            case AppTransitionProto.TRANSIT_TASK_OPEN: {
+            case ViewProtoEnums.TRANSIT_TASK_OPEN: {
                 return TRANSIT_TASK_OPEN;
             }
-            case AppTransitionProto.TRANSIT_TASK_CLOSE: {
+            case ViewProtoEnums.TRANSIT_TASK_CLOSE: {
                 return TRANSIT_TASK_CLOSE;
             }
-            case AppTransitionProto.TRANSIT_TASK_TO_FRONT: {
+            case ViewProtoEnums.TRANSIT_TASK_TO_FRONT: {
                 return "TRANSIT_TASK_TO_FRONT";
             }
-            case AppTransitionProto.TRANSIT_TASK_TO_BACK: {
+            case ViewProtoEnums.TRANSIT_TASK_TO_BACK: {
                 return "TRANSIT_TASK_TO_BACK";
             }
-            case AppTransitionProto.TRANSIT_WALLPAPER_CLOSE: {
+            case ViewProtoEnums.TRANSIT_WALLPAPER_CLOSE: {
                 return TRANSIT_WALLPAPER_CLOSE;
             }
-            case AppTransitionProto.TRANSIT_WALLPAPER_OPEN: {
+            case ViewProtoEnums.TRANSIT_WALLPAPER_OPEN: {
                 return TRANSIT_WALLPAPER_OPEN;
             }
-            case AppTransitionProto.TRANSIT_WALLPAPER_INTRA_OPEN: {
+            case ViewProtoEnums.TRANSIT_WALLPAPER_INTRA_OPEN: {
                 return TRANSIT_WALLPAPER_INTRA_OPEN;
             }
-            case AppTransitionProto.TRANSIT_WALLPAPER_INTRA_CLOSE: {
+            case ViewProtoEnums.TRANSIT_WALLPAPER_INTRA_CLOSE: {
                 return TRANSIT_WALLPAPER_INTRA_CLOSE;
             }
-            case AppTransitionProto.TRANSIT_TASK_OPEN_BEHIND: {
+            case ViewProtoEnums.TRANSIT_TASK_OPEN_BEHIND: {
                 return "TRANSIT_TASK_OPEN_BEHIND";
             }
-            case AppTransitionProto.TRANSIT_ACTIVITY_RELAUNCH: {
+            case ViewProtoEnums.TRANSIT_ACTIVITY_RELAUNCH: {
                 return "TRANSIT_ACTIVITY_RELAUNCH";
             }
-            case AppTransitionProto.TRANSIT_DOCK_TASK_FROM_RECENTS: {
+            case ViewProtoEnums.TRANSIT_DOCK_TASK_FROM_RECENTS: {
                 return "TRANSIT_DOCK_TASK_FROM_RECENTS";
             }
-            case AppTransitionProto.TRANSIT_KEYGUARD_GOING_AWAY: {
+            case ViewProtoEnums.TRANSIT_KEYGUARD_GOING_AWAY: {
                 return TRANSIT_KEYGUARD_GOING_AWAY;
             }
-            case AppTransitionProto.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER: {
+            case ViewProtoEnums.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER: {
                 return TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
             }
-            case AppTransitionProto.TRANSIT_KEYGUARD_OCCLUDE: {
+            case ViewProtoEnums.TRANSIT_KEYGUARD_OCCLUDE: {
                 return TRANSIT_KEYGUARD_OCCLUDE;
             }
-            case AppTransitionProto.TRANSIT_KEYGUARD_UNOCCLUDE: {
+            case ViewProtoEnums.TRANSIT_KEYGUARD_UNOCCLUDE: {
                 return TRANSIT_KEYGUARD_UNOCCLUDE;
             }
-            case AppTransitionProto.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN: {
+            case ViewProtoEnums.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN: {
                 return TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
             }
-            case AppTransitionProto.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE: {
+            case ViewProtoEnums.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE: {
                 return TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
             }
+            case ViewProtoEnums.TRANSIT_CRASHING_ACTIVITY_CLOSE: {
+                return "TRANSIT_CRASHING_ACTIVITY_CLOSE";
+            }
             default: {
                 fail("Invalid lastUsedAppTransition");
                 return null;
@@ -771,10 +776,10 @@
         private int mStackId;
         private int mLayer;
         private boolean mShown;
-        private Rect mContainingFrame = new Rect();
-        private Rect mParentFrame = new Rect();
-        private Rect mContentFrame = new Rect();
-        private Rect mFrame = new Rect();
+        private Rect mContainingFrame;
+        private Rect mParentFrame;
+        private Rect mContentFrame;
+        private Rect mFrame;
         private Rect mSurfaceInsets = new Rect();
         private Rect mContentInsets = new Rect();
         private Rect mGivenContentInsets = new Rect();
@@ -800,10 +805,13 @@
                 mCrop = extract(animatorProto.lastClipRect);
             }
             mGivenContentInsets = extract(proto.givenContentInsets);
-            mFrame = extract(proto.frame);
-            mContainingFrame = extract(proto.containingFrame);
-            mParentFrame = extract(proto.parentFrame);
-            mContentFrame = extract(proto.contentFrame);
+            WindowFramesProto windowFramesProto = proto.windowFrames;
+            if (windowFramesProto != null) {
+                mFrame = extract(windowFramesProto.frame);
+                mContainingFrame = extract(windowFramesProto.containingFrame);
+                mParentFrame = extract(windowFramesProto.parentFrame);
+                mContentFrame = extract(windowFramesProto.contentFrame);
+            }
             mContentInsets = extract(proto.contentInsets);
             mSurfaceInsets = extract(proto.surfaceInsets);
             if (mName.startsWith(STARTING_WINDOW_PREFIX)) {
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
index aee8390..6c20083 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -22,6 +22,7 @@
     <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
     <uses-permission android:name="android.permission.ACTIVITY_EMBEDDING" />
     <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
 
     <application android:label="CtsWindowManagerDeviceTestCases">
         <uses-library android:name="android.test.runner"/>
@@ -35,8 +36,10 @@
         <activity android:name="android.server.wm.AlertWindowsAppOpsTestsActivity"/>
         <activity android:name="android.server.wm.DialogFrameTestActivity" />
         <activity android:name="android.server.wm.DisplayCutoutTests$TestActivity" />
+        <activity android:name="android.server.wm.LayoutTestsActivity"
+                  android:theme="@style/no_animation" />
         <activity android:name="android.server.wm.LocationOnScreenTests$TestActivity"
-            android:theme="@style/no_starting_window" />
+                  android:theme="@style/no_starting_window" />
         <activity android:name="android.server.wm.LocationInWindowTests$TestActivity" />
 
     </application>
diff --git a/tests/framework/base/windowmanager/AndroidTest.xml b/tests/framework/base/windowmanager/AndroidTest.xml
index 337ada0..f89997c 100644
--- a/tests/framework/base/windowmanager/AndroidTest.xml
+++ b/tests/framework/base/windowmanager/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS WindowManager 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="instant_app" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck"/>
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true"/>
diff --git a/tests/framework/base/windowmanager/res/values/styles.xml b/tests/framework/base/windowmanager/res/values/styles.xml
index 4e59d14..e52bdb0 100644
--- a/tests/framework/base/windowmanager/res/values/styles.xml
+++ b/tests/framework/base/windowmanager/res/values/styles.xml
@@ -18,4 +18,8 @@
     <style name="no_starting_window" parent="@android:style/Theme.DeviceDefault">
         <item name="android:windowDisablePreview">true</item>
     </style>
+    <style name="no_animation" parent="@android:style/Theme.DeviceDefault">
+        <item name="android:windowAnimationStyle">@null</item>
+        <item name="android:windowDisablePreview">true</item>
+    </style>
 </resources>
\ No newline at end of file
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsTests.java
index 3ab7c198..4f925e8 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsTests.java
@@ -22,6 +22,12 @@
 import static android.server.wm.alertwindowapp.Components.ALERT_WINDOW_TEST_ACTIVITY;
 import static android.server.wm.alertwindowappsdk25.Components.SDK25_ALERT_WINDOW_TEST_ACTIVITY;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.lessThan;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
@@ -152,13 +158,12 @@
             // in place for SYSTEM_ALERT_WINDOW, which allows the window
             // to be created, but will be hidden instead.
             if (isUiModeLockedToVrHeadset()) {
-                assertTrue("Should not be empty alertWindows=" + alertWindows,
-                        !alertWindows.isEmpty());
+                assertThat("Should not be empty alertWindows",
+                        alertWindows, hasSize(greaterThan(0)));
                 assertTrue("All alert windows should be hidden",
                         allWindowsHidden(alertWindows));
             } else {
-                assertTrue("Should be empty alertWindows=" + alertWindows,
-                        alertWindows.isEmpty());
+                assertThat("Should be empty alertWindows", alertWindows, empty());
                 assertTrue(AppOpsUtils.rejectedOperationLogged(packageName,
                         OPSTR_SYSTEM_ALERT_WINDOW));
                 return;
@@ -168,8 +173,8 @@
         if (atLeastO) {
             // Assert that only TYPE_APPLICATION_OVERLAY was created.
             for (WindowManagerState.WindowState win : alertWindows) {
-                assertTrue("Can't create win=" + win + " on SDK O or greater",
-                        win.getType() == TYPE_APPLICATION_OVERLAY);
+                assertEquals("Can't create win=" + win + " on SDK O or greater",
+                        win.getType(), TYPE_APPLICATION_OVERLAY);
             }
         }
 
@@ -183,17 +188,17 @@
                 alertWindows.get(alertWindows.size() - 1);
 
         // Assert that the alert windows have higher z-order than the main app window
-        assertTrue("lowestAlertWindow=" + lowestAlertWindow + " less than mainAppWindow="
-                + mainAppWindow,
-                wmState.getZOrder(lowestAlertWindow) > wmState.getZOrder(mainAppWindow));
+        assertThat("lowestAlertWindow has higher z-order than mainAppWindow",
+                wmState.getZOrder(lowestAlertWindow),
+                greaterThan(wmState.getZOrder(mainAppWindow)));
 
         // Assert that legacy alert windows have a lower z-order than the new alert window layer.
         final WindowManagerState.WindowState appOverlayWindow =
                 wmState.getWindowByPackageName(packageName, TYPE_APPLICATION_OVERLAY);
         if (appOverlayWindow != null && highestAlertWindow != appOverlayWindow) {
-            assertTrue("highestAlertWindow=" + highestAlertWindow
-                            + " greater than appOverlayWindow=" + appOverlayWindow,
-                    wmState.getZOrder(highestAlertWindow) < wmState.getZOrder(appOverlayWindow));
+            assertThat("highestAlertWindow has lower z-order than appOverlayWindow",
+                    wmState.getZOrder(highestAlertWindow),
+                    lessThan(wmState.getZOrder(appOverlayWindow)));
         }
 
         // Assert that alert windows are below key system windows.
@@ -201,9 +206,9 @@
                 wmState.getWindowsByPackageName(packageName, SYSTEM_WINDOW_TYPES);
         if (!systemWindows.isEmpty()) {
             final WindowManagerState.WindowState lowestSystemWindow = alertWindows.get(0);
-            assertTrue("highestAlertWindow=" + highestAlertWindow
-                            + " greater than lowestSystemWindow=" + lowestSystemWindow,
-                    wmState.getZOrder(highestAlertWindow) < wmState.getZOrder(lowestSystemWindow));
+            assertThat("highestAlertWindow has lower z-order than lowestSystemWindow",
+                    wmState.getZOrder(highestAlertWindow),
+                    lessThan(wmState.getZOrder(lowestSystemWindow)));
         }
         assertTrue(AppOpsUtils.allowedOperationLogged(packageName, OPSTR_SYSTEM_ALERT_WINDOW));
     }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java b/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
index 5d26234..e3007fd 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
@@ -34,6 +34,7 @@
 import static org.junit.Assume.assumeTrue;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.content.Context;
 import android.graphics.Point;
 import android.os.RemoteException;
@@ -41,6 +42,7 @@
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.FlakyTest;
 import android.util.Log;
 
 import org.junit.After;
@@ -56,6 +58,7 @@
  */
 @Presubmit
 @AppModeFull(reason = "Requires android.permission.MANAGE_ACTIVITY_STACKS")
+@FlakyTest(bugId = 109874623)
 public class CrossAppDragAndDropTests {
     private static final String TAG = "CrossAppDragAndDrop";
 
@@ -120,6 +123,7 @@
 
     protected Context mContext;
     protected ActivityManager mAm;
+    protected ActivityTaskManager mAtm;
 
     private Map<String, String> mSourceResults;
     private Map<String, String> mTargetResults;
@@ -142,6 +146,7 @@
 
         mContext = InstrumentationRegistry.getContext();
         mAm = mContext.getSystemService(ActivityManager.class);
+        mAtm = mContext.getSystemService(ActivityTaskManager.class);
 
         mSourcePackageName = SOURCE_PACKAGE_NAME;
         mTargetPackageName = TARGET_PACKAGE_NAME;
@@ -192,7 +197,7 @@
         clearLogs();
 
         // Remove special stacks.
-        mAm.removeStacksInWindowingModes(new int[] {
+        mAtm.removeStacksInWindowingModes(new int[] {
                 WINDOWING_MODE_PINNED,
                 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
                 WINDOWING_MODE_FREEFORM
@@ -427,11 +432,11 @@
     }
 
     private static boolean supportsDragAndDrop() {
-        return ActivityManager.supportsMultiWindow(InstrumentationRegistry.getContext());
+        return ActivityTaskManager.supportsMultiWindow(InstrumentationRegistry.getContext());
     }
 
     private static boolean supportsSplitScreenMultiWindow() {
-        return ActivityManager.supportsSplitScreenMultiWindow(InstrumentationRegistry.getContext());
+        return ActivityTaskManager.supportsSplitScreenMultiWindow(InstrumentationRegistry.getContext());
     }
 
     private static boolean supportsFreeformMultiWindow() {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/LayoutTests.java b/tests/framework/base/windowmanager/src/android/server/wm/LayoutTests.java
new file mode 100644
index 0000000..0d8373c
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/LayoutTests.java
@@ -0,0 +1,155 @@
+/*
+ * 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.server.wm;
+
+import android.app.Instrumentation;
+import android.content.ContentResolver;
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.FlakyTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static android.provider.Settings.Global.WINDOW_ANIMATION_SCALE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertFalse;
+
+/**
+ * Test whether WindowManager performs the correct layout after we make some changes to it.
+ *
+ * Build/Install/Run:
+ *     atest CtsWindowManagerDeviceTestCases:LayoutTests
+ */
+@Presubmit
+@FlakyTest(detail = "Can be promoted to pre-submit once confirmed stable.")
+@RunWith(AndroidJUnit4.class)
+public class LayoutTests {
+    private final long TIMEOUT_LAYOUT = 100; // milliseconds
+    private final long TIMEOUT_REMOVE_WINDOW = 500;
+    private final long TIMEOUT_SYSTEM_UI_VISIBILITY_CHANGE = 1000;
+    private final int SYSTEM_UI_FLAG_HIDE_ALL = View.SYSTEM_UI_FLAG_FULLSCREEN
+            | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+
+    private Instrumentation mInstrumentation;
+    private LayoutTestsActivity mActivity;
+    private ContentResolver mResolver;
+    private float mWindowAnimationScale;
+    private boolean mSystemUiFlagsGotCleared = false;
+
+    @Rule
+    public final ActivityTestRule<LayoutTestsActivity> mActivityRule =
+            new ActivityTestRule<>(LayoutTestsActivity.class);
+
+    @Before
+    public void setup() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mResolver = mInstrumentation.getContext().getContentResolver();
+
+        // The layout will be performed at the end of the animation of hiding status/navigation bar,
+        // which will recover the possible issue, so we disable the animation during the test.
+        mWindowAnimationScale = Settings.Global.getFloat(mResolver, WINDOW_ANIMATION_SCALE, 1f);
+        Settings.Global.putFloat(mResolver, WINDOW_ANIMATION_SCALE, 0);
+    }
+
+    @After
+    public void tearDown() {
+        // Restore the animation we disabled previously.
+        Settings.Global.putFloat(mResolver, WINDOW_ANIMATION_SCALE, mWindowAnimationScale);
+    }
+
+    @Test
+    public void testLayoutAfterRemovingFocus() throws InterruptedException {
+        mActivity = mActivityRule.getActivity();
+        assertNotNull(mActivity);
+
+        // Get the visible frame of the main activity before adding any window.
+        Rect visibleFrame = new Rect();
+        mActivity.getWindowVisibleDisplayFrame(visibleFrame);
+
+        doTestLayoutAfterRemovingFocus(visibleFrame, mActivity::addWindowHidingStatusBar);
+        doTestLayoutAfterRemovingFocus(visibleFrame, mActivity::addWindowHidingNavigationBar);
+        doTestLayoutAfterRemovingFocus(visibleFrame, mActivity::addWindowHidingBothSystemBars);
+    }
+
+    private void doTestLayoutAfterRemovingFocus(Rect visibleFrameBeforeAddingWindow,
+            Runnable toAddWindow) throws InterruptedException {
+        // Add a window which can affect the global layout.
+        mInstrumentation.runOnMainSync(toAddWindow);
+
+        // Wait a bit for the layout finish triggered by adding window.
+        SystemClock.sleep(TIMEOUT_LAYOUT);
+
+        // Remove the window we added previously.
+        mInstrumentation.runOnMainSync(mActivity::removeWindow);
+        mInstrumentation.runOnMainSync(this::stopWaiting);
+        synchronized (this) {
+            wait(TIMEOUT_REMOVE_WINDOW);
+        }
+
+        // Get the visible frame of the main activity after removing the window we added.
+        Rect visibleFrameAfterRemovingWindow = new Rect();
+        mActivity.getWindowVisibleDisplayFrame(visibleFrameAfterRemovingWindow);
+
+        // Test whether the visible frame after removing window is the same as one before adding
+        // window. If not, it shows that the layout after removing window has a problem.
+        assertEquals(visibleFrameBeforeAddingWindow, visibleFrameAfterRemovingWindow);
+    }
+
+    private void stopWaiting() {
+        synchronized (this) {
+            notify();
+        }
+    }
+
+    @Test
+    public void testAddingImmersiveWindow() throws InterruptedException {
+        mActivity = mActivityRule.getActivity();
+        assertNotNull(mActivity);
+
+        mInstrumentation.runOnMainSync(this::addImmersiveWindow);
+        synchronized (this) {
+            wait(TIMEOUT_SYSTEM_UI_VISIBILITY_CHANGE);
+        }
+        assertFalse("System UI flags should not be cleared.", mSystemUiFlagsGotCleared);
+    }
+
+    private void addImmersiveWindow() {
+        View view = new View(mActivity);
+        view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | SYSTEM_UI_FLAG_HIDE_ALL);
+        view.setOnSystemUiVisibilityChangeListener(
+                visibility -> {
+                    if ((visibility & SYSTEM_UI_FLAG_HIDE_ALL) != SYSTEM_UI_FLAG_HIDE_ALL) {
+                        mSystemUiFlagsGotCleared = true;
+                        // Early break because things go wrong already
+                        stopWaiting();
+                    }
+                });
+        mActivity.addWindow(view);
+    }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/LayoutTestsActivity.java b/tests/framework/base/windowmanager/src/android/server/wm/LayoutTestsActivity.java
new file mode 100644
index 0000000..d58a72d
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/LayoutTestsActivity.java
@@ -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 android.server.wm;
+
+import android.app.Activity;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.WindowManager.LayoutParams;
+
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+
+public class LayoutTestsActivity extends Activity {
+    private View mChild;
+
+    public void addWindowHidingStatusBar() {
+        addWindow(View.SYSTEM_UI_FLAG_FULLSCREEN);
+    }
+
+    public void addWindowHidingNavigationBar() {
+        addWindow(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
+    }
+
+    public void addWindowHidingBothSystemBars() {
+        addWindow(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
+    }
+
+    private void addWindow(int systemUiVisibility) {
+        mChild = new View(this);
+        mChild.setSystemUiVisibility(systemUiVisibility);
+        getWindowManager().addView(mChild, new LayoutParams(TYPE_APPLICATION_PANEL));
+    }
+
+    public void removeWindow() {
+        getWindowManager().removeViewImmediate(mChild);
+    }
+
+    public void getWindowVisibleDisplayFrame(Rect outRect) {
+        getWindow().getDecorView().getWindowVisibleDisplayFrame(outRect);
+    }
+
+    public void addWindow(View view) {
+        getWindowManager().addView(view, new LayoutParams(TYPE_APPLICATION_PANEL));
+    }
+}
diff --git a/tests/inputmethod/AndroidTest.xml b/tests/inputmethod/AndroidTest.xml
index f749065..a1811bc 100644
--- a/tests/inputmethod/AndroidTest.xml
+++ b/tests/inputmethod/AndroidTest.xml
@@ -18,6 +18,7 @@
 <configuration description="Config for CTS InputMethod test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="inputmethod" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <!--
diff --git a/tests/leanbackjank/app/Android.mk b/tests/leanbackjank/app/Android.mk
index b6404fe..f23d99c 100644
--- a/tests/leanbackjank/app/Android.mk
+++ b/tests/leanbackjank/app/Android.mk
@@ -24,6 +24,7 @@
 
 LOCAL_PACKAGE_NAME := CtsLeanbackJankApp
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 21
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/tests/libcore/wycheproof-bc/AndroidTest.xml b/tests/libcore/wycheproof-bc/AndroidTest.xml
index 2e92706..8fc584f 100644
--- a/tests/libcore/wycheproof-bc/AndroidTest.xml
+++ b/tests/libcore/wycheproof-bc/AndroidTest.xml
@@ -32,5 +32,6 @@
         <option name="core-expectation" value="/knownfailures.txt" />
         <option name="runtime-hint" value="10m"/>
         <option name="test-timeout" value="600000" />
+        <option name="hidden-api-checks" value="false" />
     </test>
 </configuration>
diff --git a/tests/libcore/wycheproof/AndroidTest.xml b/tests/libcore/wycheproof/AndroidTest.xml
index 2a1f2b5..0119e6b 100644
--- a/tests/libcore/wycheproof/AndroidTest.xml
+++ b/tests/libcore/wycheproof/AndroidTest.xml
@@ -31,5 +31,6 @@
                 value="com.android.cts.core.runner.ExpectationBasedFilter" />
         <option name="core-expectation" value="/knownfailures.txt" />
         <option name="runtime-hint" value="10m"/>
+        <option name="hidden-api-checks" value="false" />
     </test>
 </configuration>
diff --git a/tests/mocking/Android.mk b/tests/mocking/Android.mk
index 2afa4d2..13f74fe 100644
--- a/tests/mocking/Android.mk
+++ b/tests/mocking/Android.mk
@@ -23,10 +23,8 @@
 LOCAL_STATIC_JAVA_LIBRARIES = \
     mockito-target \
     android-support-test \
-    ctstestrunner
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, \
-            ../../../external/dexmaker/dexmaker-mockito-tests/src/androidTest/java)
+    ctstestrunner \
+    dexmaker-mockmaker-tests
 LOCAL_COMPATIBILITY_SUITE := \
     cts vts general-tests
 LOCAL_PACKAGE_NAME := \
diff --git a/tests/mocking/AndroidTest.xml b/tests/mocking/AndroidTest.xml
index 9e53939..c6e4ffe 100644
--- a/tests/mocking/AndroidTest.xml
+++ b/tests/mocking/AndroidTest.xml
@@ -25,6 +25,6 @@
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.mocking.cts" />
-        <option name="runtime-hint" value="5s" />
+        <option name="runtime-hint" value="120s" />
     </test>
 </configuration>
diff --git a/tests/mocking/debuggable/Android.mk b/tests/mocking/debuggable/Android.mk
index 5187811..e72f69f 100644
--- a/tests/mocking/debuggable/Android.mk
+++ b/tests/mocking/debuggable/Android.mk
@@ -23,10 +23,8 @@
 LOCAL_STATIC_JAVA_LIBRARIES = \
     mockito-target \
     android-support-test \
-    ctstestrunner
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, \
-            ../../../../external/dexmaker/dexmaker-mockito-tests/src/androidTest/java)
+    ctstestrunner \
+    dexmaker-mockmaker-tests
 LOCAL_COMPATIBILITY_SUITE := \
     cts vts general-tests
 LOCAL_PACKAGE_NAME := \
diff --git a/tests/mocking/debuggable/AndroidTest.xml b/tests/mocking/debuggable/AndroidTest.xml
index 135de46..645f2a2 100644
--- a/tests/mocking/debuggable/AndroidTest.xml
+++ b/tests/mocking/debuggable/AndroidTest.xml
@@ -26,6 +26,6 @@
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.mocking.cts.debuggable" />
-        <option name="runtime-hint" value="5s" />
+        <option name="runtime-hint" value="120s" />
     </test>
 </configuration>
diff --git a/tests/mocking/extended/Android.mk b/tests/mocking/extended/Android.mk
new file mode 100644
index 0000000..3fee499
--- /dev/null
+++ b/tests/mocking/extended/Android.mk
@@ -0,0 +1,44 @@
+# 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 := \
+    tests
+LOCAL_JAVA_LIBRARIES := \
+    android.test.runner.stubs
+LOCAL_STATIC_JAVA_LIBRARIES = \
+    mockito-target-extended \
+    android-support-test \
+    ctstestrunner \
+    dexmaker-mockmaker-tests \
+    dexmaker-inline-mockmaker-tests \
+    dexmaker-extended-mockmaker-tests \
+    android-support-v4
+LOCAL_MULTILIB := \
+    both
+LOCAL_JNI_SHARED_LIBRARIES := \
+    libdexmakerjvmtiagent \
+    libmultiplejvmtiagentsinterferenceagent \
+    libstaticjvmtiagent
+LOCAL_COMPATIBILITY_SUITE := \
+    cts vts general-tests
+LOCAL_PACKAGE_NAME := \
+    CtsExtendedMockingTestCases
+LOCAL_SDK_VERSION := \
+    current
+include $(BUILD_CTS_PACKAGE)
+
diff --git a/tests/mocking/extended/AndroidManifest.xml b/tests/mocking/extended/AndroidManifest.xml
new file mode 100644
index 0000000..f62d95f
--- /dev/null
+++ b/tests/mocking/extended/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.extended.mocking.cts">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <application android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name="com.android.dx.mockito.inline.extended.tests.EmptyActivity" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.extended.mocking.cts"
+                     android:label="CTS tests for mockito extended mocking">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+
+</manifest>
+
diff --git a/tests/mocking/extended/AndroidTest.xml b/tests/mocking/extended/AndroidTest.xml
new file mode 100644
index 0000000..5fcd077
--- /dev/null
+++ b/tests/mocking/extended/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?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.
+  -->
+<configuration description="Config for Mockito extended mocking test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="mocking" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsExtendedMockingTestCases.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.extended.mocking.cts" />
+        <option name="runtime-hint" value="120s" />
+    </test>
+
+    <!-- Controller that will skip the module if a native bridge situation is detected -->
+    <!-- For example: module wants to run arm32 and device is x86 -->
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.NativeBridgeModuleController" />
+</configuration>
diff --git a/tests/mocking/inline/Android.mk b/tests/mocking/inline/Android.mk
index 9fa873d..67b4fb4 100644
--- a/tests/mocking/inline/Android.mk
+++ b/tests/mocking/inline/Android.mk
@@ -23,15 +23,15 @@
 LOCAL_STATIC_JAVA_LIBRARIES = \
     mockito-target-inline \
     android-support-test \
-    ctstestrunner
+    ctstestrunner \
+    dexmaker-mockmaker-tests \
+    dexmaker-inline-mockmaker-tests \
+    android-support-v4
 LOCAL_MULTILIB := \
     both
 LOCAL_JNI_SHARED_LIBRARIES := \
     libdexmakerjvmtiagent \
     libmultiplejvmtiagentsinterferenceagent
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, \
-            ../../../../external/dexmaker/dexmaker-mockito-inline-tests/src/androidTest/java)
 LOCAL_COMPATIBILITY_SUITE := \
     cts vts general-tests
 LOCAL_PACKAGE_NAME := \
diff --git a/tests/mocking/inline/AndroidTest.xml b/tests/mocking/inline/AndroidTest.xml
index 7158dcb..f7b51fb 100644
--- a/tests/mocking/inline/AndroidTest.xml
+++ b/tests/mocking/inline/AndroidTest.xml
@@ -25,7 +25,7 @@
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.inline.mocking.cts" />
-        <option name="runtime-hint" value="30s" />
+        <option name="runtime-hint" value="120s" />
     </test>
 
     <!-- Controller that will skip the module if a native bridge situation is detected -->
diff --git a/tests/openglperf2/Android.mk b/tests/openglperf2/Android.mk
index 4f402ca..c335854 100644
--- a/tests/openglperf2/Android.mk
+++ b/tests/openglperf2/Android.mk
@@ -33,6 +33,7 @@
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
 LOCAL_SDK_VERSION := 16
+#LOCAL_MIN_SDK_VERSION := 16
 
 include $(BUILD_CTS_PACKAGE)
 
diff --git a/tests/pdf/AndroidTest.xml b/tests/pdf/AndroidTest.xml
index a402e10..475e555 100644
--- a/tests/pdf/AndroidTest.xml
+++ b/tests/pdf/AndroidTest.xml
@@ -19,6 +19,7 @@
 
     <!-- Printing is a large user of PDF, hence run the tests in the same component -->
     <option name="config-descriptor:metadata" key="component" value="print" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/sample/AndroidTest.xml b/tests/sample/AndroidTest.xml
index 8a7d2d9..c398ab05 100644
--- a/tests/sample/AndroidTest.xml
+++ b/tests/sample/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS Sample test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="misc" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsSampleDeviceTestCases.apk" />
diff --git a/tests/signature/api-check/android-test-base-27-api/Android.mk b/tests/signature/api-check/android-test-base-27-api/Android.mk
index 1adcb12c..dc8dea0 100644
--- a/tests/signature/api-check/android-test-base-27-api/Android.mk
+++ b/tests/signature/api-check/android-test-base-27-api/Android.mk
@@ -21,4 +21,6 @@
 LOCAL_SIGNATURE_API_FILES := \
     android-test-base-current.api \
 
+LOCAL_MIN_SDK_VERSION := 25
+
 include $(LOCAL_PATH)/../build_signature_apk.mk
diff --git a/tests/signature/api-check/apache-http-legacy-27-api/Android.mk b/tests/signature/api-check/apache-http-legacy-27-api/Android.mk
index 0f2161a..4e17901 100644
--- a/tests/signature/api-check/apache-http-legacy-27-api/Android.mk
+++ b/tests/signature/api-check/apache-http-legacy-27-api/Android.mk
@@ -22,4 +22,6 @@
     current.api \
     apache-http-legacy-minus-current.api \
 
+LOCAL_MIN_SDK_VERSION := 22
+
 include $(LOCAL_PATH)/../build_signature_apk.mk
diff --git a/tests/tests/accounts/AndroidTest.xml b/tests/tests/accounts/AndroidTest.xml
index e0a3ae9..7ca519c 100644
--- a/tests/tests/accounts/AndroidTest.xml
+++ b/tests/tests/accounts/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS Accounts 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="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="cmd account set-bind-instant-service-allowed true" />
         <option name="teardown-command" value="cmd account set-bind-instant-service-allowed false" />
diff --git a/tests/tests/animation/AndroidTest.xml b/tests/tests/animation/AndroidTest.xml
index 9453183..13b2a02 100644
--- a/tests/tests/animation/AndroidTest.xml
+++ b/tests/tests/animation/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS Animation test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="uitoolkit" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsAnimationTestCases.apk" />
diff --git a/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java b/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
index 8a5e974..d4f844f 100644
--- a/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
+++ b/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
@@ -368,6 +368,33 @@
     }
 
     @Test
+    public void testSeekAfterPause() throws Throwable {
+        final AnimatorSet set = new AnimatorSet();
+        ValueAnimator a1 = ValueAnimator.ofFloat(0f, 50f);
+        a1.setDuration(50);
+        ValueAnimator a2 = ValueAnimator.ofFloat(50, 100f);
+        a2.setDuration(50);
+        set.playSequentially(a1, a2);
+        set.setInterpolator(new LinearInterpolator());
+
+        mActivityRule.runOnUiThread(() -> {
+            set.start();
+            set.pause();
+            set.setCurrentPlayTime(60);
+            assertEquals((long) set.getCurrentPlayTime(), 60);
+            assertEquals((float) a1.getAnimatedValue(), 50f, EPSILON);
+            assertEquals((float) a2.getAnimatedValue(), 60f, EPSILON);
+
+            set.setCurrentPlayTime(40);
+            assertEquals((long) set.getCurrentPlayTime(), 40);
+            assertEquals((float) a1.getAnimatedValue(), 40f, EPSILON);
+            assertEquals((float) a2.getAnimatedValue(), 50f, EPSILON);
+
+            set.cancel();
+        });
+    }
+
+    @Test
     public void testDuration() throws Throwable {
         xAnimator.setRepeatCount(ValueAnimator.INFINITE);
         Animator[] animatorArray = { xAnimator, yAnimator };
diff --git a/tests/tests/app/AndroidTest.xml b/tests/tests/app/AndroidTest.xml
index 9f74210..adebf03 100644
--- a/tests/tests/app/AndroidTest.xml
+++ b/tests/tests/app/AndroidTest.xml
@@ -24,6 +24,5 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.app.cts" />
         <option name="runtime-hint" value="1s" />
-        <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/tests/tests/appwidget/AndroidManifest.xml b/tests/tests/appwidget/AndroidManifest.xml
index 46daf85..d2157fe 100644
--- a/tests/tests/appwidget/AndroidManifest.xml
+++ b/tests/tests/appwidget/AndroidManifest.xml
@@ -63,10 +63,25 @@
               android:resource="@xml/appwidget_info_with_feature3" />
       </receiver>
 
+      <receiver android:name="android.appwidget.cts.provider.CollectionAppWidgetProvider" >
+          <intent-filter>
+              <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+          </intent-filter>
+          <meta-data android:name="android.appwidget.provider"
+              android:resource="@xml/collection_appwidget_info" />
+      </receiver>
+
       <service android:name="android.appwidget.cts.service.MyAppWidgetService"
           android:permission="android.permission.BIND_REMOTEVIEWS">
       </service>
 
+      <activity android:name="android.appwidget.cts.EmptyActivity"
+                android:label="EmptyActivity">
+          <intent-filter>
+              <action android:name="android.intent.action.MAIN" />
+              <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+          </intent-filter>
+      </activity>
   </application>
 
   <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/appwidget/AndroidTest.xml b/tests/tests/appwidget/AndroidTest.xml
index e64123c..4b19277 100644
--- a/tests/tests/appwidget/AndroidTest.xml
+++ b/tests/tests/appwidget/AndroidTest.xml
@@ -15,6 +15,7 @@
 <configuration description="Config for CTS App Widget 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="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsAppWidgetLauncher1.apk" />
diff --git a/tests/tests/widget/res/layout/remoteviews_adapter.xml b/tests/tests/appwidget/res/layout/remoteviews_adapter.xml
similarity index 99%
rename from tests/tests/widget/res/layout/remoteviews_adapter.xml
rename to tests/tests/appwidget/res/layout/remoteviews_adapter.xml
index 59115da..1cd50c3 100644
--- a/tests/tests/widget/res/layout/remoteviews_adapter.xml
+++ b/tests/tests/appwidget/res/layout/remoteviews_adapter.xml
@@ -35,5 +35,3 @@
         android:layout_height="match_parent"
         android:visibility="gone" />
 </FrameLayout>
-
-
diff --git a/tests/tests/widget/res/layout/remoteviews_adapter_item.xml b/tests/tests/appwidget/res/layout/remoteviews_adapter_item.xml
similarity index 100%
rename from tests/tests/widget/res/layout/remoteviews_adapter_item.xml
rename to tests/tests/appwidget/res/layout/remoteviews_adapter_item.xml
diff --git a/tests/tests/widget/res/xml/remoteviews_appwidget_info.xml b/tests/tests/appwidget/res/xml/collection_appwidget_info.xml
similarity index 91%
rename from tests/tests/widget/res/xml/remoteviews_appwidget_info.xml
rename to tests/tests/appwidget/res/xml/collection_appwidget_info.xml
index e75ed72..9e4baa0 100644
--- a/tests/tests/widget/res/xml/remoteviews_appwidget_info.xml
+++ b/tests/tests/appwidget/res/xml/collection_appwidget_info.xml
@@ -24,6 +24,5 @@
     android:updatePeriodMillis="86400000"
     android:initialLayout="@layout/remoteviews_adapter"
     android:resizeMode="horizontal|vertical"
-    android:widgetCategory="home_screen|keyguard"
-    android:previewImage="@drawable/icon_red">
+    android:widgetCategory="home_screen|keyguard">
 </appwidget-provider>
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
index cbf0a2f..2ddd5bc 100644
--- a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
@@ -89,12 +89,6 @@
                 .getTargetContext().getCacheDir().getPath());
     }
 
-    private static final String GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND =
-            "appwidget grantbind --package android.appwidget.cts --user 0";
-
-    private static final String REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND =
-            "appwidget revokebind --package android.appwidget.cts --user 0";
-
     @AppModeInstant(reason = "Instant apps cannot provide or host app widgets")
     @Test
     public void testInstantAppsCannotProvideAppWidgets() {
@@ -1343,14 +1337,6 @@
         assertTrue(verifiedWidgets[1]);
     }
 
-    private void grantBindAppWidgetPermission() throws Exception {
-        runShellCommand(GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND);
-    }
-
-    private void revokeBindAppWidgetPermission() throws Exception {
-        runShellCommand(REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND);
-    }
-
     private AppWidgetProviderInfo getFirstAppWidgetProviderInfo() {
         return getProviderInfo(getFirstWidgetComponent());
     }
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTestCase.java b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTestCase.java
index 3352d12..7e795a7 100644
--- a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTestCase.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTestCase.java
@@ -46,6 +46,12 @@
     private static final String SECOND_APP_WIDGET_CONFIGURE_ACTIVITY =
             "android.appwidget.cts.provider.SecondAppWidgetConfigureActivity";
 
+    private static final String GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND =
+            "appwidget grantbind --package android.appwidget.cts --user 0";
+
+    private static final String REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND =
+            "appwidget revokebind --package android.appwidget.cts --user 0";
+
     @Before
     public void assumeHasWidgets() {
         assumeTrue(hasAppWidgets());
@@ -171,4 +177,12 @@
         }
         return ret;
     }
+
+    protected void grantBindAppWidgetPermission() throws Exception {
+        runShellCommand(GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND);
+    }
+
+    protected void revokeBindAppWidgetPermission() throws Exception {
+        runShellCommand(REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND);
+    }
 }
diff --git a/tests/tests/widget/src/android/widget/cts/RemoteViewsWidgetTest.java b/tests/tests/appwidget/src/android/appwidget/cts/CollectionAppWidgetTest.java
similarity index 78%
rename from tests/tests/widget/src/android/widget/cts/RemoteViewsWidgetTest.java
rename to tests/tests/appwidget/src/android/appwidget/cts/CollectionAppWidgetTest.java
index 0e05a0d..dcee24d 100644
--- a/tests/tests/widget/src/android/widget/cts/RemoteViewsWidgetTest.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/CollectionAppWidgetTest.java
@@ -13,28 +13,29 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package android.widget.cts;
+package android.appwidget.cts;
 
 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 static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-import android.app.Activity;
 import android.app.Instrumentation;
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
+import android.appwidget.cts.provider.CollectionAppWidgetProvider;
+import android.appwidget.cts.service.MyAppWidgetService;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
+import android.content.IntentFilter;
 import android.os.Bundle;
 import android.os.Process;
 import android.platform.test.annotations.AppModeFull;
@@ -43,18 +44,13 @@
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 import android.view.View;
-import android.view.ViewGroup;
 import android.widget.AbsListView;
-import android.widget.FrameLayout;
 import android.widget.ListView;
 import android.widget.RemoteViews;
 import android.widget.RemoteViewsService;
 import android.widget.StackView;
-import android.widget.cts.appwidget.MyAppWidgetProvider;
-import android.widget.cts.appwidget.MyAppWidgetService;
 
 import com.android.compatibility.common.util.PollingCheck;
-import com.android.compatibility.common.util.SystemUtil;
 
 import org.junit.After;
 import org.junit.Before;
@@ -64,19 +60,18 @@
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
-import java.io.IOException;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
- * Test {@link RemoteViews} that expect to operate within a {@link AppWidgetHostView} root.
+ * Test AppWidgets which host collection widgets.
  */
 @LargeTest
 @AppModeFull
 @RunWith(AndroidJUnit4.class)
-public class RemoteViewsWidgetTest {
+public class CollectionAppWidgetTest extends AppWidgetTestCase {
     public static final String[] COUNTRY_LIST = new String[] {
         "Argentina", "Australia", "Belize", "Botswana", "Brazil", "Cameroon", "China", "Cyprus",
         "Denmark", "Djibouti", "Ethiopia", "Fiji", "Finland", "France", "Gabon", "Germany",
@@ -87,17 +82,11 @@
         "Uganda", "Ukraine", "United States", "Vanuatu", "Venezuela", "Zimbabwe"
     };
 
-    private static final String GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND =
-        "appwidget grantbind --package android.widget.cts --user 0";
-
-    private static final String REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND =
-        "appwidget revokebind --package android.widget.cts --user 0";
-
     private static final long TEST_TIMEOUT_MS = 5000;
 
     @Rule
-    public ActivityTestRule<RemoteViewsCtsActivity> mActivityRule =
-            new ActivityTestRule<>(RemoteViewsCtsActivity.class);
+    public ActivityTestRule<EmptyActivity> mActivityRule =
+            new ActivityTestRule<>(EmptyActivity.class);
 
     private Instrumentation mInstrumentation;
 
@@ -136,7 +125,7 @@
 
         // Configure the app widget provider behavior
         final CountDownLatch providerCountDownLatch = new CountDownLatch(2);
-        MyAppWidgetProvider.configure(providerCountDownLatch, null, null);
+        CollectionAppWidgetProvider.configure(providerCountDownLatch, null, null);
 
         // Grab the provider to be bound
         final AppWidgetProviderInfo providerInfo = getAppWidgetProviderInfo();
@@ -170,9 +159,9 @@
                 remoteViews.setTextViewText(R.id.item, COUNTRY_LIST[position]);
 
                 // Set a fill-intent which will be used to fill-in the pending intent template
-                // which is set on the collection view in MyAppWidgetProvider.
+                // which is set on the collection view in CollectionAppWidgetProvider.
                 Bundle extras = new Bundle();
-                extras.putString(MockURLSpanTestActivity.KEY_PARAM, COUNTRY_LIST[position]);
+                extras.putString(ClickBroadcastReceiver.KEY_PARAM, COUNTRY_LIST[position]);
                 Intent fillInIntent = new Intent();
                 fillInIntent.putExtras(extras);
                 remoteViews.setOnClickFillInIntent(R.id.item, fillInIntent);
@@ -199,17 +188,14 @@
 
         // Add our host view to the activity behind this test. This is similar to how launchers
         // add widgets to the on-screen UI.
-        ViewGroup root = (ViewGroup) mActivityRule.getActivity().findViewById(R.id.remoteView_host);
-        FrameLayout.MarginLayoutParams lp = new FrameLayout.MarginLayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                ViewGroup.LayoutParams.MATCH_PARENT);
-        mAppWidgetHostView.setLayoutParams(lp);
-
-        mActivityRule.runOnUiThread(() -> root.addView(mAppWidgetHostView));
+        mActivityRule.runOnUiThread(() -> {
+            EmptyActivity activity = mActivityRule.getActivity();
+            activity.setContentView(mAppWidgetHostView);
+        });
     }
 
     @After
-    public void teardown() {
+    public void teardown() throws Exception {
         if (!mHasAppWidgets) {
             return;
         }
@@ -217,38 +203,13 @@
         revokeBindAppWidgetPermission();
     }
 
-    private void grantBindAppWidgetPermission() {
-        try {
-            SystemUtil.runShellCommand(mInstrumentation, GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND);
-        } catch (IOException e) {
-            fail("Error granting app widget permission. Command: "
-                    + GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND + ": ["
-                    + e.getMessage() + "]");
-        }
-    }
-
-    private void revokeBindAppWidgetPermission() {
-        try {
-            SystemUtil.runShellCommand(mInstrumentation, REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND);
-        } catch (IOException e) {
-            fail("Error revoking app widget permission. Command: "
-                    + REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND + ": ["
-                    + e.getMessage() + "]");
-        }
-    }
-
-    private boolean hasAppWidgets() {
-        return mInstrumentation.getTargetContext().getPackageManager()
-                .hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS);
-    }
-
     private AppWidgetManager getAppWidgetManager() {
         return (AppWidgetManager) mContext.getSystemService(Context.APPWIDGET_SERVICE);
     }
 
     private AppWidgetProviderInfo getAppWidgetProviderInfo() {
         ComponentName firstComponentName = new ComponentName(mContext.getPackageName(),
-                MyAppWidgetProvider.class.getName());
+                CollectionAppWidgetProvider.class.getName());
 
         return getProviderInfo(firstComponentName);
     }
@@ -286,14 +247,14 @@
 
     private void verifySetDisplayedChild(int displayedChildIndex) {
         final CountDownLatch updateLatch = new CountDownLatch(1);
-        MyAppWidgetProvider.configure(updateLatch, null, null);
+        CollectionAppWidgetProvider.configure(updateLatch, null, null);
 
         // Create the intent to update the widget. Note that we're passing the value
         // for displayed child index in the intent
-        Intent intent = new Intent(mContext, MyAppWidgetProvider.class);
+        Intent intent = new Intent(mContext, CollectionAppWidgetProvider.class);
         intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
         intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new  int[] { mAppWidgetId });
-        intent.putExtra(MyAppWidgetProvider.KEY_DISPLAYED_CHILD_INDEX, displayedChildIndex);
+        intent.putExtra(CollectionAppWidgetProvider.KEY_DISPLAYED_CHILD_INDEX, displayedChildIndex);
         mContext.sendBroadcast(intent);
 
         // Wait until the update request has been processed
@@ -323,12 +284,12 @@
 
     private void verifyShowCommand(String intentShowKey, int expectedDisplayedChild) {
         final CountDownLatch updateLatch = new CountDownLatch(1);
-        MyAppWidgetProvider.configure(updateLatch, null, null);
+        CollectionAppWidgetProvider.configure(updateLatch, null, null);
 
         // Create the intent to update the widget. Note that we're passing the "indication"
         // which one of showNext / showPrevious APIs to execute in the intent that we're
         // creating.
-        Intent intent = new Intent(mContext, MyAppWidgetProvider.class);
+        Intent intent = new Intent(mContext, CollectionAppWidgetProvider.class);
         intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
         intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new  int[] { mAppWidgetId });
         intent.putExtra(intentShowKey, true);
@@ -355,34 +316,30 @@
         mStackView = (StackView) mAppWidgetHostView.findViewById(R.id.remoteViews_stack);
 
         // Two forward
-        verifyShowCommand(MyAppWidgetProvider.KEY_SHOW_NEXT, 1);
-        verifyShowCommand(MyAppWidgetProvider.KEY_SHOW_NEXT, 2);
+        verifyShowCommand(CollectionAppWidgetProvider.KEY_SHOW_NEXT, 1);
+        verifyShowCommand(CollectionAppWidgetProvider.KEY_SHOW_NEXT, 2);
         // Four back (looping to the end of the adapter data)
-        verifyShowCommand(MyAppWidgetProvider.KEY_SHOW_PREVIOUS, 1);
-        verifyShowCommand(MyAppWidgetProvider.KEY_SHOW_PREVIOUS, 0);
-        verifyShowCommand(MyAppWidgetProvider.KEY_SHOW_PREVIOUS, COUNTRY_LIST.length - 1);
-        verifyShowCommand(MyAppWidgetProvider.KEY_SHOW_PREVIOUS, COUNTRY_LIST.length - 2);
+        verifyShowCommand(CollectionAppWidgetProvider.KEY_SHOW_PREVIOUS, 1);
+        verifyShowCommand(CollectionAppWidgetProvider.KEY_SHOW_PREVIOUS, 0);
+        verifyShowCommand(CollectionAppWidgetProvider.KEY_SHOW_PREVIOUS, COUNTRY_LIST.length - 1);
+        verifyShowCommand(CollectionAppWidgetProvider.KEY_SHOW_PREVIOUS, COUNTRY_LIST.length - 2);
         // And three forward (looping to the start of the adapter data)
-        verifyShowCommand(MyAppWidgetProvider.KEY_SHOW_NEXT, COUNTRY_LIST.length - 1);
-        verifyShowCommand(MyAppWidgetProvider.KEY_SHOW_NEXT, 0);
-        verifyShowCommand(MyAppWidgetProvider.KEY_SHOW_NEXT, 1);
+        verifyShowCommand(CollectionAppWidgetProvider.KEY_SHOW_NEXT, COUNTRY_LIST.length - 1);
+        verifyShowCommand(CollectionAppWidgetProvider.KEY_SHOW_NEXT, 0);
+        verifyShowCommand(CollectionAppWidgetProvider.KEY_SHOW_NEXT, 1);
     }
 
     private void verifyItemClickIntents(int indexToClick) throws Throwable {
-        Instrumentation.ActivityMonitor am = mInstrumentation.addMonitor(
-                MockURLSpanTestActivity.class.getName(), null, false);
+        ClickBroadcastReceiver receiver = new ClickBroadcastReceiver();
+        mActivityRule.runOnUiThread(receiver::register);
 
         mStackView = (StackView) mAppWidgetHostView.findViewById(R.id.remoteViews_stack);
         PollingCheck.waitFor(() -> mStackView.getCurrentView() != null);
         final View initialView = mStackView.getCurrentView();
         mActivityRule.runOnUiThread(
                 () -> mStackView.performItemClick(initialView, indexToClick, 0L));
-
-        Activity newActivity = am.waitForActivityWithTimeout(TEST_TIMEOUT_MS);
-        assertNotNull(newActivity);
-        assertTrue(newActivity instanceof MockURLSpanTestActivity);
-        assertEquals(COUNTRY_LIST[indexToClick], ((MockURLSpanTestActivity) newActivity).getParam());
-        newActivity.finish();
+        assertEquals(COUNTRY_LIST[indexToClick],
+                receiver.getParam(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS));
     }
 
     @Test
@@ -398,7 +355,7 @@
         verifyItemClickIntents(2);
 
         // And one more
-        verifyShowCommand(MyAppWidgetProvider.KEY_SHOW_NEXT, 3);
+        verifyShowCommand(CollectionAppWidgetProvider.KEY_SHOW_NEXT, 3);
         verifyItemClickIntents(3);
     }
 
@@ -443,7 +400,7 @@
         // 3. The gating condition that waits until the setScrollPosition has completed
         //    its processing / scrolling so that we can proceed to call
         //    setRelativeScrollPosition on it
-        MyAppWidgetProvider.configure(updateLatch, () -> mListView.getChildCount() > 0,
+        CollectionAppWidgetProvider.configure(updateLatch, () -> mListView.getChildCount() > 0,
                 scrollToPositionIsComplete::get);
 
         final int positionToScrollTo = COUNTRY_LIST.length - 10;
@@ -459,12 +416,12 @@
 
         // Create the intent to update the widget. Note that we're passing the "indication"
         // to switch to our ListView in the intent that we're creating.
-        Intent intent = new Intent(mContext, MyAppWidgetProvider.class);
+        Intent intent = new Intent(mContext, CollectionAppWidgetProvider.class);
         intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
         intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new  int[] { mAppWidgetId });
-        intent.putExtra(MyAppWidgetProvider.KEY_SWITCH_TO_LIST, true);
-        intent.putExtra(MyAppWidgetProvider.KEY_SCROLL_POSITION, positionToScrollTo);
-        intent.putExtra(MyAppWidgetProvider.KEY_SCROLL_OFFSET, -scrollByAmount);
+        intent.putExtra(CollectionAppWidgetProvider.KEY_SWITCH_TO_LIST, true);
+        intent.putExtra(CollectionAppWidgetProvider.KEY_SCROLL_POSITION, positionToScrollTo);
+        intent.putExtra(CollectionAppWidgetProvider.KEY_SCROLL_OFFSET, -scrollByAmount);
         mContext.sendBroadcast(intent);
 
         // Wait until the update request has been processed
@@ -520,4 +477,30 @@
         }
         assertTrue("Timed out while waiting for the target view to be scrolled into view", result);
     }
+
+    private static final class ClickBroadcastReceiver extends BroadcastReceiver {
+
+        public static final String KEY_PARAM = "ClickBroadcastReceiver.param";
+
+        private final CountDownLatch latch = new CountDownLatch(1);
+
+        private String mParams;
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mParams = intent.getStringExtra(KEY_PARAM);
+            InstrumentationRegistry.getTargetContext().unregisterReceiver(this);
+            latch.countDown();
+        }
+
+        public String getParam(long timeout, TimeUnit unit) throws InterruptedException {
+            latch.await(timeout, unit);
+            return mParams;
+        }
+
+        public void register() {
+            InstrumentationRegistry.getTargetContext().registerReceiver(this,
+                    new IntentFilter(CollectionAppWidgetProvider.BROADCAST_ACTION));
+        }
+    }
 }
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/EmptyActivity.java b/tests/tests/appwidget/src/android/appwidget/cts/EmptyActivity.java
new file mode 100644
index 0000000..6bb0915
--- /dev/null
+++ b/tests/tests/appwidget/src/android/appwidget/cts/EmptyActivity.java
@@ -0,0 +1,20 @@
+/*
+ * 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.appwidget.cts;
+
+import android.app.Activity;
+
+public class EmptyActivity extends Activity { }
diff --git a/tests/tests/widget/src/android/widget/cts/appwidget/MyAppWidgetProvider.java b/tests/tests/appwidget/src/android/appwidget/cts/provider/CollectionAppWidgetProvider.java
similarity index 94%
rename from tests/tests/widget/src/android/widget/cts/appwidget/MyAppWidgetProvider.java
rename to tests/tests/appwidget/src/android/appwidget/cts/provider/CollectionAppWidgetProvider.java
index a0711e2..b1b21b7 100644
--- a/tests/tests/widget/src/android/widget/cts/appwidget/MyAppWidgetProvider.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/provider/CollectionAppWidgetProvider.java
@@ -13,12 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package android.widget.cts.appwidget;
+package android.appwidget.cts.provider;
 
 import android.app.PendingIntent;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProvider;
+import android.appwidget.cts.R;
+import android.appwidget.cts.service.MyAppWidgetService;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
@@ -26,13 +27,12 @@
 import android.os.Looper;
 import android.view.View;
 import android.widget.RemoteViews;
-import android.widget.cts.R;
 
 import com.android.compatibility.common.util.PollingCheck;
 
 import java.util.concurrent.CountDownLatch;
 
-public final class MyAppWidgetProvider extends AppWidgetProvider {
+public class CollectionAppWidgetProvider  extends AppWidgetProvider {
     private static final long TIME_SLICE = 100;
 
     public static final String KEY_DISPLAYED_CHILD_INDEX =
@@ -42,6 +42,7 @@
     public static final String KEY_SWITCH_TO_LIST = "MyAppWidgetProvider.switchToList";
     public static final String KEY_SCROLL_POSITION = "MyAppWidgetProvider.scrollPosition";
     public static final String KEY_SCROLL_OFFSET = "MyAppWidgetProvider.scrollOffset";
+    public static final String BROADCAST_ACTION = CollectionAppWidgetProvider.class.getName();
 
     // This latch will be notified when onEnabled is called on our provider.
     private static CountDownLatch sCountDownLatch;
@@ -104,9 +105,8 @@
         // cannot setup their own pending intents, instead, the collection as a whole can
         // setup a pending intent template, and the individual items can set a fillInIntent
         // to create unique before on an item to item basis.
-        Intent viewIntent = new Intent(Intent.ACTION_VIEW,
-                Uri.parse("ctstest://RemoteView/testWidget"));
-        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, viewIntent,
+        Intent viewIntent = new Intent(BROADCAST_ACTION);
+        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, viewIntent,
                 PendingIntent.FLAG_UPDATE_CURRENT);
 
         widgetAdapterView.setPendingIntentTemplate(R.id.remoteViews_stack, pendingIntent);
diff --git a/tests/tests/background/AndroidTest.xml b/tests/tests/background/AndroidTest.xml
index c54940c..f0af389 100644
--- a/tests/tests/background/AndroidTest.xml
+++ b/tests/tests/background/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for background restrictions 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="instant_app" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/batterysaving/apps/app_target_api_25/Android.mk b/tests/tests/batterysaving/apps/app_target_api_25/Android.mk
index f1ebee6..42c1466 100644
--- a/tests/tests/batterysaving/apps/app_target_api_25/Android.mk
+++ b/tests/tests/batterysaving/apps/app_target_api_25/Android.mk
@@ -33,6 +33,7 @@
     ub-uiautomator
 
 LOCAL_SDK_VERSION := test_current
+LOCAL_MIN_SDK_VERSION := 23
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
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 4aef998..19a34eb1 100644
--- a/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySavingTestBase.java
+++ b/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySavingTestBase.java
@@ -29,6 +29,7 @@
 import android.support.test.InstrumentationRegistry;
 import android.util.Log;
 
+import com.android.compatibility.common.util.BatteryUtils;
 import com.android.compatibility.common.util.BeforeAfterRule;
 import com.android.compatibility.common.util.OnFailureRule;
 
@@ -60,9 +61,7 @@
     private final BeforeAfterRule mInitializeAndCleanupRule = new BeforeAfterRule() {
         @Override
         protected void onBefore(Statement base, Description description) throws Throwable {
-            // Don't run any battery saver tests on wear.
-            final PackageManager pm = getPackageManager();
-            Assume.assumeFalse(pm.hasSystemFeature(PackageManager.FEATURE_WATCH));
+            BatteryUtils.assumeBatterySaverFeature();
 
             turnOnScreen(true);
         }
diff --git a/tests/tests/calendarcommon/Android.mk b/tests/tests/calendarcommon/Android.mk
index bfd9f26..fd97621 100644
--- a/tests/tests/calendarcommon/Android.mk
+++ b/tests/tests/calendarcommon/Android.mk
@@ -32,6 +32,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 15
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/tests/tests/colormode/AndroidTest.xml b/tests/tests/colormode/AndroidTest.xml
index eb491c2..4316cc6 100644
--- a/tests/tests/colormode/AndroidTest.xml
+++ b/tests/tests/colormode/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS Color Mode test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="graphics" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/content/Android.mk b/tests/tests/content/Android.mk
index 5d59af7..480bb06 100644
--- a/tests/tests/content/Android.mk
+++ b/tests/tests/content/Android.mk
@@ -63,7 +63,8 @@
 	-c tlh \
 	-c xx,xx-rYY
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    $(call all-Iaidl-files-under, BinderPermissionTestService)
 LOCAL_MULTILIB := both
 LOCAL_PACKAGE_NAME := CtsContentTestCases
 LOCAL_PRIVATE_PLATFORM_APIS := true
diff --git a/tests/tests/content/AndroidManifest.xml b/tests/tests/content/AndroidManifest.xml
index 20baa2d..a4e5b1f 100644
--- a/tests/tests/content/AndroidManifest.xml
+++ b/tests/tests/content/AndroidManifest.xml
@@ -144,9 +144,9 @@
 
         <uses-library android:name="android.test.runner" />
 
-        <service android:name="android.content.cts.MockContextWrapperService" />
-        <activity android:name=".content.ContextWrapperCtsActivity"
-            android:label="ContextWrapperCtsActivity">
+        <service android:name="android.content.cts.MockContextService" />
+        <activity android:name=".content.ContextCtsActivity"
+            android:label="ContextCtsActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
             </intent-filter>
@@ -167,7 +167,7 @@
             <intent-filter android:priority="1">
                 <action android:name="android.content.cts.BroadcastReceiverTest.BROADCAST_MOCKTEST" />
                 <action android:name="android.content.cts.BroadcastReceiverTest.BROADCAST_TESTABORT" />
-                <action android:name="android.content.cts.ContextWrapperTest.BROADCAST_TESTORDER" />
+                <action android:name="android.content.cts.ContextTest.BROADCAST_TESTORDER" />
             </intent-filter>
         </receiver>
 
@@ -189,7 +189,8 @@
 
         <!--Test for PackageManager-->
         <activity android:name="android.content.pm.cts.TestPmActivity"
-                android:icon="@drawable/start">
+                android:icon="@drawable/start"
+                android:launchMode="singleTop">
             <intent-filter>
                 <action android:name="android.intent.action.PMTEST" />
                 <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
@@ -203,7 +204,8 @@
             </intent-filter>
         </activity>
         <!--Test for PackageManager-->
-        <service android:name="android.content.pm.cts.TestPmService">
+        <service android:name="android.content.pm.cts.TestPmService"
+            android:permission="android.content.cts.CALL_ABROAD_PERMISSION">
             <intent-filter>
                 <action android:name="android.content.pm.cts.activity.PMTEST_SERVICE" />
             </intent-filter>
diff --git a/tests/tests/content/AndroidTest.xml b/tests/tests/content/AndroidTest.xml
index 58305dd..7b7f52e 100644
--- a/tests/tests/content/AndroidTest.xml
+++ b/tests/tests/content/AndroidTest.xml
@@ -32,6 +32,7 @@
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsContentTestCases.apk" />
         <option name="test-file-name" value="CtsSyncAccountAccessStubs.apk" />
+        <option name="test-file-name" value="CtsBinderPermissionTestService.apk" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/tests/tests/content/BinderPermissionTestService/Android.mk b/tests/tests/content/BinderPermissionTestService/Android.mk
new file mode 100644
index 0000000..697d8f6
--- /dev/null
+++ b/tests/tests/content/BinderPermissionTestService/Android.mk
@@ -0,0 +1,36 @@
+#
+# 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_SDK_VERSION := current
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    $(call all-Iaidl-files-under, aidl)
+
+LOCAL_PACKAGE_NAME := CtsBinderPermissionTestService
+
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/tests/content/BinderPermissionTestService/AndroidManifest.xml b/tests/tests/content/BinderPermissionTestService/AndroidManifest.xml
new file mode 100644
index 0000000..c0e4774
--- /dev/null
+++ b/tests/tests/content/BinderPermissionTestService/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts">
+    <application>
+        <service
+            android:name=".BinderPermissionTestService"
+            android:exported="true">
+        </service>
+    </application>
+</manifest>
diff --git a/tests/tests/content/BinderPermissionTestService/README.txt b/tests/tests/content/BinderPermissionTestService/README.txt
new file mode 100644
index 0000000..1b7c2bd
--- /dev/null
+++ b/tests/tests/content/BinderPermissionTestService/README.txt
@@ -0,0 +1,7 @@
+A test project that publishes a Binder service. The methods of this service
+check if their caller has certain permissions. This service is used by
+Context tests to verify that methods like enforceCallingPermission()
+work correctly.
+
+This service has to be in a separate package so the permissions of the
+caller (the test) and the callee (this service) are different.
diff --git a/tests/tests/content/BinderPermissionTestService/aidl/com/android/cts/IBinderPermissionTestService.aidl b/tests/tests/content/BinderPermissionTestService/aidl/com/android/cts/IBinderPermissionTestService.aidl
new file mode 100644
index 0000000..26e6326
--- /dev/null
+++ b/tests/tests/content/BinderPermissionTestService/aidl/com/android/cts/IBinderPermissionTestService.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+interface IBinderPermissionTestService {
+    // Methods that, when called, invoke one of the permission check methods
+    // to check the caller's permissions.
+    void doEnforceCallingPermission(String permission);
+    int doCheckCallingPermission(String permission);
+    void doEnforceCallingOrSelfPermission(String permission);
+    int doCheckCallingOrSelfPermission(String permission);
+}
diff --git a/tests/tests/content/BinderPermissionTestService/src/com/android/cts/BinderPermissionTestService.java b/tests/tests/content/BinderPermissionTestService/src/com/android/cts/BinderPermissionTestService.java
new file mode 100644
index 0000000..955fb30
--- /dev/null
+++ b/tests/tests/content/BinderPermissionTestService/src/com/android/cts/BinderPermissionTestService.java
@@ -0,0 +1,60 @@
+/*
+ * 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;
+
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
+import android.accounts.NetworkErrorException;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+
+public class BinderPermissionTestService extends Service {
+
+    private static String TEST_NOT_ALLOWED_MESSAGE = "Test: you're not allowed to do this.";
+
+    private final IBinder mBinder = new IBinderPermissionTestService.Stub() {
+        @Override
+        public void doEnforceCallingPermission(String permission) {
+            enforceCallingPermission(permission, TEST_NOT_ALLOWED_MESSAGE);
+        }
+
+        @Override
+        public int doCheckCallingPermission(String permission) {
+            return checkCallingPermission(permission);
+        }
+
+        @Override
+        public void doEnforceCallingOrSelfPermission(String permission) {
+            enforceCallingOrSelfPermission(permission, TEST_NOT_ALLOWED_MESSAGE);
+        }
+
+        @Override
+        public int doCheckCallingOrSelfPermission(String permission) {
+            return checkCallingOrSelfPermission(permission);
+        }
+    };
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+}
diff --git a/tests/tests/content/SyncAccountAccessStubs/Android.mk b/tests/tests/content/SyncAccountAccessStubs/Android.mk
index 9f015a3..d176ce7 100644
--- a/tests/tests/content/SyncAccountAccessStubs/Android.mk
+++ b/tests/tests/content/SyncAccountAccessStubs/Android.mk
@@ -20,10 +20,13 @@
 
 LOCAL_MODULE_TAGS := tests
 
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-annotations
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsSyncAccountAccessStubs
-LOCAL_PRIVATE_PLATFORM_APIS := true
+
+LOCAL_SDK_VERSION := current
 
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
diff --git a/tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubProvider.java b/tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubProvider.java
index 8a21e0d..c97e115 100644
--- a/tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubProvider.java
+++ b/tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubProvider.java
@@ -16,12 +16,12 @@
 
 package com.android.cts.stub;
 
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 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 StubProvider extends ContentProvider {
     @Override
diff --git a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
index 4723524..f82e43f 100644
--- a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
+++ b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
@@ -365,4 +365,22 @@
             assertCanBeHandled(new Intent("android.settings.LOCATION_SCANNING_SETTINGS"));
         }
     }
+
+    public void testChangeDefaultDialer() {
+        PackageManager packageManager = mContext.getPackageManager();
+        if (packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            assertCanBeHandled(new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER));
+        }
+    }
+
+    public void testTapAnPaySettings() {
+        PackageManager packageManager = mContext.getPackageManager();
+        if (packageManager.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
+            assertCanBeHandled(new Intent(Settings.ACTION_NFC_PAYMENT_SETTINGS));
+        }
+    }
+
+    public void testPowerUsageSummarySettings() {
+        assertCanBeHandled(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY));
+    }
 }
diff --git a/tests/tests/content/src/android/content/cts/ContentProviderCursorWindowTest.java b/tests/tests/content/src/android/content/cts/ContentProviderCursorWindowTest.java
index 4d5378c..5cfd532 100644
--- a/tests/tests/content/src/android/content/cts/ContentProviderCursorWindowTest.java
+++ b/tests/tests/content/src/android/content/cts/ContentProviderCursorWindowTest.java
@@ -16,7 +16,9 @@
 
 package android.content.cts;
 
+import android.database.Cursor;
 import android.database.CursorWindowAllocationException;
+import android.database.sqlite.SQLiteException;
 import android.net.Uri;
 import android.platform.test.annotations.SecurityTest;
 import android.test.AndroidTestCase;
@@ -30,13 +32,33 @@
     private static final String TAG = "ContentProviderCursorWindowTest";
 
     public void testQuery() {
+        // First check if the system has a patch for enforcing protected Parcel data
+        Cursor cursor;
         try {
-            getContext().getContentResolver().query(
+            cursor = getContext().getContentResolver().query(
                     Uri.parse("content://cursorwindow.provider/hello"),
                     null, null, null, null);
-            fail("Reading from malformed Parcel should fail due to invalid offset used");
         } catch (CursorWindowAllocationException expected) {
             Log.i(TAG, "Expected exception: " + expected);
+            return;
+        }
+
+        // If the system has no patch for protected Parcel data,
+        // it should still fail while reading from the cursor
+        try {
+            cursor.moveToFirst();
+
+            int type = cursor.getType(0);
+            if (type != Cursor.FIELD_TYPE_BLOB) {
+                fail("Unexpected type " + type);
+            }
+            byte[] blob = cursor.getBlob(0);
+            Log.i(TAG,  "Blob length " + blob.length);
+            fail("getBlob should fail due to invalid offset used in the field slot");
+        } catch (SQLiteException expected) {
+            Log.i(TAG, "Expected exception: " + expected);
+        } finally {
+            cursor.close();
         }
     }
 }
diff --git a/tests/tests/content/src/android/content/cts/ContentValuesTest.java b/tests/tests/content/src/android/content/cts/ContentValuesTest.java
index 24a6e36..4159817 100644
--- a/tests/tests/content/src/android/content/cts/ContentValuesTest.java
+++ b/tests/tests/content/src/android/content/cts/ContentValuesTest.java
@@ -16,24 +16,36 @@
 
 package android.content.cts;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
+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.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.content.ContentValues;
 import android.os.Parcel;
-import android.test.AndroidTestCase;
+import android.support.test.runner.AndroidJUnit4;
 
-public class ContentValuesTest extends AndroidTestCase {
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+public class ContentValuesTest {
     ContentValues mContentValues;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+    @Before
+    public void setUp() throws Exception {
         mContentValues = new ContentValues();
     }
 
+    @Test
     public void testConstructor() {
         new ContentValues();
         new ContentValues(5);
@@ -54,6 +66,7 @@
         }
     }
 
+    @Test
     public void testValueSet() {
         Set<Map.Entry<String, Object>> map;
         assertNotNull(map = mContentValues.valueSet());
@@ -67,6 +80,7 @@
         assertEquals(2, map.size());
     }
 
+    @Test
     public void testPutNull() {
         mContentValues.putNull("key");
         assertNull(mContentValues.get("key"));
@@ -81,6 +95,7 @@
         mContentValues.putNull(null);
     }
 
+    @Test
     public void testGetAsLong() {
         Long expected = 10L;
         mContentValues.put("Long", expected);
@@ -94,6 +109,7 @@
         assertNull(mContentValues.getAsLong(null));
     }
 
+    @Test
     public void testGetAsByte() {
         Byte expected = 'a';
         mContentValues.put("Byte", expected);
@@ -107,6 +123,7 @@
         assertNull(mContentValues.getAsByte(null));
     }
 
+    @Test
     public void testGetAsInteger() {
         Integer expected = 20;
         mContentValues.put("Integer", expected);
@@ -120,6 +137,7 @@
         assertNull(mContentValues.getAsInteger(null));
     }
 
+    @Test
     public void testSize() {
         assertEquals(0, mContentValues.size());
 
@@ -135,6 +153,7 @@
         assertEquals(0, mContentValues.size());
     }
 
+    @Test
     public void testGetAsShort() {
         Short expected = 20;
         mContentValues.put("Short", expected);
@@ -148,6 +167,7 @@
         assertNull(mContentValues.getAsShort(null));
     }
 
+    @Test
     public void testHashCode() {
         assertEquals(0, mContentValues.hashCode());
 
@@ -166,6 +186,7 @@
         assertEquals(0, mContentValues.hashCode());
     }
 
+    @Test
     public void testGetAsFloat() {
         Float expected = 1.0F;
         mContentValues.put("Float", expected);
@@ -179,6 +200,7 @@
         assertNull(mContentValues.getAsFloat(null));
     }
 
+    @Test
     public void testGetAsBoolean() {
         mContentValues.put("Boolean", true);
         assertTrue(mContentValues.getAsBoolean("Boolean"));
@@ -190,6 +212,7 @@
         assertNull(mContentValues.getAsBoolean(null));
     }
 
+    @Test
     public void testToString() {
         assertNotNull(mContentValues.toString());
 
@@ -198,6 +221,7 @@
         assertTrue(mContentValues.toString().length() > 0);
     }
 
+    @Test
     public void testGet() {
         Object expected = "android";
         mContentValues.put("Object", "android");
@@ -211,6 +235,7 @@
         assertNull(mContentValues.get(null));
     }
 
+    @Test
     public void testEquals() {
         mContentValues.put("Boolean", false);
         mContentValues.put("String", "string");
@@ -222,6 +247,7 @@
         assertTrue(mContentValues.equals(cv));
     }
 
+    @Test
     public void testEqualsFailure() {
         // the target object is not an instance of ContentValues.
         assertFalse(mContentValues.equals(new String()));
@@ -237,6 +263,7 @@
         assertFalse(mContentValues.equals(cv));
     }
 
+    @Test
     public void testGetAsDouble() {
         Double expected = 10.2;
         mContentValues.put("Double", expected);
@@ -250,6 +277,7 @@
         assertNull(mContentValues.getAsDouble(null));
     }
 
+    @Test
     public void testPutString() {
         String expected = "cts";
         mContentValues.put("String", expected);
@@ -263,6 +291,7 @@
         mContentValues.put(null, (String)null);
     }
 
+    @Test
     public void testPutByte() {
         Byte expected = 'a';
         mContentValues.put("Byte", expected);
@@ -276,6 +305,7 @@
         mContentValues.put(null, (Byte)null);
     }
 
+    @Test
     public void testPutShort() {
         Short expected = 20;
         mContentValues.put("Short", expected);
@@ -289,6 +319,7 @@
         mContentValues.put(null, (Short)null);
     }
 
+    @Test
     public void testPutInteger() {
         Integer expected = 20;
         mContentValues.put("Integer", expected);
@@ -302,6 +333,7 @@
         mContentValues.put(null, (Integer)null);
     }
 
+    @Test
     public void testPutLong() {
         Long expected = 10L;
         mContentValues.put("Long", expected);
@@ -315,6 +347,7 @@
         mContentValues.put(null, (Long)null);
     }
 
+    @Test
     public void testPutFloat() {
         Float expected = 1.0F;
         mContentValues.put("Float", expected);
@@ -328,6 +361,7 @@
         mContentValues.put(null, (Float)null);
     }
 
+    @Test
     public void testPutDouble() {
         Double expected = 10.2;
         mContentValues.put("Double", expected);
@@ -341,6 +375,7 @@
         mContentValues.put(null, (Double)null);
     }
 
+    @Test
     public void testPutBoolean() {
         // set the expected value
         mContentValues.put("Boolean", true);
@@ -353,6 +388,7 @@
         mContentValues.put(null, (Boolean)null);
     }
 
+    @Test
     public void testPutByteArray() {
         byte[] expected = new byte[] {'1', '2', '3', '4'};
         mContentValues.put("byte[]", expected);
@@ -362,6 +398,7 @@
         mContentValues.put(null, (byte[])null);
     }
 
+    @Test
     public void testContainsKey() {
         mContentValues.put("Double", 10.2);
         mContentValues.put("Float", 1.0F);
@@ -376,6 +413,7 @@
         assertFalse(mContentValues.containsKey(null));
     }
 
+    @Test
     public void testClear() {
         assertEquals(0, mContentValues.size());
 
@@ -387,6 +425,7 @@
         assertEquals(0, mContentValues.size());
     }
 
+    @Test
     @SuppressWarnings("deprecation")
     public void testAccessStringArrayList() {
         // set the expected value
@@ -402,6 +441,7 @@
         assertNull(mContentValues.getStringArrayList(null));
     }
 
+    @Test
     public void testRemove() {
         assertEquals(0, mContentValues.size());
 
@@ -427,6 +467,7 @@
         mContentValues.remove(null);
     }
 
+    @Test
     public void testGetAsString() {
         String expected = "cts";
         mContentValues.put("String", expected);
@@ -440,6 +481,7 @@
         assertNull(mContentValues.getAsString(null));
     }
 
+    @Test
     public void testGetAsByteArray() {
         byte[] expected = new byte[] {'1', '2', '3', '4'};
         mContentValues.put("byte[]", expected);
@@ -449,26 +491,26 @@
         assertNull(mContentValues.getAsByteArray(null));
     }
 
+    @Test
     @SuppressWarnings({ "unchecked" })
     public void testWriteToParcel() {
+        final ContentValues before = new ContentValues();
+        before.put("Integer", -110);
+        before.put("String", "cts");
+        before.put("Boolean", false);
+
         Parcel p = Parcel.obtain();
-
-        mContentValues.put("Integer", -110);
-        mContentValues.put("String", "cts");
-        mContentValues.put("Boolean", false);
-
-        mContentValues.writeToParcel(p, 0);
-
+        before.writeToParcel(p, 0);
         p.setDataPosition(0);
-        HashMap<String, Object> values = p.readHashMap(ClassLoader.getSystemClassLoader());
-        assertNotNull(values);
-        assertEquals(3, values.size());
 
-        assertEquals(-110, values.get("Integer"));
-        assertEquals("cts", values.get("String"));
-        assertEquals(false, values.get("Boolean"));
+        final ContentValues after = ContentValues.CREATOR.createFromParcel(p);
+        assertEquals(3, after.size());
+        assertEquals(-110, after.get("Integer"));
+        assertEquals("cts", after.get("String"));
+        assertEquals(false, after.get("Boolean"));
     }
 
+    @Test
     public void testWriteToParcelFailure() {
         try {
             mContentValues.writeToParcel(null, -1);
@@ -478,10 +520,12 @@
         }
     }
 
+    @Test
     public void testDescribeContents() {
         assertEquals(0, mContentValues.describeContents());
     }
 
+    @Test
     public void testPutAll() {
         assertEquals(0, mContentValues.size());
 
@@ -497,6 +541,7 @@
         assertEquals(3, mContentValues.size());
     }
 
+    @Test
     public void testPutAllFailure() {
         try {
             mContentValues.putAll(null);
diff --git a/tests/tests/content/src/android/content/cts/ContextWrapperCtsActivity.java b/tests/tests/content/src/android/content/cts/ContextCtsActivity.java
similarity index 94%
rename from tests/tests/content/src/android/content/cts/ContextWrapperCtsActivity.java
rename to tests/tests/content/src/android/content/cts/ContextCtsActivity.java
index 7ea2ab7..a7ea89a 100644
--- a/tests/tests/content/src/android/content/cts/ContextWrapperCtsActivity.java
+++ b/tests/tests/content/src/android/content/cts/ContextCtsActivity.java
@@ -23,7 +23,7 @@
 
 import android.content.cts.R;
 
-public class ContextWrapperCtsActivity extends Activity {
+public class ContextCtsActivity extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
diff --git a/tests/tests/content/src/android/content/cts/ContextTest.java b/tests/tests/content/src/android/content/cts/ContextTest.java
index f84c771..413da63 100644
--- a/tests/tests/content/src/android/content/cts/ContextTest.java
+++ b/tests/tests/content/src/android/content/cts/ContextTest.java
@@ -16,39 +16,131 @@
 
 package android.content.cts;
 
+import android.content.ActivityNotFoundException;
+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.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.content.res.ColorStateList;
 import android.content.res.Resources.NotFoundException;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.database.Cursor;
+import android.database.sqlite.SQLiteCursorDriver;
 import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteQuery;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
+import android.os.Process;
+import android.preference.PreferenceManager;
 import android.test.AndroidTestCase;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Xml;
 import android.view.WindowManager;
 
+import com.android.compatibility.common.util.PollingCheck;
+import com.android.cts.IBinderPermissionTestService;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 public class ContextTest extends AndroidTestCase {
     private static final String TAG = "ContextTest";
+    private static final String ACTUAL_RESULT = "ResultSetByReceiver";
 
-    private Context mContext;
+    private static final String INTIAL_RESULT = "IntialResult";
+
+    private static final String VALUE_ADDED = "ValueAdded";
+    private static final String KEY_ADDED = "AddedByReceiver";
+
+    private static final String VALUE_REMOVED = "ValueWillBeRemove";
+    private static final String KEY_REMOVED = "ToBeRemoved";
+
+    private static final String VALUE_KEPT = "ValueKept";
+    private static final String KEY_KEPT = "ToBeKept";
+
+    private static final String MOCK_STICKY_ACTION = "android.content.cts.ContextTest."
+            + "STICKY_BROADCAST_RESULT";
+
+    private static final String ACTION_BROADCAST_TESTORDER =
+            "android.content.cts.ContextTest.BROADCAST_TESTORDER";
+    private final static String MOCK_ACTION1 = ACTION_BROADCAST_TESTORDER + "1";
+    private final static String MOCK_ACTION2 = ACTION_BROADCAST_TESTORDER + "2";
+
+    // Note: keep these constants in sync with the permissions used by BinderPermissionTestService.
+    //
+    // A permission that's granted to this test package.
+    public static final String GRANTED_PERMISSION = "android.permission.USE_CREDENTIALS";
+    // A permission that's not granted to this test package.
+    public static final String NOT_GRANTED_PERMISSION = "android.permission.HARDWARE_TEST";
+
+    private static final int BROADCAST_TIMEOUT = 10000;
+    private static final int ROOT_UID = 0;
+
+    private Object mLockObj;
+
+    private ArrayList<BroadcastReceiver> mRegisteredReceiverList;
+
+    private boolean mWallpaperChanged;
+    private BitmapDrawable mOriginalWallpaper;
+    private volatile IBinderPermissionTestService mBinderPermissionTestService;
+    private ServiceConnection mBinderPermissionTestConnection;
+
+    protected Context mContext;
+
+    /**
+     * Returns the Context object that's being tested.
+     */
+    protected Context getContextUnderTest() {
+        return getContext();
+    }
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        mContext = getContext();
+        mContext = getContextUnderTest();
         mContext.setTheme(R.style.Test_Theme);
+
+        mLockObj = new Object();
+
+        mRegisteredReceiverList = new ArrayList<BroadcastReceiver>();
+
+        mOriginalWallpaper = (BitmapDrawable) mContext.getWallpaper();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mWallpaperChanged) {
+            mContext.setWallpaper(mOriginalWallpaper.getBitmap());
+        }
+
+        for (BroadcastReceiver receiver : mRegisteredReceiverList) {
+            mContext.unregisterReceiver(receiver);
+        }
+
+        super.tearDown();
     }
 
     public void testGetString() {
@@ -354,6 +446,10 @@
 
     private void assertValidFile(File file) throws Exception {
         Log.d(TAG, "Checking " + file);
+        if (file.exists()) {
+            assertTrue("File already exists and couldn't be deleted before test: " + file,
+                    file.delete());
+        }
         assertTrue("Failed to create " + file, file.createNewFile());
         assertTrue("Doesn't exist after create " + file, file.exists());
         assertTrue("Failed to delete after create " + file, file.delete());
@@ -382,7 +478,7 @@
     }
 
     private AttributeSet getAttributeSet(int resourceId) {
-        final XmlResourceParser parser = getContext().getResources().getXml(
+        final XmlResourceParser parser = mContext.getResources().getXml(
                 resourceId);
 
         try {
@@ -397,4 +493,842 @@
         assertNotNull(attr);
         return attr;
     }
+
+    private void registerBroadcastReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+        mContext.registerReceiver(receiver, filter);
+
+        mRegisteredReceiverList.add(receiver);
+    }
+
+    public void testSendOrderedBroadcast1() throws InterruptedException {
+        final HighPriorityBroadcastReceiver highPriorityReceiver =
+                new HighPriorityBroadcastReceiver();
+        final LowPriorityBroadcastReceiver lowPriorityReceiver =
+                new LowPriorityBroadcastReceiver();
+
+        final IntentFilter filterHighPriority = new IntentFilter(ResultReceiver.MOCK_ACTION);
+        filterHighPriority.setPriority(1);
+        final IntentFilter filterLowPriority = new IntentFilter(ResultReceiver.MOCK_ACTION);
+        registerBroadcastReceiver(highPriorityReceiver, filterHighPriority);
+        registerBroadcastReceiver(lowPriorityReceiver, filterLowPriority);
+
+        final Intent broadcastIntent = new Intent(ResultReceiver.MOCK_ACTION);
+        broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        mContext.sendOrderedBroadcast(broadcastIntent, null);
+        new PollingCheck(BROADCAST_TIMEOUT) {
+            @Override
+            protected boolean check() {
+                return highPriorityReceiver.hasReceivedBroadCast()
+                        && !lowPriorityReceiver.hasReceivedBroadCast();
+            }
+        }.run();
+
+        synchronized (highPriorityReceiver) {
+            highPriorityReceiver.notify();
+        }
+
+        new PollingCheck(BROADCAST_TIMEOUT) {
+            @Override
+            protected boolean check() {
+                return highPriorityReceiver.hasReceivedBroadCast()
+                        && lowPriorityReceiver.hasReceivedBroadCast();
+            }
+        }.run();
+    }
+
+    public void testSendOrderedBroadcast2() throws InterruptedException {
+        final TestBroadcastReceiver broadcastReceiver = new TestBroadcastReceiver();
+        broadcastReceiver.mIsOrderedBroadcasts = true;
+
+        Bundle bundle = new Bundle();
+        bundle.putString(KEY_KEPT, VALUE_KEPT);
+        bundle.putString(KEY_REMOVED, VALUE_REMOVED);
+        Intent intent = new Intent(ResultReceiver.MOCK_ACTION);
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        mContext.sendOrderedBroadcast(intent, null, broadcastReceiver, null, 1,
+                INTIAL_RESULT, bundle);
+
+        synchronized (mLockObj) {
+            try {
+                mLockObj.wait(BROADCAST_TIMEOUT);
+            } catch (InterruptedException e) {
+                fail("unexpected InterruptedException.");
+            }
+        }
+
+        assertTrue("Receiver didn't make any response.", broadcastReceiver.hadReceivedBroadCast());
+        assertEquals("Incorrect code: " + broadcastReceiver.getResultCode(), 3,
+                broadcastReceiver.getResultCode());
+        assertEquals(ACTUAL_RESULT, broadcastReceiver.getResultData());
+        Bundle resultExtras = broadcastReceiver.getResultExtras(false);
+        assertEquals(VALUE_ADDED, resultExtras.getString(KEY_ADDED));
+        assertEquals(VALUE_KEPT, resultExtras.getString(KEY_KEPT));
+        assertNull(resultExtras.getString(KEY_REMOVED));
+    }
+
+    public void testRegisterReceiver1() throws InterruptedException {
+        final FilteredReceiver broadcastReceiver = new FilteredReceiver();
+        final IntentFilter filter = new IntentFilter(MOCK_ACTION1);
+
+        // Test registerReceiver
+        mContext.registerReceiver(broadcastReceiver, filter);
+
+        // Test unwanted intent(action = MOCK_ACTION2)
+        broadcastReceiver.reset();
+        waitForFilteredIntent(mContext, MOCK_ACTION2);
+        assertFalse(broadcastReceiver.hadReceivedBroadCast1());
+        assertFalse(broadcastReceiver.hadReceivedBroadCast2());
+
+        // Send wanted intent(action = MOCK_ACTION1)
+        broadcastReceiver.reset();
+        waitForFilteredIntent(mContext, MOCK_ACTION1);
+        assertTrue(broadcastReceiver.hadReceivedBroadCast1());
+        assertFalse(broadcastReceiver.hadReceivedBroadCast2());
+
+        mContext.unregisterReceiver(broadcastReceiver);
+
+        // Test unregisterReceiver
+        FilteredReceiver broadcastReceiver2 = new FilteredReceiver();
+        mContext.registerReceiver(broadcastReceiver2, filter);
+        mContext.unregisterReceiver(broadcastReceiver2);
+
+        // Test unwanted intent(action = MOCK_ACTION2)
+        broadcastReceiver2.reset();
+        waitForFilteredIntent(mContext, MOCK_ACTION2);
+        assertFalse(broadcastReceiver2.hadReceivedBroadCast1());
+        assertFalse(broadcastReceiver2.hadReceivedBroadCast2());
+
+        // Send wanted intent(action = MOCK_ACTION1), but the receiver is unregistered.
+        broadcastReceiver2.reset();
+        waitForFilteredIntent(mContext, MOCK_ACTION1);
+        assertFalse(broadcastReceiver2.hadReceivedBroadCast1());
+        assertFalse(broadcastReceiver2.hadReceivedBroadCast2());
+    }
+
+    public void testRegisterReceiver2() throws InterruptedException {
+        FilteredReceiver broadcastReceiver = new FilteredReceiver();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(MOCK_ACTION1);
+
+        // Test registerReceiver
+        mContext.registerReceiver(broadcastReceiver, filter, null, null);
+
+        // Test unwanted intent(action = MOCK_ACTION2)
+        broadcastReceiver.reset();
+        waitForFilteredIntent(mContext, MOCK_ACTION2);
+        assertFalse(broadcastReceiver.hadReceivedBroadCast1());
+        assertFalse(broadcastReceiver.hadReceivedBroadCast2());
+
+        // Send wanted intent(action = MOCK_ACTION1)
+        broadcastReceiver.reset();
+        waitForFilteredIntent(mContext, MOCK_ACTION1);
+        assertTrue(broadcastReceiver.hadReceivedBroadCast1());
+        assertFalse(broadcastReceiver.hadReceivedBroadCast2());
+
+        mContext.unregisterReceiver(broadcastReceiver);
+    }
+
+    public void testAccessWallpaper() throws IOException, InterruptedException {
+        // set Wallpaper by context#setWallpaper(Bitmap)
+        Bitmap bitmap = Bitmap.createBitmap(20, 30, Bitmap.Config.RGB_565);
+        // Test getWallpaper
+        Drawable testDrawable = mContext.getWallpaper();
+        // Test peekWallpaper
+        Drawable testDrawable2 = mContext.peekWallpaper();
+
+        mContext.setWallpaper(bitmap);
+        mWallpaperChanged = true;
+        synchronized(this) {
+            wait(500);
+        }
+
+        assertNotSame(testDrawable, mContext.peekWallpaper());
+        assertNotNull(mContext.getWallpaper());
+        assertNotSame(testDrawable2, mContext.peekWallpaper());
+        assertNotNull(mContext.peekWallpaper());
+
+        // set Wallpaper by context#setWallpaper(InputStream)
+        mContext.clearWallpaper();
+
+        testDrawable = mContext.getWallpaper();
+        InputStream stream = mContext.getResources().openRawResource(R.drawable.scenery);
+
+        mContext.setWallpaper(stream);
+        synchronized (this) {
+            wait(1000);
+        }
+
+        assertNotSame(testDrawable, mContext.peekWallpaper());
+    }
+
+    public void testAccessDatabase() {
+        String DATABASE_NAME = "databasetest";
+        String DATABASE_NAME1 = DATABASE_NAME + "1";
+        String DATABASE_NAME2 = DATABASE_NAME + "2";
+        SQLiteDatabase mDatabase;
+        File mDatabaseFile;
+
+        SQLiteDatabase.CursorFactory factory = new SQLiteDatabase.CursorFactory() {
+            public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery,
+                                    String editTable, SQLiteQuery query) {
+                return new android.database.sqlite.SQLiteCursor(db, masterQuery, editTable, query) {
+                    @Override
+                    public boolean requery() {
+                        setSelectionArguments(new String[] { "2" });
+                        return super.requery();
+                    }
+                };
+            }
+        };
+
+        // FIXME: Move cleanup into tearDown()
+        for (String db : mContext.databaseList()) {
+            File f = mContext.getDatabasePath(db);
+            if (f.exists()) {
+                mContext.deleteDatabase(db);
+            }
+        }
+
+        // Test openOrCreateDatabase with null and actual factory
+        mDatabase = mContext.openOrCreateDatabase(DATABASE_NAME1,
+                Context.MODE_ENABLE_WRITE_AHEAD_LOGGING, factory);
+        assertNotNull(mDatabase);
+        mDatabase.close();
+        mDatabase = mContext.openOrCreateDatabase(DATABASE_NAME2,
+                Context.MODE_ENABLE_WRITE_AHEAD_LOGGING, factory);
+        assertNotNull(mDatabase);
+        mDatabase.close();
+
+        // Test getDatabasePath
+        File actualDBPath = mContext.getDatabasePath(DATABASE_NAME1);
+
+        // Test databaseList()
+        List<String> list = Arrays.asList(mContext.databaseList());
+        assertEquals(2, list.size());
+        assertTrue("1) database list: " + list, list.contains(DATABASE_NAME1));
+        assertTrue("2) database list: " + list, list.contains(DATABASE_NAME2));
+
+        // Test deleteDatabase()
+        for (int i = 1; i < 3; i++) {
+            mDatabaseFile = mContext.getDatabasePath(DATABASE_NAME + i);
+            assertTrue(mDatabaseFile.exists());
+            mContext.deleteDatabase(DATABASE_NAME + i);
+            mDatabaseFile = new File(actualDBPath, DATABASE_NAME + i);
+            assertFalse(mDatabaseFile.exists());
+        }
+    }
+
+    public void testEnforceUriPermission1() {
+        try {
+            Uri uri = Uri.parse("content://ctstest");
+            mContext.enforceUriPermission(uri, Binder.getCallingPid(),
+                    Binder.getCallingUid(), Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+                    "enforceUriPermission is not working without possessing an IPC.");
+            fail("enforceUriPermission is not working without possessing an IPC.");
+        } catch (SecurityException e) {
+            // If the function is OK, it should throw a SecurityException here because currently no
+            // IPC is handled by this process.
+        }
+    }
+
+    public void testEnforceUriPermission2() {
+        Uri uri = Uri.parse("content://ctstest");
+        try {
+            mContext.enforceUriPermission(uri, NOT_GRANTED_PERMISSION,
+                    NOT_GRANTED_PERMISSION, Binder.getCallingPid(), Binder.getCallingUid(),
+                    Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+                    "enforceUriPermission is not working without possessing an IPC.");
+            fail("enforceUriPermission is not working without possessing an IPC.");
+        } catch (SecurityException e) {
+            // If the function is ok, it should throw a SecurityException here because currently no
+            // IPC is handled by this process.
+        }
+    }
+
+    public void testGetPackageResourcePath() {
+        assertNotNull(mContext.getPackageResourcePath());
+    }
+
+    public void testStartActivity() {
+        Intent intent = new Intent(mContext, ContextCtsActivity.class);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        try {
+            mContext.startActivity(intent);
+            fail("Test startActivity should thow a ActivityNotFoundException here.");
+        } catch (ActivityNotFoundException e) {
+            // Because ContextWrapper is a wrapper class, so no need to test
+            // the details of the function's performance. Getting a result
+            // from the wrapped class is enough for testing.
+        }
+    }
+
+    public void testCreatePackageContext() throws PackageManager.NameNotFoundException {
+        Context actualContext = mContext.createPackageContext(getValidPackageName(),
+                Context.CONTEXT_IGNORE_SECURITY);
+
+        assertNotNull(actualContext);
+    }
+
+    /**
+     * Helper method to retrieve a valid application package name to use for tests.
+     */
+    protected String getValidPackageName() {
+        List<PackageInfo> packages = mContext.getPackageManager().getInstalledPackages(
+                PackageManager.GET_ACTIVITIES);
+        assertTrue(packages.size() >= 1);
+        return packages.get(0).packageName;
+    }
+
+    public void testGetMainLooper() {
+        assertNotNull(mContext.getMainLooper());
+    }
+
+    public void testGetApplicationContext() {
+        assertSame(mContext.getApplicationContext(), mContext.getApplicationContext());
+    }
+
+    public void testGetSharedPreferences() {
+        SharedPreferences sp;
+        SharedPreferences localSP;
+
+        sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+        String packageName = mContext.getPackageName();
+        localSP = mContext.getSharedPreferences(packageName + "_preferences",
+                Context.MODE_PRIVATE);
+        assertSame(sp, localSP);
+    }
+
+    public void testRevokeUriPermission() {
+        Uri uri = Uri.parse("contents://ctstest");
+        mContext.revokeUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+    }
+
+    public void testAccessService() throws InterruptedException {
+        MockContextService.reset();
+        bindExpectResult(mContext, new Intent(mContext, MockContextService.class));
+
+        // Check startService
+        assertTrue(MockContextService.hadCalledOnStart());
+        // Check bindService
+        assertTrue(MockContextService.hadCalledOnBind());
+
+        assertTrue(MockContextService.hadCalledOnDestory());
+        // Check unbinService
+        assertTrue(MockContextService.hadCalledOnUnbind());
+    }
+
+    public void testGetPackageCodePath() {
+        assertNotNull(mContext.getPackageCodePath());
+    }
+
+    public void testGetPackageName() {
+        assertEquals("android.content.cts", mContext.getPackageName());
+    }
+
+    public void testGetCacheDir() {
+        assertNotNull(mContext.getCacheDir());
+    }
+
+    public void testGetContentResolver() {
+        assertSame(mContext.getContentResolver(), mContext.getContentResolver());
+    }
+
+    public void testGetFileStreamPath() {
+        String TEST_FILENAME = "TestGetFileStreamPath";
+
+        // Test the path including the input filename
+        String fileStreamPath = mContext.getFileStreamPath(TEST_FILENAME).toString();
+        assertTrue(fileStreamPath.indexOf(TEST_FILENAME) >= 0);
+    }
+
+    public void testGetClassLoader() {
+        assertSame(mContext.getClassLoader(), mContext.getClassLoader());
+    }
+
+    public void testGetWallpaperDesiredMinimumHeightAndWidth() {
+        int height = mContext.getWallpaperDesiredMinimumHeight();
+        int width = mContext.getWallpaperDesiredMinimumWidth();
+
+        // returned value is <= 0, the caller should use the height of the
+        // default display instead.
+        // That is to say, the return values of desired minimumHeight and
+        // minimunWidth are at the same side of 0-dividing line.
+        assertTrue((height > 0 && width > 0) || (height <= 0 && width <= 0));
+    }
+
+    public void testAccessStickyBroadcast() throws InterruptedException {
+        ResultReceiver resultReceiver = new ResultReceiver();
+
+        Intent intent = new Intent(MOCK_STICKY_ACTION);
+        TestBroadcastReceiver stickyReceiver = new TestBroadcastReceiver();
+
+        mContext.sendStickyBroadcast(intent);
+
+        waitForReceiveBroadCast(resultReceiver);
+
+        assertEquals(intent.getAction(), mContext.registerReceiver(stickyReceiver,
+                new IntentFilter(MOCK_STICKY_ACTION)).getAction());
+
+        synchronized (mLockObj) {
+            mLockObj.wait(BROADCAST_TIMEOUT);
+        }
+
+        assertTrue("Receiver didn't make any response.", stickyReceiver.hadReceivedBroadCast());
+
+        mContext.unregisterReceiver(stickyReceiver);
+        mContext.removeStickyBroadcast(intent);
+
+        assertNull(mContext.registerReceiver(stickyReceiver,
+                new IntentFilter(MOCK_STICKY_ACTION)));
+        mContext.unregisterReceiver(stickyReceiver);
+    }
+
+    public void testCheckCallingOrSelfUriPermission() {
+        Uri uri = Uri.parse("content://ctstest");
+
+        int retValue = mContext.checkCallingOrSelfUriPermission(uri,
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        assertEquals(PackageManager.PERMISSION_DENIED, retValue);
+    }
+
+    public void testGrantUriPermission() {
+        mContext.grantUriPermission("com.android.mms", Uri.parse("contents://ctstest"),
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+    }
+
+    public void testCheckPermissionGranted() {
+        int returnValue = mContext.checkPermission(
+                GRANTED_PERMISSION, Process.myPid(), Process.myUid());
+        assertEquals(PackageManager.PERMISSION_GRANTED, returnValue);
+    }
+
+    public void testCheckPermissionNotGranted() {
+        int returnValue = mContext.checkPermission(
+                NOT_GRANTED_PERMISSION, Process.myPid(), Process.myUid());
+        assertEquals(PackageManager.PERMISSION_DENIED, returnValue);
+    }
+
+    public void testCheckPermissionRootUser() {
+        // Test with root user, everything will be granted.
+        int returnValue = mContext.checkPermission(NOT_GRANTED_PERMISSION, 1, ROOT_UID);
+        assertEquals(PackageManager.PERMISSION_GRANTED, returnValue);
+    }
+
+    public void testCheckPermissionInvalidRequest() {
+        // Test with null permission.
+        try {
+            int returnValue = mContext.checkPermission(null, 0, ROOT_UID);
+            fail("checkPermission should not accept null permission");
+        } catch (IllegalArgumentException e) {
+        }
+
+        // Test with invalid uid and included granted permission.
+        int returnValue = mContext.checkPermission(GRANTED_PERMISSION, 1, -11);
+        assertEquals(PackageManager.PERMISSION_DENIED, returnValue);
+    }
+
+    public void testCheckSelfPermissionGranted() {
+        int returnValue = mContext.checkSelfPermission(GRANTED_PERMISSION);
+        assertEquals(PackageManager.PERMISSION_GRANTED, returnValue);
+    }
+
+    public void testCheckSelfPermissionNotGranted() {
+        int returnValue = mContext.checkSelfPermission(NOT_GRANTED_PERMISSION);
+        assertEquals(PackageManager.PERMISSION_DENIED, returnValue);
+    }
+
+    public void testEnforcePermissionGranted() {
+        mContext.enforcePermission(
+                GRANTED_PERMISSION, Process.myPid(), Process.myUid(),
+                "permission isn't granted");
+    }
+
+    public void testEnforcePermissionNotGranted() {
+        try {
+            mContext.enforcePermission(
+                    NOT_GRANTED_PERMISSION, Process.myPid(), Process.myUid(),
+                    "permission isn't granted");
+            fail("Permission shouldn't be granted.");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    public void testCheckCallingOrSelfPermission_noIpc() {
+        // There's no ongoing Binder call, so this package's permissions are checked.
+        int retValue = mContext.checkCallingOrSelfPermission(GRANTED_PERMISSION);
+        assertEquals(PackageManager.PERMISSION_GRANTED, retValue);
+
+        retValue = mContext.checkCallingOrSelfPermission(NOT_GRANTED_PERMISSION);
+        assertEquals(PackageManager.PERMISSION_DENIED, retValue);
+    }
+
+    public void testCheckCallingOrSelfPermission_ipc() throws Exception {
+        bindBinderPermissionTestService();
+        try {
+            int retValue = mBinderPermissionTestService.doCheckCallingOrSelfPermission(
+                    GRANTED_PERMISSION);
+            assertEquals(PackageManager.PERMISSION_GRANTED, retValue);
+
+            retValue = mBinderPermissionTestService.doCheckCallingOrSelfPermission(
+                    NOT_GRANTED_PERMISSION);
+            assertEquals(PackageManager.PERMISSION_DENIED, retValue);
+        } finally {
+            mContext.unbindService(mBinderPermissionTestConnection);
+        }
+    }
+
+    public void testEnforceCallingOrSelfPermission_noIpc() {
+        // There's no ongoing Binder call, so this package's permissions are checked.
+        mContext.enforceCallingOrSelfPermission(
+                GRANTED_PERMISSION, "permission isn't granted");
+
+        try {
+            mContext.enforceCallingOrSelfPermission(
+                    NOT_GRANTED_PERMISSION, "permission isn't granted");
+            fail("Permission shouldn't be granted.");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    public void testEnforceCallingOrSelfPermission_ipc() throws Exception {
+        bindBinderPermissionTestService();
+        try {
+            mBinderPermissionTestService.doEnforceCallingOrSelfPermission(GRANTED_PERMISSION);
+
+            try {
+                mBinderPermissionTestService.doEnforceCallingOrSelfPermission(
+                        NOT_GRANTED_PERMISSION);
+                fail("Permission shouldn't be granted.");
+            } catch (SecurityException expected) {
+            }
+        } finally {
+            mContext.unbindService(mBinderPermissionTestConnection);
+        }
+    }
+
+    public void testCheckCallingPermission_noIpc() {
+        // Denied because no IPC is active.
+        int retValue = mContext.checkCallingPermission(GRANTED_PERMISSION);
+        assertEquals(PackageManager.PERMISSION_DENIED, retValue);
+    }
+
+    public void testEnforceCallingPermission_noIpc() {
+        try {
+            mContext.enforceCallingPermission(
+                    GRANTED_PERMISSION,
+                    "enforceCallingPermission is not working without possessing an IPC.");
+            fail("enforceCallingPermission is not working without possessing an IPC.");
+        } catch (SecurityException e) {
+            // Currently no IPC is handled by this process, this exception is expected
+        }
+    }
+
+    public void testEnforceCallingPermission_ipc() throws Exception {
+        bindBinderPermissionTestService();
+        try {
+            mBinderPermissionTestService.doEnforceCallingPermission(GRANTED_PERMISSION);
+
+            try {
+                mBinderPermissionTestService.doEnforceCallingPermission(NOT_GRANTED_PERMISSION);
+                fail("Permission shouldn't be granted.");
+            } catch (SecurityException expected) {
+            }
+        } finally {
+            mContext.unbindService(mBinderPermissionTestConnection);
+        }
+    }
+
+    public void testCheckCallingPermission_ipc() throws Exception {
+        bindBinderPermissionTestService();
+        try {
+            int returnValue = mBinderPermissionTestService.doCheckCallingPermission(
+                    GRANTED_PERMISSION);
+            assertEquals(PackageManager.PERMISSION_GRANTED, returnValue);
+
+            returnValue = mBinderPermissionTestService.doCheckCallingPermission(
+                    NOT_GRANTED_PERMISSION);
+            assertEquals(PackageManager.PERMISSION_DENIED, returnValue);
+        } finally {
+            mContext.unbindService(mBinderPermissionTestConnection);
+        }
+    }
+
+    private void bindBinderPermissionTestService() {
+        Intent intent = new Intent(mContext, IBinderPermissionTestService.class);
+        intent.setComponent(new ComponentName(
+                "com.android.cts", "com.android.cts.BinderPermissionTestService"));
+
+        mBinderPermissionTestConnection = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
+                mBinderPermissionTestService =
+                        IBinderPermissionTestService.Stub.asInterface(iBinder);
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName componentName) {
+            }
+        };
+
+        assertTrue("Service not bound", mContext.bindService(
+                intent, mBinderPermissionTestConnection, Context.BIND_AUTO_CREATE));
+
+        new PollingCheck(15 * 1000) {
+            protected boolean check() {
+                return mBinderPermissionTestService != null; // Service was bound.
+            }
+        }.run();
+    }
+
+    public void testCheckUriPermission1() {
+        Uri uri = Uri.parse("content://ctstest");
+
+        int retValue = mContext.checkUriPermission(uri, Binder.getCallingPid(), 0,
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        assertEquals(PackageManager.PERMISSION_GRANTED, retValue);
+
+        retValue = mContext.checkUriPermission(uri, Binder.getCallingPid(),
+                Binder.getCallingUid(), Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        assertEquals(PackageManager.PERMISSION_DENIED, retValue);
+    }
+
+    public void testCheckUriPermission2() {
+        Uri uri = Uri.parse("content://ctstest");
+
+        int retValue = mContext.checkUriPermission(uri, NOT_GRANTED_PERMISSION,
+                NOT_GRANTED_PERMISSION, Binder.getCallingPid(), 0,
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        assertEquals(PackageManager.PERMISSION_GRANTED, retValue);
+
+        retValue = mContext.checkUriPermission(uri, NOT_GRANTED_PERMISSION,
+                NOT_GRANTED_PERMISSION, Binder.getCallingPid(), Binder.getCallingUid(),
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        assertEquals(PackageManager.PERMISSION_DENIED, retValue);
+    }
+
+    public void testCheckCallingUriPermission() {
+        Uri uri = Uri.parse("content://ctstest");
+
+        int retValue = mContext.checkCallingUriPermission(uri,
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        assertEquals(PackageManager.PERMISSION_DENIED, retValue);
+    }
+
+    public void testEnforceCallingUriPermission() {
+        try {
+            Uri uri = Uri.parse("content://ctstest");
+            mContext.enforceCallingUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+                    "enforceCallingUriPermission is not working without possessing an IPC.");
+            fail("enforceCallingUriPermission is not working without possessing an IPC.");
+        } catch (SecurityException e) {
+            // If the function is OK, it should throw a SecurityException here because currently no
+            // IPC is handled by this process.
+        }
+    }
+
+    public void testGetDir() {
+        File dir = mContext.getDir("testpath", Context.MODE_PRIVATE);
+        assertNotNull(dir);
+        dir.delete();
+    }
+
+    public void testGetPackageManager() {
+        assertSame(mContext.getPackageManager(), mContext.getPackageManager());
+    }
+
+    public void testSendBroadcast1() throws InterruptedException {
+        final ResultReceiver receiver = new ResultReceiver();
+
+        registerBroadcastReceiver(receiver, new IntentFilter(ResultReceiver.MOCK_ACTION));
+
+        mContext.sendBroadcast(new Intent(ResultReceiver.MOCK_ACTION));
+
+        new PollingCheck(BROADCAST_TIMEOUT){
+            @Override
+            protected boolean check() {
+                return receiver.hasReceivedBroadCast();
+            }
+        }.run();
+    }
+
+    public void testSendBroadcast2() throws InterruptedException {
+        final ResultReceiver receiver = new ResultReceiver();
+
+        registerBroadcastReceiver(receiver, new IntentFilter(ResultReceiver.MOCK_ACTION));
+
+        mContext.sendBroadcast(new Intent(ResultReceiver.MOCK_ACTION), null);
+
+        new PollingCheck(BROADCAST_TIMEOUT){
+            @Override
+            protected boolean check() {
+                return receiver.hasReceivedBroadCast();
+            }
+        }.run();
+    }
+
+    public void testEnforceCallingOrSelfUriPermission() {
+        try {
+            Uri uri = Uri.parse("content://ctstest");
+            mContext.enforceCallingOrSelfUriPermission(uri,
+                    Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+                    "enforceCallingOrSelfUriPermission is not working without possessing an IPC.");
+            fail("enforceCallingOrSelfUriPermission is not working without possessing an IPC.");
+        } catch (SecurityException e) {
+            // If the function is OK, it should throw a SecurityException here because currently no
+            // IPC is handled by this process.
+        }
+    }
+
+    public void testGetAssets() {
+        assertSame(mContext.getAssets(), mContext.getAssets());
+    }
+
+    public void testGetResources() {
+        assertSame(mContext.getResources(), mContext.getResources());
+    }
+
+    public void testStartInstrumentation() {
+        // Use wrong name
+        ComponentName cn = new ComponentName("com.android",
+                "com.android.content.FalseLocalSampleInstrumentation");
+        assertNotNull(cn);
+        assertNotNull(mContext);
+        // If the target instrumentation is wrong, the function should return false.
+        assertFalse(mContext.startInstrumentation(cn, null, null));
+    }
+
+    private void bindExpectResult(Context context, Intent service)
+            throws InterruptedException {
+        if (service == null) {
+            fail("No service created!");
+        }
+        TestConnection conn = new TestConnection(true, false);
+
+        context.bindService(service, conn, Context.BIND_AUTO_CREATE);
+        context.startService(service);
+
+        // Wait for a short time, so the service related operations could be
+        // working.
+        synchronized (this) {
+            wait(2500);
+        }
+        // Test stop Service
+        assertTrue(context.stopService(service));
+        context.unbindService(conn);
+
+        synchronized (this) {
+            wait(1000);
+        }
+    }
+
+    private interface Condition {
+        public boolean onCondition();
+    }
+
+    private synchronized void waitForCondition(Condition con) throws InterruptedException {
+        // check the condition every 1 second until the condition is fulfilled
+        // and wait for 3 seconds at most
+        for (int i = 0; !con.onCondition() && i <= 3; i++) {
+            wait(1000);
+        }
+    }
+
+    private void waitForReceiveBroadCast(final ResultReceiver receiver)
+            throws InterruptedException {
+        Condition con = new Condition() {
+            public boolean onCondition() {
+                return receiver.hasReceivedBroadCast();
+            }
+        };
+        waitForCondition(con);
+    }
+
+    private void waitForFilteredIntent(Context context, final String action)
+            throws InterruptedException {
+        context.sendBroadcast(new Intent(action), null);
+
+        synchronized (mLockObj) {
+            mLockObj.wait(BROADCAST_TIMEOUT);
+        }
+    }
+
+    private final class TestBroadcastReceiver extends BroadcastReceiver {
+        boolean mHadReceivedBroadCast;
+        boolean mIsOrderedBroadcasts;
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            synchronized (this) {
+                if (mIsOrderedBroadcasts) {
+                    setResultCode(3);
+                    setResultData(ACTUAL_RESULT);
+                }
+
+                Bundle map = getResultExtras(false);
+                if (map != null) {
+                    map.remove(KEY_REMOVED);
+                    map.putString(KEY_ADDED, VALUE_ADDED);
+                }
+                mHadReceivedBroadCast = true;
+                this.notifyAll();
+            }
+
+            synchronized (mLockObj) {
+                mLockObj.notify();
+            }
+        }
+
+        boolean hadReceivedBroadCast() {
+            return mHadReceivedBroadCast;
+        }
+
+        void reset(){
+            mHadReceivedBroadCast = false;
+        }
+    }
+
+    private class FilteredReceiver extends BroadcastReceiver {
+        private boolean mHadReceivedBroadCast1 = false;
+        private boolean mHadReceivedBroadCast2 = false;
+
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (MOCK_ACTION1.equals(action)) {
+                mHadReceivedBroadCast1 = true;
+            } else if (MOCK_ACTION2.equals(action)) {
+                mHadReceivedBroadCast2 = true;
+            }
+
+            synchronized (mLockObj) {
+                mLockObj.notify();
+            }
+        }
+
+        public boolean hadReceivedBroadCast1() {
+            return mHadReceivedBroadCast1;
+        }
+
+        public boolean hadReceivedBroadCast2() {
+            return mHadReceivedBroadCast2;
+        }
+
+        public void reset(){
+            mHadReceivedBroadCast1 = false;
+            mHadReceivedBroadCast2 = false;
+        }
+    }
+
+    private class TestConnection implements ServiceConnection {
+        public TestConnection(boolean expectDisconnect, boolean setReporter) {
+        }
+
+        void setMonitor(boolean v) {
+        }
+
+        public void onServiceConnected(ComponentName name, IBinder service) {
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+        }
+    }
 }
diff --git a/tests/tests/content/src/android/content/cts/ContextWrapperTest.java b/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
index 408855d..abf3506 100644
--- a/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
+++ b/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
@@ -16,117 +16,29 @@
 
 package android.content.cts;
 
-import android.content.cts.R;
-
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.ContextWrapper;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.content.SharedPreferences;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteCursorDriver;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteQuery;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Process;
-import android.preference.PreferenceManager;
-import android.test.AndroidTestCase;
-import android.view.WindowManager;
-
-import com.android.compatibility.common.util.PollingCheck;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
 
 /**
  * Test {@link ContextWrapper}.
+ *
+ * <p>
+ * This class inherits most of its test methods from its parent ContextTest.
+ * Since ContextWrapper delegates its requests to the Context, the same test cases should pass
+ * for both Context and ContextWrapper.
+ *
+ * <p>
+ * There are some tests for ContextWrapper that don't make sense for Context - those are included
+ * in this class.
  */
-public class ContextWrapperTest extends AndroidTestCase {
-    private static final String ACTUAL_RESULT = "ResultSetByReceiver";
+public class ContextWrapperTest extends ContextTest {
 
-    private static final String INTIAL_RESULT = "IntialResult";
-
-    private static final String VALUE_ADDED = "ValueAdded";
-    private static final String KEY_ADDED = "AddedByReceiver";
-
-    private static final String VALUE_REMOVED = "ValueWillBeRemove";
-    private static final String KEY_REMOVED = "ToBeRemoved";
-
-    private static final String VALUE_KEPT = "ValueKept";
-    private static final String KEY_KEPT = "ToBeKept";
-
-    private static final String MOCK_STICKY_ACTION = "android.content.cts.ContextWrapperTest."
-        + "STICKY_BROADCAST_RESULT";
-
-    private static final String ACTION_BROADCAST_TESTORDER =
-        "android.content.cts.ContextWrapperTest.BROADCAST_TESTORDER";
-    private final static String MOCK_ACTION1 = ACTION_BROADCAST_TESTORDER + "1";
-    private final static String MOCK_ACTION2 = ACTION_BROADCAST_TESTORDER + "2";
-
-    // A permission that's granted to this test package.
-    public static final String GRANTED_PERMISSION = "android.permission.USE_CREDENTIALS";
-    // A permission that's not granted to this test package.
-    public static final String NOT_GRANTED_PERMISSION = "android.permission.HARDWARE_TEST";
-
-    private static final int BROADCAST_TIMEOUT = 10000;
-    private static final int ROOT_UID = 0;
-
-    private Context mContext;
-
-    private ContextWrapper mContextWrapper;
-    private Object mLockObj;
-
-    private ArrayList<BroadcastReceiver> mRegisteredReceiverList;
-
-    private boolean mWallpaperChanged;
-    private BitmapDrawable mOriginalWallpaper;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        mLockObj = new Object();
-        mContext = getContext();
-        mContextWrapper = new ContextWrapper(mContext);
-
-        mRegisteredReceiverList = new ArrayList<BroadcastReceiver>();
-
-        mOriginalWallpaper = (BitmapDrawable) mContextWrapper.getWallpaper();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        if (mWallpaperChanged) {
-            mContextWrapper.setWallpaper(mOriginalWallpaper.getBitmap());
-        }
-
-        for (BroadcastReceiver receiver : mRegisteredReceiverList) {
-            mContextWrapper.unregisterReceiver(receiver);
-        }
-
-        super.tearDown();
-    }
-
-    private void registerBroadcastReceiver(BroadcastReceiver receiver, IntentFilter filter) {
-        mContextWrapper.registerReceiver(receiver, filter);
-
-        mRegisteredReceiverList.add(receiver);
+    /**
+     * Returns the ContextWrapper object that's being tested.
+     */
+    protected Context getContextUnderTest() {
+        return new ContextWrapper(getContext());
     }
 
     public void testConstructor() {
@@ -136,344 +48,12 @@
         new ContextWrapper(null);
     }
 
-    public void testSendOrderedBroadcast1() throws InterruptedException {
-        final HighPriorityBroadcastReceiver highPriorityReceiver =
-                new HighPriorityBroadcastReceiver();
-        final LowPriorityBroadcastReceiver lowPriorityReceiver =
-            new LowPriorityBroadcastReceiver();
-
-        final IntentFilter filterHighPriority = new IntentFilter(ResultReceiver.MOCK_ACTION);
-        filterHighPriority.setPriority(1);
-        final IntentFilter filterLowPriority = new IntentFilter(ResultReceiver.MOCK_ACTION);
-        registerBroadcastReceiver(highPriorityReceiver, filterHighPriority);
-        registerBroadcastReceiver(lowPriorityReceiver, filterLowPriority);
-
-        final Intent broadcastIntent = new Intent(ResultReceiver.MOCK_ACTION);
-        broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        mContextWrapper.sendOrderedBroadcast(broadcastIntent, null);
-        new PollingCheck(BROADCAST_TIMEOUT) {
-            @Override
-            protected boolean check() {
-                return highPriorityReceiver.hasReceivedBroadCast()
-                        && !lowPriorityReceiver.hasReceivedBroadCast();
-            }
-        }.run();
-
-        synchronized (highPriorityReceiver) {
-            highPriorityReceiver.notify();
-        }
-
-        new PollingCheck(BROADCAST_TIMEOUT) {
-            @Override
-            protected boolean check() {
-                return highPriorityReceiver.hasReceivedBroadCast()
-                        && lowPriorityReceiver.hasReceivedBroadCast();
-            }
-        }.run();
-    }
-
-    public void testSendOrderedBroadcast2() throws InterruptedException {
-        final TestBroadcastReceiver broadcastReceiver = new TestBroadcastReceiver();
-        broadcastReceiver.mIsOrderedBroadcasts = true;
-
-        Bundle bundle = new Bundle();
-        bundle.putString(KEY_KEPT, VALUE_KEPT);
-        bundle.putString(KEY_REMOVED, VALUE_REMOVED);
-        Intent intent = new Intent(ResultReceiver.MOCK_ACTION);
-        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        mContextWrapper.sendOrderedBroadcast(intent, null, broadcastReceiver, null, 1,
-                INTIAL_RESULT, bundle);
-
-        synchronized (mLockObj) {
-            try {
-                mLockObj.wait(BROADCAST_TIMEOUT);
-            } catch (InterruptedException e) {
-                fail("unexpected InterruptedException.");
-            }
-        }
-
-        assertTrue("Receiver didn't make any response.", broadcastReceiver.hadReceivedBroadCast());
-        assertEquals("Incorrect code: " + broadcastReceiver.getResultCode(), 3,
-                broadcastReceiver.getResultCode());
-        assertEquals(ACTUAL_RESULT, broadcastReceiver.getResultData());
-        Bundle resultExtras = broadcastReceiver.getResultExtras(false);
-        assertEquals(VALUE_ADDED, resultExtras.getString(KEY_ADDED));
-        assertEquals(VALUE_KEPT, resultExtras.getString(KEY_KEPT));
-        assertNull(resultExtras.getString(KEY_REMOVED));
-    }
-
-    public void testRegisterReceiver1() throws InterruptedException {
-        final FilteredReceiver broadcastReceiver = new FilteredReceiver();
-        final IntentFilter filter = new IntentFilter(MOCK_ACTION1);
-
-        // Test registerReceiver
-        mContextWrapper.registerReceiver(broadcastReceiver, filter);
-
-        // Test unwanted intent(action = MOCK_ACTION2)
-        broadcastReceiver.reset();
-        waitForFilteredIntent(mContextWrapper, MOCK_ACTION2);
-        assertFalse(broadcastReceiver.hadReceivedBroadCast1());
-        assertFalse(broadcastReceiver.hadReceivedBroadCast2());
-
-        // Send wanted intent(action = MOCK_ACTION1)
-        broadcastReceiver.reset();
-        waitForFilteredIntent(mContextWrapper, MOCK_ACTION1);
-        assertTrue(broadcastReceiver.hadReceivedBroadCast1());
-        assertFalse(broadcastReceiver.hadReceivedBroadCast2());
-
-        mContextWrapper.unregisterReceiver(broadcastReceiver);
-
-        // Test unregisterReceiver
-        FilteredReceiver broadcastReceiver2 = new FilteredReceiver();
-        mContextWrapper.registerReceiver(broadcastReceiver2, filter);
-        mContextWrapper.unregisterReceiver(broadcastReceiver2);
-
-        // Test unwanted intent(action = MOCK_ACTION2)
-        broadcastReceiver2.reset();
-        waitForFilteredIntent(mContextWrapper, MOCK_ACTION2);
-        assertFalse(broadcastReceiver2.hadReceivedBroadCast1());
-        assertFalse(broadcastReceiver2.hadReceivedBroadCast2());
-
-        // Send wanted intent(action = MOCK_ACTION1), but the receiver is unregistered.
-        broadcastReceiver2.reset();
-        waitForFilteredIntent(mContextWrapper, MOCK_ACTION1);
-        assertFalse(broadcastReceiver2.hadReceivedBroadCast1());
-        assertFalse(broadcastReceiver2.hadReceivedBroadCast2());
-    }
-
-    public void testRegisterReceiver2() throws InterruptedException {
-        FilteredReceiver broadcastReceiver = new FilteredReceiver();
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(MOCK_ACTION1);
-
-        // Test registerReceiver
-        mContextWrapper.registerReceiver(broadcastReceiver, filter, null, null);
-
-        // Test unwanted intent(action = MOCK_ACTION2)
-        broadcastReceiver.reset();
-        waitForFilteredIntent(mContextWrapper, MOCK_ACTION2);
-        assertFalse(broadcastReceiver.hadReceivedBroadCast1());
-        assertFalse(broadcastReceiver.hadReceivedBroadCast2());
-
-        // Send wanted intent(action = MOCK_ACTION1)
-        broadcastReceiver.reset();
-        waitForFilteredIntent(mContextWrapper, MOCK_ACTION1);
-        assertTrue(broadcastReceiver.hadReceivedBroadCast1());
-        assertFalse(broadcastReceiver.hadReceivedBroadCast2());
-
-        mContextWrapper.unregisterReceiver(broadcastReceiver);
-    }
-
-    public void testAccessWallpaper() throws IOException, InterruptedException {
-        // set Wallpaper by contextWrapper#setWallpaper(Bitmap)
-        Bitmap bitmap = Bitmap.createBitmap(20, 30, Bitmap.Config.RGB_565);
-        // Test getWallpaper
-        Drawable testDrawable = mContextWrapper.getWallpaper();
-        // Test peekWallpaper
-        Drawable testDrawable2 = mContextWrapper.peekWallpaper();
-
-        mContextWrapper.setWallpaper(bitmap);
-        mWallpaperChanged = true;
-        synchronized(this) {
-            wait(500);
-        }
-
-        assertNotSame(testDrawable, mContextWrapper.peekWallpaper());
-        assertNotNull(mContextWrapper.getWallpaper());
-        assertNotSame(testDrawable2, mContextWrapper.peekWallpaper());
-        assertNotNull(mContextWrapper.peekWallpaper());
-
-        // set Wallpaper by contextWrapper#setWallpaper(InputStream)
-        mContextWrapper.clearWallpaper();
-
-        testDrawable = mContextWrapper.getWallpaper();
-        InputStream stream = mContextWrapper.getResources().openRawResource(R.drawable.scenery);
-
-        mContextWrapper.setWallpaper(stream);
-        synchronized (this) {
-            wait(1000);
-        }
-
-        assertNotSame(testDrawable, mContextWrapper.peekWallpaper());
-    }
-
-    public void testAccessDatabase() {
-        String DATABASE_NAME = "databasetest";
-        String DATABASE_NAME1 = DATABASE_NAME + "1";
-        String DATABASE_NAME2 = DATABASE_NAME + "2";
-        SQLiteDatabase mDatabase;
-        File mDatabaseFile;
-
-        SQLiteDatabase.CursorFactory factory = new SQLiteDatabase.CursorFactory() {
-            public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery,
-                    String editTable, SQLiteQuery query) {
-                return new android.database.sqlite.SQLiteCursor(db, masterQuery, editTable, query) {
-                    @Override
-                    public boolean requery() {
-                        setSelectionArguments(new String[] { "2" });
-                        return super.requery();
-                    }
-                };
-            }
-        };
-
-        // FIXME: Move cleanup into tearDown()
-        for (String db : mContextWrapper.databaseList()) {
-            File f = mContextWrapper.getDatabasePath(db);
-            if (f.exists()) {
-                mContextWrapper.deleteDatabase(db);
-            }
-        }
-
-        // Test openOrCreateDatabase with null and actual factory
-        mDatabase = mContextWrapper.openOrCreateDatabase(DATABASE_NAME1,
-                ContextWrapper.MODE_ENABLE_WRITE_AHEAD_LOGGING, factory);
-        assertNotNull(mDatabase);
-        mDatabase.close();
-        mDatabase = mContextWrapper.openOrCreateDatabase(DATABASE_NAME2,
-                ContextWrapper.MODE_ENABLE_WRITE_AHEAD_LOGGING, factory);
-        assertNotNull(mDatabase);
-        mDatabase.close();
-
-        // Test getDatabasePath
-        File actualDBPath = mContextWrapper.getDatabasePath(DATABASE_NAME1);
-
-        // Test databaseList()
-        List<String> list = Arrays.asList(mContextWrapper.databaseList());
-        assertEquals(2, list.size());
-        assertTrue("1) database list: " + list, list.contains(DATABASE_NAME1));
-        assertTrue("2) database list: " + list, list.contains(DATABASE_NAME2));
-
-        // Test deleteDatabase()
-        for (int i = 1; i < 3; i++) {
-            mDatabaseFile = mContextWrapper.getDatabasePath(DATABASE_NAME + i);
-            assertTrue(mDatabaseFile.exists());
-            mContextWrapper.deleteDatabase(DATABASE_NAME + i);
-            mDatabaseFile = new File(actualDBPath, DATABASE_NAME + i);
-            assertFalse(mDatabaseFile.exists());
-        }
-    }
-
-    public void testEnforceUriPermission1() {
-        try {
-            Uri uri = Uri.parse("content://ctstest");
-            mContextWrapper.enforceUriPermission(uri, Binder.getCallingPid(),
-                    Binder.getCallingUid(), Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
-                    "enforceUriPermission is not working without possessing an IPC.");
-            fail("enforceUriPermission is not working without possessing an IPC.");
-        } catch (SecurityException e) {
-            // If the function is OK, it should throw a SecurityException here because currently no
-            // IPC is handled by this process.
-        }
-    }
-
-    public void testEnforceUriPermission2() {
-        Uri uri = Uri.parse("content://ctstest");
-        try {
-            mContextWrapper.enforceUriPermission(uri, NOT_GRANTED_PERMISSION,
-                    NOT_GRANTED_PERMISSION, Binder.getCallingPid(), Binder.getCallingUid(),
-                    Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
-                    "enforceUriPermission is not working without possessing an IPC.");
-            fail("enforceUriPermission is not working without possessing an IPC.");
-        } catch (SecurityException e) {
-            // If the function is ok, it should throw a SecurityException here because currently no
-            // IPC is handled by this process.
-        }
-    }
-
-    public void testGetPackageResourcePath() {
-        assertNotNull(mContextWrapper.getPackageResourcePath());
-    }
-
-    public void testStartActivity() {
-        Intent intent = new Intent(mContext, ContextWrapperCtsActivity.class);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        try {
-            mContextWrapper.startActivity(intent);
-            fail("Test startActivity should thow a ActivityNotFoundException here.");
-        } catch (ActivityNotFoundException e) {
-            // Because ContextWrapper is a wrapper class, so no need to test
-            // the details of the function's performance. Getting a result
-            // from the wrapped class is enough for testing.
-        }
-    }
-
-    public void testCreatePackageContext() throws PackageManager.NameNotFoundException {
-        Context actualContext = mContextWrapper.createPackageContext(getValidPackageName(),
-                Context.CONTEXT_IGNORE_SECURITY);
-
-        assertNotNull(actualContext);
-    }
-
-    /**
-     * Helper method to retrieve a valid application package name to use for tests.
-     */
-    private String getValidPackageName() {
-        List<PackageInfo> packages = mContextWrapper.getPackageManager().getInstalledPackages(
-                PackageManager.GET_ACTIVITIES);
-        assertTrue(packages.size() >= 1);
-        return packages.get(0).packageName;
-    }
-
-    public void testGetMainLooper() {
-        assertNotNull(mContextWrapper.getMainLooper());
-    }
-
-    public void testGetApplicationContext() {
-        assertSame(mContext.getApplicationContext(), mContextWrapper.getApplicationContext());
-    }
-
-    public void testGetSharedPreferences() {
-        SharedPreferences sp;
-        SharedPreferences localSP;
-
-        sp = PreferenceManager.getDefaultSharedPreferences(mContext);
-        String packageName = mContextWrapper.getPackageName();
-        localSP = mContextWrapper.getSharedPreferences(packageName + "_preferences",
-                Context.MODE_PRIVATE);
-        assertSame(sp, localSP);
-    }
-
-    public void testRevokeUriPermission() {
-        Uri uri = Uri.parse("contents://ctstest");
-        mContextWrapper.revokeUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-    }
-
-    public void testAccessService() throws InterruptedException {
-        MockContextWrapperService.reset();
-        bindExpectResult(mContextWrapper, new Intent(mContext, MockContextWrapperService.class));
-
-        // Check startService
-        assertTrue(MockContextWrapperService.hadCalledOnStart());
-        // Check bindService
-        assertTrue(MockContextWrapperService.hadCalledOnBind());
-
-        assertTrue(MockContextWrapperService.hadCalledOnDestory());
-        // Check unbinService
-        assertTrue(MockContextWrapperService.hadCalledOnUnbind());
-    }
-
-    public void testGetPackageCodePath() {
-        assertNotNull(mContextWrapper.getPackageCodePath());
-    }
-
-    public void testGetPackageName() {
-        assertEquals("android.content.cts", mContextWrapper.getPackageName());
-    }
-
-    public void testGetCacheDir() {
-        assertNotNull(mContextWrapper.getCacheDir());
-    }
-
-    public void testGetContentResolver() {
-        assertSame(mContext.getContentResolver(), mContextWrapper.getContentResolver());
-    }
-
     public void testAccessBaseContext() throws PackageManager.NameNotFoundException {
-        MockContextWrapper testContextWrapper = new MockContextWrapper(mContext);
+        Context context = getContext();
+        MockContextWrapper testContextWrapper = new MockContextWrapper(context);
 
         // Test getBaseContext()
-        assertSame(mContext, testContextWrapper.getBaseContext());
+        assertSame(context, testContextWrapper.getBaseContext());
 
         Context secondContext = testContextWrapper.createPackageContext(getValidPackageName(),
                 Context.CONTEXT_IGNORE_SECURITY);
@@ -487,352 +67,7 @@
         }
     }
 
-    public void testGetFileStreamPath() {
-        String TEST_FILENAME = "TestGetFileStreamPath";
-
-        // Test the path including the input filename
-        String fileStreamPath = mContextWrapper.getFileStreamPath(TEST_FILENAME).toString();
-        assertTrue(fileStreamPath.indexOf(TEST_FILENAME) >= 0);
-    }
-
-    public void testGetClassLoader() {
-        assertSame(mContext.getClassLoader(), mContextWrapper.getClassLoader());
-    }
-
-    public void testGetWallpaperDesiredMinimumHeightAndWidth() {
-        int height = mContextWrapper.getWallpaperDesiredMinimumHeight();
-        int width = mContextWrapper.getWallpaperDesiredMinimumWidth();
-
-        // returned value is <= 0, the caller should use the height of the
-        // default display instead.
-        // That is to say, the return values of desired minimumHeight and
-        // minimunWidth are at the same side of 0-dividing line.
-        assertTrue((height > 0 && width > 0) || (height <= 0 && width <= 0));
-    }
-
-    public void testAccessStickyBroadcast() throws InterruptedException {
-        ResultReceiver resultReceiver = new ResultReceiver();
-
-        Intent intent = new Intent(MOCK_STICKY_ACTION);
-        TestBroadcastReceiver stickyReceiver = new TestBroadcastReceiver();
-
-        mContextWrapper.sendStickyBroadcast(intent);
-
-        waitForReceiveBroadCast(resultReceiver);
-
-        assertEquals(intent.getAction(), mContextWrapper.registerReceiver(stickyReceiver,
-                new IntentFilter(MOCK_STICKY_ACTION)).getAction());
-
-        synchronized (mLockObj) {
-            mLockObj.wait(BROADCAST_TIMEOUT);
-        }
-
-        assertTrue("Receiver didn't make any response.", stickyReceiver.hadReceivedBroadCast());
-
-        mContextWrapper.unregisterReceiver(stickyReceiver);
-        mContextWrapper.removeStickyBroadcast(intent);
-
-        assertNull(mContextWrapper.registerReceiver(stickyReceiver,
-                new IntentFilter(MOCK_STICKY_ACTION)));
-        mContextWrapper.unregisterReceiver(stickyReceiver);
-    }
-
-    public void testCheckCallingOrSelfUriPermission() {
-        Uri uri = Uri.parse("content://ctstest");
-
-        int retValue = mContextWrapper.checkCallingOrSelfUriPermission(uri,
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-        assertEquals(PackageManager.PERMISSION_DENIED, retValue);
-    }
-
-    public void testGrantUriPermission() {
-        mContextWrapper.grantUriPermission("com.android.mms", Uri.parse("contents://ctstest"),
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-    }
-
-    public void testCheckPermissionGranted() {
-        int returnValue = mContextWrapper.checkPermission(
-                GRANTED_PERMISSION, Process.myPid(), Process.myUid());
-        assertEquals(PackageManager.PERMISSION_GRANTED, returnValue);
-    }
-
-    public void testCheckPermissionNotGranted() {
-        int returnValue = mContextWrapper.checkPermission(
-                NOT_GRANTED_PERMISSION, Process.myPid(), Process.myUid());
-        assertEquals(PackageManager.PERMISSION_DENIED, returnValue);
-    }
-
-    public void testCheckPermissionRootUser() {
-        // Test with root user, everything will be granted.
-        int returnValue = mContextWrapper.checkPermission(NOT_GRANTED_PERMISSION, 1, ROOT_UID);
-        assertEquals(PackageManager.PERMISSION_GRANTED, returnValue);
-    }
-
-    public void testCheckPermissionInvalidRequest() {
-        // Test with null permission.
-        try {
-            int returnValue = mContextWrapper.checkPermission(null, 0, ROOT_UID);
-            fail("checkPermission should not accept null permission");
-        } catch (IllegalArgumentException e) {
-        }
-
-        // Test with invalid uid and included granted permission.
-        int returnValue = mContextWrapper.checkPermission(GRANTED_PERMISSION, 1, -11);
-        assertEquals(PackageManager.PERMISSION_DENIED, returnValue);
-    }
-
-    public void testCheckSelfPermissionGranted() {
-        int returnValue = mContextWrapper.checkSelfPermission(GRANTED_PERMISSION);
-        assertEquals(PackageManager.PERMISSION_GRANTED, returnValue);
-    }
-
-    public void testCheckSelfPermissionNotGranted() {
-        int returnValue = mContextWrapper.checkSelfPermission(NOT_GRANTED_PERMISSION);
-        assertEquals(PackageManager.PERMISSION_DENIED, returnValue);
-    }
-
-    public void testEnforcePermissionGranted() {
-        mContextWrapper.enforcePermission(
-                GRANTED_PERMISSION, Process.myPid(), Process.myUid(),
-                "permission isn't granted");
-    }
-
-    public void testEnforcePermissionNotGranted() {
-        try {
-            mContextWrapper.enforcePermission(
-                    NOT_GRANTED_PERMISSION, Process.myPid(), Process.myUid(),
-                    "permission isn't granted");
-            fail("Permission shouldn't be granted.");
-        } catch (SecurityException expected) {
-        }
-    }
-
-    public void testCheckCallingOrSelfPermissionGranted() {
-        int retValue = mContextWrapper.checkCallingOrSelfPermission(GRANTED_PERMISSION);
-        assertEquals(PackageManager.PERMISSION_GRANTED, retValue);
-    }
-
-    public void testEnforceCallingOrSelfPermissionGranted() {
-        mContextWrapper.enforceCallingOrSelfPermission(
-                GRANTED_PERMISSION, "permission isn't granted");
-    }
-
-    public void testEnforceCallingOrSelfPermissionNotGranted() {
-        try {
-            mContextWrapper.enforceCallingOrSelfPermission(
-                    NOT_GRANTED_PERMISSION, "permission isn't granted");
-            fail("Permission shouldn't be granted.");
-        } catch (SecurityException expected) {
-        }
-    }
-
-    public void testCheckCallingPermission() {
-        // Denied because no IPC is active.
-        int retValue = mContextWrapper.checkCallingPermission(GRANTED_PERMISSION);
-        assertEquals(PackageManager.PERMISSION_DENIED, retValue);
-    }
-
-    public void testEnforceCallingPermission() {
-        try {
-            mContextWrapper.enforceCallingPermission(
-                    GRANTED_PERMISSION,
-                    "enforceCallingPermission is not working without possessing an IPC.");
-            fail("enforceCallingPermission is not working without possessing an IPC.");
-        } catch (SecurityException e) {
-            // Currently no IPC is handled by this process, this exception is expected
-        }
-    }
-
-    public void testCheckUriPermission1() {
-        Uri uri = Uri.parse("content://ctstest");
-
-        int retValue = mContextWrapper.checkUriPermission(uri, Binder.getCallingPid(), 0,
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-        assertEquals(PackageManager.PERMISSION_GRANTED, retValue);
-
-        retValue = mContextWrapper.checkUriPermission(uri, Binder.getCallingPid(),
-                Binder.getCallingUid(), Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-        assertEquals(PackageManager.PERMISSION_DENIED, retValue);
-    }
-
-    public void testCheckUriPermission2() {
-        Uri uri = Uri.parse("content://ctstest");
-
-        int retValue = mContextWrapper.checkUriPermission(uri, NOT_GRANTED_PERMISSION,
-                NOT_GRANTED_PERMISSION, Binder.getCallingPid(), 0,
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-        assertEquals(PackageManager.PERMISSION_GRANTED, retValue);
-
-        retValue = mContextWrapper.checkUriPermission(uri, NOT_GRANTED_PERMISSION,
-                NOT_GRANTED_PERMISSION, Binder.getCallingPid(), Binder.getCallingUid(),
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-        assertEquals(PackageManager.PERMISSION_DENIED, retValue);
-    }
-
-    public void testCheckCallingUriPermission() {
-        Uri uri = Uri.parse("content://ctstest");
-
-        int retValue = mContextWrapper.checkCallingUriPermission(uri,
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-        assertEquals(PackageManager.PERMISSION_DENIED, retValue);
-    }
-
-    public void testEnforceCallingUriPermission() {
-        try {
-            Uri uri = Uri.parse("content://ctstest");
-            mContextWrapper.enforceCallingUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
-                    "enforceCallingUriPermission is not working without possessing an IPC.");
-            fail("enforceCallingUriPermission is not working without possessing an IPC.");
-        } catch (SecurityException e) {
-            // If the function is OK, it should throw a SecurityException here because currently no
-            // IPC is handled by this process.
-        }
-    }
-
-    public void testGetDir() {
-        File dir = mContextWrapper.getDir("testpath", Context.MODE_PRIVATE);
-        assertNotNull(dir);
-        dir.delete();
-    }
-
-    public void testGetPackageManager() {
-        assertSame(mContext.getPackageManager(), mContextWrapper.getPackageManager());
-    }
-
-    public void testSendBroadcast1() throws InterruptedException {
-        final ResultReceiver receiver = new ResultReceiver();
-
-        registerBroadcastReceiver(receiver, new IntentFilter(ResultReceiver.MOCK_ACTION));
-
-        mContextWrapper.sendBroadcast(new Intent(ResultReceiver.MOCK_ACTION));
-
-        new PollingCheck(BROADCAST_TIMEOUT){
-            @Override
-            protected boolean check() {
-                return receiver.hasReceivedBroadCast();
-            }
-        }.run();
-    }
-
-    public void testSendBroadcast2() throws InterruptedException {
-        final ResultReceiver receiver = new ResultReceiver();
-
-        registerBroadcastReceiver(receiver, new IntentFilter(ResultReceiver.MOCK_ACTION));
-
-        mContextWrapper.sendBroadcast(new Intent(ResultReceiver.MOCK_ACTION), null);
-
-        new PollingCheck(BROADCAST_TIMEOUT){
-            @Override
-            protected boolean check() {
-                return receiver.hasReceivedBroadCast();
-            }
-        }.run();
-    }
-
-    public void testEnforceCallingOrSelfUriPermission() {
-        try {
-            Uri uri = Uri.parse("content://ctstest");
-            mContextWrapper.enforceCallingOrSelfUriPermission(uri,
-                    Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
-                    "enforceCallingOrSelfUriPermission is not working without possessing an IPC.");
-            fail("enforceCallingOrSelfUriPermission is not working without possessing an IPC.");
-        } catch (SecurityException e) {
-            // If the function is OK, it should throw a SecurityException here because currently no
-            // IPC is handled by this process.
-        }
-    }
-
-    public void testGetSystemService() {
-        // Test invalid service name
-        assertNull(mContextWrapper.getSystemService("invalid"));
-
-        // Test valid service name
-        assertNotNull(mContextWrapper.getSystemService(Context.WINDOW_SERVICE));
-    }
-
-    public void testGetSystemServiceByClass() {
-        // Test invalid service class
-        assertNull(mContextWrapper.getSystemService(Object.class));
-
-        // Test valid service name
-        assertNotNull(mContextWrapper.getSystemService(WindowManager.class));
-        assertEquals(mContextWrapper.getSystemService(Context.WINDOW_SERVICE),
-                mContextWrapper.getSystemService(WindowManager.class));
-    }
-
-    public void testGetAssets() {
-        assertSame(mContext.getAssets(), mContextWrapper.getAssets());
-    }
-
-    public void testGetResources() {
-        assertSame(mContext.getResources(), mContextWrapper.getResources());
-    }
-
-    public void testStartInstrumentation() {
-        // Use wrong name
-        ComponentName cn = new ComponentName("com.android",
-                "com.android.content.FalseLocalSampleInstrumentation");
-        assertNotNull(cn);
-        assertNotNull(mContextWrapper);
-        // If the target instrumentation is wrong, the function should return false.
-        assertFalse(mContextWrapper.startInstrumentation(cn, null, null));
-    }
-
-    private void bindExpectResult(Context contextWrapper, Intent service)
-            throws InterruptedException {
-        if (service == null) {
-            fail("No service created!");
-        }
-        TestConnection conn = new TestConnection(true, false);
-
-        contextWrapper.bindService(service, conn, Context.BIND_AUTO_CREATE);
-        contextWrapper.startService(service);
-
-        // Wait for a short time, so the service related operations could be
-        // working.
-        synchronized (this) {
-            wait(2500);
-        }
-        // Test stop Service
-        assertTrue(contextWrapper.stopService(service));
-        contextWrapper.unbindService(conn);
-
-        synchronized (this) {
-            wait(1000);
-        }
-    }
-
-    private interface Condition {
-        public boolean onCondition();
-    }
-
-    private synchronized void waitForCondition(Condition con) throws InterruptedException {
-        // check the condition every 1 second until the condition is fulfilled
-        // and wait for 3 seconds at most
-        for (int i = 0; !con.onCondition() && i <= 3; i++) {
-            wait(1000);
-        }
-    }
-
-    private void waitForReceiveBroadCast(final ResultReceiver receiver)
-            throws InterruptedException {
-        Condition con = new Condition() {
-            public boolean onCondition() {
-                return receiver.hasReceivedBroadCast();
-            }
-        };
-        waitForCondition(con);
-    }
-
-    private void waitForFilteredIntent(ContextWrapper contextWrapper, final String action)
-            throws InterruptedException {
-        contextWrapper.sendBroadcast(new Intent(action), null);
-
-        synchronized (mLockObj) {
-            mLockObj.wait(BROADCAST_TIMEOUT);
-        }
-    }
-
+    // TODO: this mock seems unnecessary. Remove it, and just use ContextWrapper?
     private static final class MockContextWrapper extends ContextWrapper {
         public MockContextWrapper(Context base) {
             super(base);
@@ -843,84 +78,4 @@
             super.attachBaseContext(base);
         }
     }
-
-    private final class TestBroadcastReceiver extends BroadcastReceiver {
-        boolean mHadReceivedBroadCast;
-        boolean mIsOrderedBroadcasts;
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            synchronized (this) {
-                if (mIsOrderedBroadcasts) {
-                    setResultCode(3);
-                    setResultData(ACTUAL_RESULT);
-                }
-
-                Bundle map = getResultExtras(false);
-                if (map != null) {
-                    map.remove(KEY_REMOVED);
-                    map.putString(KEY_ADDED, VALUE_ADDED);
-                }
-                mHadReceivedBroadCast = true;
-                this.notifyAll();
-            }
-
-            synchronized (mLockObj) {
-                mLockObj.notify();
-            }
-        }
-
-        boolean hadReceivedBroadCast() {
-            return mHadReceivedBroadCast;
-        }
-
-        void reset(){
-            mHadReceivedBroadCast = false;
-        }
-    }
-
-    private class FilteredReceiver extends BroadcastReceiver {
-        private boolean mHadReceivedBroadCast1 = false;
-        private boolean mHadReceivedBroadCast2 = false;
-
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (MOCK_ACTION1.equals(action)) {
-                mHadReceivedBroadCast1 = true;
-            } else if (MOCK_ACTION2.equals(action)) {
-                mHadReceivedBroadCast2 = true;
-            }
-
-            synchronized (mLockObj) {
-                mLockObj.notify();
-            }
-        }
-
-        public boolean hadReceivedBroadCast1() {
-            return mHadReceivedBroadCast1;
-        }
-
-        public boolean hadReceivedBroadCast2() {
-            return mHadReceivedBroadCast2;
-        }
-
-        public void reset(){
-            mHadReceivedBroadCast1 = false;
-            mHadReceivedBroadCast2 = false;
-        }
-    }
-
-    private class TestConnection implements ServiceConnection {
-        public TestConnection(boolean expectDisconnect, boolean setReporter) {
-        }
-
-        void setMonitor(boolean v) {
-        }
-
-        public void onServiceConnected(ComponentName name, IBinder service) {
-        }
-
-        public void onServiceDisconnected(ComponentName name) {
-        }
-    }
 }
diff --git a/tests/tests/content/src/android/content/cts/MockContextWrapperService.java b/tests/tests/content/src/android/content/cts/MockContextService.java
similarity index 94%
rename from tests/tests/content/src/android/content/cts/MockContextWrapperService.java
rename to tests/tests/content/src/android/content/cts/MockContextService.java
index ef4b09a..f634f68 100644
--- a/tests/tests/content/src/android/content/cts/MockContextWrapperService.java
+++ b/tests/tests/content/src/android/content/cts/MockContextService.java
@@ -25,11 +25,9 @@
 import android.os.Message;
 
 /**
- * This class is used for {@link ContextWrapper}
- *
- * @see ContextWrapperTest
+ * This class is used for {@link ContextTest}.
  */
-public class MockContextWrapperService extends Service {
+public class MockContextService extends Service {
     private static boolean mHadCalledOnBind = false;
     private static boolean mHadCalledOnUnbind = false;
     private static boolean mHadCalledOnStart = false;
diff --git a/tests/tests/content/src/android/content/cts/ResultReceiver.java b/tests/tests/content/src/android/content/cts/ResultReceiver.java
index 1b80774..8b9cf67 100644
--- a/tests/tests/content/src/android/content/cts/ResultReceiver.java
+++ b/tests/tests/content/src/android/content/cts/ResultReceiver.java
@@ -21,13 +21,11 @@
 import android.content.Intent;
 
 /**
- * This class is used for testing android.content.ContentWrapper.
- *
- * @see ContextWrapperTest
+ * This class is used for {@link ContextTest}.
  */
 public class ResultReceiver extends BroadcastReceiver {
     public static final String MOCK_ACTION =
-        "android.content.cts.ContextWrapperTest.BROADCAST_RESULT";
+        "android.content.cts.ContextTest.BROADCAST_RESULT";
 
     private boolean mReceivedBroadCast;
 
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 60a7abd..4ddafbe 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
@@ -18,22 +18,37 @@
 
 import android.content.cts.R;
 
+import static android.content.pm.ApplicationInfo.FLAG_HAS_CODE;
+import static android.content.pm.ApplicationInfo.FLAG_INSTALLED;
+import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
+import static android.content.pm.PackageManager.GET_ACTIVITIES;
+import static android.content.pm.PackageManager.GET_META_DATA;
+import static android.content.pm.PackageManager.GET_PERMISSIONS;
+import static android.content.pm.PackageManager.GET_PROVIDERS;
+import static android.content.pm.PackageManager.GET_RECEIVERS;
+import static android.content.pm.PackageManager.GET_SERVICES;
+
+import static com.google.common.truth.Truth.assertThat;
 
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
 import android.test.AndroidTestCase;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
 
@@ -45,6 +60,8 @@
 public class PackageManagerTest extends AndroidTestCase {
     private PackageManager mPackageManager;
     private static final String PACKAGE_NAME = "android.content.cts";
+    private static final String CONTENT_PKG_NAME = "android.content.cts";
+    private static final String APPLICATION_NAME = "android.content.cts.MockApplication";
     private static final String ACTIVITY_ACTION_NAME = "android.intent.action.PMTEST";
     private static final String MAIN_ACTION_NAME = "android.intent.action.MAIN";
     private static final String SERVICE_ACTION_NAME =
@@ -55,6 +72,8 @@
     private static final String SERVICE_NAME = "android.content.pm.cts.TestPmService";
     private static final String RECEIVER_NAME = "android.content.pm.cts.PmTestReceiver";
     private static final String INSTRUMENT_NAME = "android.content.pm.cts.TestPmInstrumentation";
+    private static final String CALL_ABROAD_PERMISSION_NAME =
+            "android.content.cts.CALL_ABROAD_PERMISSION";
     private static final String PROVIDER_NAME = "android.content.cts.MockContentProvider";
     private static final String PERMISSIONGROUP_NAME = "android.permission-group.COST_MONEY";
 
@@ -110,7 +129,7 @@
         String testPermissionsGroup = "android.permission-group.COST_MONEY";
         List<PermissionInfo> permissions = mPackageManager.queryPermissionsByGroup(
                 testPermissionsGroup, PackageManager.GET_META_DATA);
-        checkPermissionInfoName("android.content.cts.CALL_ABROAD_PERMISSION", permissions);
+        checkPermissionInfoName(CALL_ABROAD_PERMISSION_NAME, permissions);
 
         ApplicationInfo appInfo = mPackageManager.getApplicationInfo(PACKAGE_NAME, 0);
         List<ProviderInfo> providers = mPackageManager.queryContentProviders(PACKAGE_NAME,
@@ -509,4 +528,111 @@
         assertEquals(null, result[1]);
         assertEquals("com.android.cts.ctsshim", result[2]);
     }
+
+    public void testGetInstalledPackages() throws Exception {
+        List<PackageInfo> pkgs = mPackageManager.getInstalledPackages(GET_META_DATA
+                | GET_PERMISSIONS | GET_ACTIVITIES | GET_PROVIDERS | GET_SERVICES | GET_RECEIVERS);
+
+        PackageInfo pkgInfo = findPackageOrFail(pkgs, PACKAGE_NAME);
+
+        // Check metadata
+        ApplicationInfo appInfo = pkgInfo.applicationInfo;
+        assertEquals(APPLICATION_NAME, appInfo.name);
+        assertEquals("Android TestCase", appInfo.loadLabel(mPackageManager));
+        assertEquals(PACKAGE_NAME, appInfo.packageName);
+        assertTrue(appInfo.enabled);
+        // The process name defaults to the package name when not set.
+        assertEquals(PACKAGE_NAME, appInfo.processName);
+        assertEquals(0, appInfo.flags & FLAG_SYSTEM);
+        assertEquals(FLAG_INSTALLED, appInfo.flags & FLAG_INSTALLED);
+        assertEquals(FLAG_HAS_CODE, appInfo.flags & FLAG_HAS_CODE);
+
+        // Check required permissions
+        List<String> requestedPermissions = Arrays.asList(pkgInfo.requestedPermissions);
+        assertThat(requestedPermissions).containsAllOf(
+                "android.permission.MANAGE_ACCOUNTS",
+                "android.permission.ACCESS_NETWORK_STATE",
+                "android.content.cts.permission.TEST_GRANTED");
+
+        // Check declared permissions
+        PermissionInfo declaredPermission = (PermissionInfo) findPackageItemOrFail(
+                pkgInfo.permissions, CALL_ABROAD_PERMISSION_NAME);
+        assertEquals("Call abroad", declaredPermission.loadLabel(mPackageManager));
+        assertEquals(PERMISSIONGROUP_NAME, declaredPermission.group);
+        assertEquals(PermissionInfo.PROTECTION_NORMAL, declaredPermission.protectionLevel);
+
+        // Check activities
+        ActivityInfo activity = findPackageItemOrFail(pkgInfo.activities, ACTIVITY_NAME);
+        assertTrue(activity.enabled);
+        assertTrue(activity.exported); // Has intent filters - export by default.
+        assertEquals(PACKAGE_NAME, activity.taskAffinity);
+        assertEquals(ActivityInfo.LAUNCH_SINGLE_TOP, activity.launchMode);
+
+        // Check services
+        ServiceInfo service = findPackageItemOrFail(pkgInfo.services, SERVICE_NAME);
+        assertTrue(service.enabled);
+        assertTrue(service.exported); // Has intent filters - export by default.
+        assertEquals(PACKAGE_NAME, service.packageName);
+        assertEquals(CALL_ABROAD_PERMISSION_NAME, service.permission);
+
+        // Check ContentProviders
+        ProviderInfo provider = findPackageItemOrFail(pkgInfo.providers, PROVIDER_NAME);
+        assertTrue(provider.enabled);
+        assertFalse(provider.exported); // Don't export by default.
+        assertEquals(PACKAGE_NAME, provider.packageName);
+        assertEquals("ctstest", provider.authority);
+
+        // Check Receivers
+        ActivityInfo receiver = findPackageItemOrFail(pkgInfo.receivers, RECEIVER_NAME);
+        assertTrue(receiver.enabled);
+        assertTrue(receiver.exported); // Has intent filters - export by default.
+        assertEquals(PACKAGE_NAME, receiver.packageName);
+    }
+
+    // Tests that other packages can be queried.
+    public void testGetInstalledPackages_OtherPackages() throws Exception {
+        List<PackageInfo> pkgInfos = mPackageManager.getInstalledPackages(0);
+
+        // Check a normal package.
+        PackageInfo pkgInfo = findPackageOrFail(pkgInfos, "com.android.cts.stub"); // A test package
+        assertEquals(0, pkgInfo.applicationInfo.flags & FLAG_SYSTEM);
+
+        // Check a system package.
+        pkgInfo = findPackageOrFail(pkgInfos, "android");
+        assertEquals(FLAG_SYSTEM, pkgInfo.applicationInfo.flags & FLAG_SYSTEM);
+    }
+
+    public void testGetInstalledApplications() throws Exception {
+        List<ApplicationInfo> apps = mPackageManager.getInstalledApplications(GET_META_DATA);
+
+        ApplicationInfo app = findPackageItemOrFail(
+                apps.toArray(new ApplicationInfo[] {}), APPLICATION_NAME);
+
+        assertEquals(APPLICATION_NAME, app.name);
+        assertEquals("Android TestCase", app.loadLabel(mPackageManager));
+        assertEquals(PACKAGE_NAME, app.packageName);
+        assertTrue(app.enabled);
+        // The process name defaults to the package name when not set.
+        assertEquals(PACKAGE_NAME, app.processName);
+    }
+
+    private PackageInfo findPackageOrFail(List<PackageInfo> pkgInfos, String pkgName) {
+        for (PackageInfo pkgInfo : pkgInfos) {
+            if (pkgName.equals(pkgInfo.packageName)) {
+                return pkgInfo;
+            }
+        }
+        fail("Package not found with name " + pkgName);
+        return null;
+    }
+
+    private <T extends PackageItemInfo> T findPackageItemOrFail(T[] items, String name) {
+        for (T item : items) {
+            if (name.equals(item.name)) {
+                return item;
+            }
+        }
+        fail("Package item not found with name " + name);
+        return null;
+    }
 }
diff --git a/tests/tests/database/AndroidTest.xml b/tests/tests/database/AndroidTest.xml
index a1db9c0..1a53a10 100644
--- a/tests/tests/database/AndroidTest.xml
+++ b/tests/tests/database/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS Database 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="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsDatabaseTestCases.apk" />
@@ -23,6 +24,5 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.database.cts" />
         <option name="runtime-hint" value="11m" />
-        <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
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 97b0b8f..a772881 100644
--- a/tests/tests/database/src/android/database/sqlite/cts/SQLiteQueryBuilderTest.java
+++ b/tests/tests/database/src/android/database/sqlite/cts/SQLiteQueryBuilderTest.java
@@ -16,7 +16,12 @@
 
 package android.database.sqlite.cts;
 
+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.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteCursor;
@@ -26,38 +31,49 @@
 import android.database.sqlite.SQLiteQueryBuilder;
 import android.os.CancellationSignal;
 import android.os.OperationCanceledException;
-import android.test.AndroidTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.Semaphore;
 
-public class SQLiteQueryBuilderTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class SQLiteQueryBuilderTest {
     private SQLiteDatabase mDatabase;
     private final String TEST_TABLE_NAME = "test";
     private final String EMPLOYEE_TABLE_NAME = "employee";
     private static final String DATABASE_FILE = "database_test.db";
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+    @Before
+    public void setUp() throws Exception {
+        final Context context = InstrumentationRegistry.getTargetContext();
 
-        getContext().deleteDatabase(DATABASE_FILE);
-        mDatabase = getContext().openOrCreateDatabase(DATABASE_FILE, Context.MODE_PRIVATE, null);
-        assertNotNull(mDatabase);
+        context.deleteDatabase(DATABASE_FILE);
+        mDatabase = Objects.requireNonNull(
+                context.openOrCreateDatabase(DATABASE_FILE, Context.MODE_PRIVATE, null));
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void tearDown() throws Exception {
+        final Context context = InstrumentationRegistry.getTargetContext();
+
         mDatabase.close();
-        getContext().deleteDatabase(DATABASE_FILE);
-        super.tearDown();
+        context.deleteDatabase(DATABASE_FILE);
     }
 
+    @Test
     public void testConstructor() {
         new SQLiteQueryBuilder();
     }
 
+    @Test
     public void testSetDistinct() {
         String expected;
         SQLiteQueryBuilder sqliteQueryBuilder = new SQLiteQueryBuilder();
@@ -65,7 +81,7 @@
         sqliteQueryBuilder.setDistinct(false);
         sqliteQueryBuilder.appendWhere("age=20");
         String sql = sqliteQueryBuilder.buildQuery(new String[] { "age", "address" },
-                null, null, null, null, null, null);
+                null, null, null, null, null);
         assertEquals(TEST_TABLE_NAME, sqliteQueryBuilder.getTables());
         expected = "SELECT age, address FROM " + TEST_TABLE_NAME + " WHERE (age=20)";
         assertEquals(expected, sql);
@@ -75,7 +91,7 @@
         sqliteQueryBuilder.setDistinct(true);
         sqliteQueryBuilder.appendWhere("age>32");
         sql = sqliteQueryBuilder.buildQuery(new String[] { "age", "address" },
-                null, null, null, null, null, null);
+                null, null, null, null, null);
         assertEquals(EMPLOYEE_TABLE_NAME, sqliteQueryBuilder.getTables());
         expected = "SELECT DISTINCT age, address FROM " + EMPLOYEE_TABLE_NAME + " WHERE (age>32)";
         assertEquals(expected, sql);
@@ -85,13 +101,14 @@
         sqliteQueryBuilder.setDistinct(true);
         sqliteQueryBuilder.appendWhereEscapeString("age>32");
         sql = sqliteQueryBuilder.buildQuery(new String[] { "age", "address" },
-                null, null, null, null, null, null);
+                null, null, null, null, null);
         assertEquals(EMPLOYEE_TABLE_NAME, sqliteQueryBuilder.getTables());
         expected = "SELECT DISTINCT age, address FROM " + EMPLOYEE_TABLE_NAME
                 + " WHERE ('age>32')";
         assertEquals(expected, sql);
     }
 
+    @Test
     public void testSetProjectionMap() {
         String expected;
         Map<String, String> projectMap = new HashMap<String, String>();
@@ -103,12 +120,12 @@
         sqliteQueryBuilder.setDistinct(false);
         sqliteQueryBuilder.setProjectionMap(projectMap);
         String sql = sqliteQueryBuilder.buildQuery(new String[] { "EmployeeName", "EmployeeAge" },
-                null, null, null, null, null, null);
+                null, null, null, null, null);
         expected = "SELECT name, age FROM " + TEST_TABLE_NAME;
         assertEquals(expected, sql);
 
         sql = sqliteQueryBuilder.buildQuery(null, // projectionIn is null
-                null, null, null, null, null, null);
+                null, null, null, null, null);
         assertTrue(sql.matches("SELECT (age|name|address), (age|name|address), (age|name|address) "
                 + "FROM " + TEST_TABLE_NAME));
         assertTrue(sql.contains("age"));
@@ -117,13 +134,14 @@
 
         sqliteQueryBuilder.setProjectionMap(null);
         sql = sqliteQueryBuilder.buildQuery(new String[] { "name", "address" },
-                null, null, null, null, null, null);
+                null, null, null, null, null);
         assertTrue(sql.matches("SELECT (name|address), (name|address) "
                 + "FROM " + TEST_TABLE_NAME));
         assertTrue(sql.contains("name"));
         assertTrue(sql.contains("address"));
     }
 
+    @Test
     public void testSetCursorFactory() {
         mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, " +
                 "name TEXT, age INTEGER, address TEXT);");
@@ -138,9 +156,10 @@
         assertTrue(cursor instanceof SQLiteCursor);
 
         SQLiteDatabase.CursorFactory factory = new SQLiteDatabase.CursorFactory() {
+            @Override
             public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery,
                     String editTable, SQLiteQuery query) {
-                return new MockCursor(db, masterQuery, editTable, query);
+                return new MockCursor(masterQuery, editTable, query);
             }
         };
 
@@ -152,12 +171,13 @@
     }
 
     private static class MockCursor extends SQLiteCursor {
-        public MockCursor(SQLiteDatabase db, SQLiteCursorDriver driver,
+        public MockCursor(SQLiteCursorDriver driver,
                 String editTable, SQLiteQuery query) {
-            super(db, driver, editTable, query);
+            super(driver, editTable, query);
         }
     }
 
+    @Test
     public void testBuildQueryString() {
         String expected;
         final String[] DEFAULT_TEST_PROJECTION = new String [] { "name", "age", "sum(salary)" };
@@ -176,6 +196,7 @@
         assertEquals(expected, sql);
     }
 
+    @Test
     public void testBuildQuery() {
         final String[] DEFAULT_TEST_PROJECTION = new String[] { "name", "sum(salary)" };
         final String DEFAULT_TEST_WHERE = "age > 25";
@@ -185,13 +206,14 @@
         sqliteQueryBuilder.setTables(TEST_TABLE_NAME);
         sqliteQueryBuilder.setDistinct(false);
         String sql = sqliteQueryBuilder.buildQuery(DEFAULT_TEST_PROJECTION,
-                DEFAULT_TEST_WHERE, null, "name", DEFAULT_HAVING, "name", "2");
+                DEFAULT_TEST_WHERE, "name", DEFAULT_HAVING, "name", "2");
         String expected = "SELECT name, sum(salary) FROM " + TEST_TABLE_NAME
                 + " WHERE (" + DEFAULT_TEST_WHERE + ") " +
                 "GROUP BY name HAVING " + DEFAULT_HAVING + " ORDER BY name LIMIT 2";
         assertEquals(expected, sql);
     }
 
+    @Test
     public void testAppendColumns() {
         StringBuilder sb = new StringBuilder();
         String[] columns = new String[] { "name", "age" };
@@ -201,6 +223,7 @@
         assertEquals("name, age ", sb.toString());
     }
 
+    @Test
     public void testQuery() {
         createEmployeeTable();
 
@@ -240,6 +263,7 @@
         assertEquals(4000, cursor.getInt(COLUMN_SALARY_INDEX));
     }
 
+    @Test
     public void testUnionQuery() {
         String expected;
         String[] innerProjection = new String[] {"name", "age", "location"};
@@ -253,12 +277,12 @@
                 "_id", innerProjection,
                 null, 2, "employee",
                 "age=25",
-                null, null, null);
+                null, null);
         String peopleSubQuery = peopleQueryBuilder.buildUnionSubQuery(
                 "_id", innerProjection,
                 null, 2, "people",
                 "location=LA",
-                null, null, null);
+                null, null);
         expected = "SELECT name, age, location FROM employee WHERE (age=25)";
         assertEquals(expected, employeeSubQuery);
         expected = "SELECT name, age, location FROM people WHERE (location=LA)";
@@ -275,6 +299,7 @@
         assertEquals(expected, unionQuery);
     }
 
+    @Test
     public void testCancelableQuery_WhenNotCanceled_ReturnsResultSet() {
         createEmployeeTable();
 
@@ -288,6 +313,7 @@
         assertEquals(3, cursor.getCount());
     }
 
+    @Test
     public void testCancelableQuery_WhenCanceledBeforeQuery_ThrowsImmediately() {
         createEmployeeTable();
 
@@ -306,6 +332,7 @@
         }
     }
 
+    @Test
     public void testCancelableQuery_WhenCanceledAfterQuery_ThrowsWhenExecuted() {
         createEmployeeTable();
 
@@ -326,6 +353,7 @@
         }
     }
 
+    @Test
     public void testCancelableQuery_WhenCanceledDueToContention_StopsWaitingAndThrows() {
         createEmployeeTable();
 
@@ -401,6 +429,7 @@
         fail("Could not prove that the query actually blocked before cancel() was called.");
     }
 
+    @Test
     public void testCancelableQuery_WhenCanceledDuringLongRunningQuery_CancelsQueryAndThrows() {
         // Populate a table with a bunch of integers.
         mDatabase.execSQL("CREATE TABLE x (v INTEGER);");
@@ -460,7 +489,153 @@
         fail("Could not prove that the query actually canceled midway during execution.");
     }
 
+    @Test
+    public void testUpdate() throws Exception {
+        createEmployeeTable();
+
+        final ContentValues values = new ContentValues();
+        values.put("name", "Anonymous");
+        values.put("salary", 0);
+
+        {
+            final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+            qb.setTables("employee");
+            qb.appendWhere("month=3");
+            assertEquals(2, qb.update(mDatabase, values, null, null));
+        }
+        {
+            final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+            qb.setTables("employee");
+            assertEquals(1, qb.update(mDatabase, values, "month=?", new String[] { "2" }));
+        }
+    }
+
+    @Test
+    public void testDelete() throws Exception {
+        createEmployeeTable();
+
+        {
+            final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+            qb.setTables("employee");
+            qb.appendWhere("month=3");
+            assertEquals(2, qb.delete(mDatabase, null, null));
+            assertEquals(0, qb.delete(mDatabase, null, null));
+        }
+        {
+            final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+            qb.setTables("employee");
+            assertEquals(1, qb.delete(mDatabase, "month=?", new String[] { "2" }));
+            assertEquals(0, qb.delete(mDatabase, "month=?", new String[] { "2" }));
+        }
+    }
+
+    @Test
+    public void testStrictQuery() throws Exception {
+        createEmployeeTable();
+
+        final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+        qb.setTables("employee");
+        qb.setStrict(true);
+        qb.appendWhere("month=2");
+
+        // Should normally only be able to see one row
+        try (Cursor c = qb.query(mDatabase, null, null, null, null, null, null)) {
+            assertEquals(1, c.getCount());
+        }
+
+        // Trying sneaky queries should fail; even if they somehow succeed, we
+        // shouldn't get to see any other data.
+        try (Cursor c = qb.query(mDatabase, null, "1=1", null, null, null, null)) {
+            assertEquals(1, c.getCount());
+        } catch (Exception tolerated) {
+        }
+        try (Cursor c = qb.query(mDatabase, null, "1=1 --", null, null, null, null)) {
+            assertEquals(1, c.getCount());
+        } catch (Exception tolerated) {
+        }
+        try (Cursor c = qb.query(mDatabase, null, "1=1) OR (1=1", null, null, null, null)) {
+            assertEquals(1, c.getCount());
+        } catch (Exception tolerated) {
+        }
+        try (Cursor c = qb.query(mDatabase, null, "1=1)) OR ((1=1", null, null, null, null)) {
+            assertEquals(1, c.getCount());
+        } catch (Exception tolerated) {
+        }
+    }
+
+    @Test
+    public void testStrictUpdate() throws Exception {
+        createEmployeeTable();
+
+        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));
+
+        // Trying sneaky queries should fail; even if they somehow succeed, we
+        // shouldn't get to see any other data.
+        try {
+            assertEquals(1, qb.update(mDatabase, values, "1=1", null));
+        } catch (Exception tolerated) {
+        }
+        try {
+            assertEquals(1, qb.update(mDatabase, values, "1=1 --", null));
+        } catch (Exception tolerated) {
+        }
+        try {
+            assertEquals(1, qb.update(mDatabase, values, "1=1) OR (1=1", null));
+        } catch (Exception tolerated) {
+        }
+        try {
+            assertEquals(1, qb.update(mDatabase, values, "1=1)) OR ((1=1", null));
+        } catch (Exception tolerated) {
+        }
+    }
+
+    @Test
+    public void testStrictDelete() throws Exception {
+        final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+        qb.setTables("employee");
+        qb.setStrict(true);
+        qb.appendWhere("month=2");
+
+        // Should normally only be able to update one row
+        createEmployeeTable();
+        assertEquals(1, qb.delete(mDatabase, null, null));
+
+        // Trying sneaky queries should fail; even if they somehow succeed, we
+        // shouldn't get to see any other data.
+        try {
+            createEmployeeTable();
+            assertEquals(1, qb.delete(mDatabase, "1=1", null));
+        } catch (Exception tolerated) {
+        }
+        try {
+            createEmployeeTable();
+            assertEquals(1, qb.delete(mDatabase, "1=1 --", null));
+        } catch (Exception tolerated) {
+        }
+        try {
+            createEmployeeTable();
+            assertEquals(1, qb.delete(mDatabase, "1=1) OR (1=1", null));
+        } catch (Exception tolerated) {
+        }
+        try {
+            createEmployeeTable();
+            assertEquals(1, qb.delete(mDatabase, "1=1)) OR ((1=1", null));
+        } catch (Exception tolerated) {
+        }
+    }
+
     private void createEmployeeTable() {
+        mDatabase.execSQL("DROP TABLE IF EXISTS employee;");
         mDatabase.execSQL("CREATE TABLE employee (_id INTEGER PRIMARY KEY, " +
                 "name TEXT, month INTEGER, salary INTEGER);");
         mDatabase.execSQL("INSERT INTO employee (name, month, salary) " +
diff --git a/tests/tests/display/AndroidTest.xml b/tests/tests/display/AndroidTest.xml
index 3e1b38d..6eeb53b 100644
--- a/tests/tests/display/AndroidTest.xml
+++ b/tests/tests/display/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS Display 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="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsDisplayTestCases.apk" />
diff --git a/tests/tests/dpi/AndroidTest.xml b/tests/tests/dpi/AndroidTest.xml
index 5e07b7c..7a7fc6b 100644
--- a/tests/tests/dpi/AndroidTest.xml
+++ b/tests/tests/dpi/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS DPI 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="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsDpiTestCases.apk" />
diff --git a/tests/tests/dpi2/Android.mk b/tests/tests/dpi2/Android.mk
index de8f65a..8bf29d6 100644
--- a/tests/tests/dpi2/Android.mk
+++ b/tests/tests/dpi2/Android.mk
@@ -31,6 +31,7 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 3
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
diff --git a/tests/tests/dreams/AndroidTest.xml b/tests/tests/dreams/AndroidTest.xml
index 86b1131..d04dfdd 100644
--- a/tests/tests/dreams/AndroidTest.xml
+++ b/tests/tests/dreams/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS Dreams test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="sysui" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/gesture/Android.mk b/tests/tests/gesture/Android.mk
index 570a9b4..4f3b08c 100755
--- a/tests/tests/gesture/Android.mk
+++ b/tests/tests/gesture/Android.mk
@@ -27,7 +27,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := androidx-test
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
diff --git a/tests/tests/gesture/AndroidManifest.xml b/tests/tests/gesture/AndroidManifest.xml
index eed0ffd..28a2c7a 100755
--- a/tests/tests/gesture/AndroidManifest.xml
+++ b/tests/tests/gesture/AndroidManifest.xml
@@ -25,7 +25,7 @@
 
     <!--  self-instrumenting test package. -->
     <instrumentation
-        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="android.gesture.cts"
         android:label="CTS tests of android.gesture">
     </instrumentation>
diff --git a/tests/tests/gesture/AndroidTest.xml b/tests/tests/gesture/AndroidTest.xml
index 5276c29..10d1e6a 100644
--- a/tests/tests/gesture/AndroidTest.xml
+++ b/tests/tests/gesture/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS Gesture 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="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsGestureTestCases.apk" />
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/ascii.ttc b/tests/tests/graphics/assets/fonts_for_family_selection/ascii.ttc
new file mode 100644
index 0000000..e404f2b
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/ascii.ttc
Binary files differ
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/ascii_vf.ttf b/tests/tests/graphics/assets/fonts_for_family_selection/ascii_vf.ttf
new file mode 100644
index 0000000..792b7d7
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/ascii_vf.ttf
Binary files differ
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/ascii_vf.ttx b/tests/tests/graphics/assets/fonts_for_family_selection/ascii_vf.ttx
new file mode 100644
index 0000000..22b2941
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/ascii_vf.ttx
@@ -0,0 +1,1942 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="a"/>
+    <GlyphID id="2" name="b"/>
+    <GlyphID id="3" name="c"/>
+    <GlyphID id="4" name="d"/>
+    <GlyphID id="5" name="e"/>
+    <GlyphID id="6" name="f"/>
+    <GlyphID id="7" name="g"/>
+    <GlyphID id="8" name="h"/>
+    <GlyphID id="9" name="i"/>
+    <GlyphID id="10" name="j"/>
+    <GlyphID id="11" name="k"/>
+    <GlyphID id="12" name="l"/>
+    <GlyphID id="13" name="m"/>
+    <GlyphID id="14" name="n"/>
+    <GlyphID id="15" name="o"/>
+    <GlyphID id="16" name="p"/>
+    <GlyphID id="17" name="q"/>
+    <GlyphID id="18" name="r"/>
+    <GlyphID id="19" name="s"/>
+    <GlyphID id="20" name="t"/>
+    <GlyphID id="21" name="u"/>
+    <GlyphID id="22" name="v"/>
+    <GlyphID id="23" name="w"/>
+    <GlyphID id="24" name="x"/>
+    <GlyphID id="25" name="y"/>
+    <GlyphID id="26" name="z"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="50"/>
+    <created value="Thu Feb 22 10:04:28 2018"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="100"/>
+    <descent value="0"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="100"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="50" lsb="93"/>
+    <mtx name="a" width="50" lsb="0"/>
+    <mtx name="b" width="50" lsb="0"/>
+    <mtx name="c" width="50" lsb="0"/>
+    <mtx name="d" width="50" lsb="0"/>
+    <mtx name="e" width="50" lsb="0"/>
+    <mtx name="f" width="50" lsb="0"/>
+    <mtx name="g" width="50" lsb="0"/>
+    <mtx name="h" width="50" lsb="0"/>
+    <mtx name="i" width="50" lsb="0"/>
+    <mtx name="j" width="50" lsb="0"/>
+    <mtx name="k" width="50" lsb="0"/>
+    <mtx name="l" width="50" lsb="0"/>
+    <mtx name="m" width="50" lsb="0"/>
+    <mtx name="n" width="50" lsb="0"/>
+    <mtx name="o" width="50" lsb="0"/>
+    <mtx name="p" width="50" lsb="0"/>
+    <mtx name="q" width="50" lsb="0"/>
+    <mtx name="r" width="50" lsb="0"/>
+    <mtx name="s" width="50" lsb="0"/>
+    <mtx name="t" width="50" lsb="0"/>
+    <mtx name="u" width="50" lsb="0"/>
+    <mtx name="v" width="50" lsb="0"/>
+    <mtx name="w" width="50" lsb="0"/>
+    <mtx name="x" width="50" lsb="0"/>
+    <mtx name="y" width="50" lsb="0"/>
+    <mtx name="z" width="50" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+        <map code="0x0061" name="a" /> <!-- a -->
+        <map code="0x0062" name="b" /> <!-- b -->
+        <map code="0x0063" name="c" /> <!-- c -->
+        <map code="0x0064" name="d" /> <!-- d -->
+        <map code="0x0065" name="e" /> <!-- e -->
+        <map code="0x0066" name="f" /> <!-- f -->
+        <map code="0x0067" name="g" /> <!-- g -->
+        <map code="0x0068" name="h" /> <!-- h -->
+        <map code="0x0069" name="i" /> <!-- i -->
+        <map code="0x006A" name="j" /> <!-- j -->
+        <map code="0x006B" name="k" /> <!-- k -->
+        <map code="0x006C" name="l" /> <!-- l -->
+        <map code="0x006D" name="m" /> <!-- m -->
+        <map code="0x006E" name="n" /> <!-- n -->
+        <map code="0x006F" name="o" /> <!-- o -->
+        <map code="0x0070" name="p" /> <!-- p -->
+        <map code="0x0071" name="q" /> <!-- q -->
+        <map code="0x0072" name="r" /> <!-- r -->
+        <map code="0x0073" name="s" /> <!-- s -->
+        <map code="0x0074" name="t" /> <!-- t -->
+        <map code="0x0075" name="u" /> <!-- u -->
+        <map code="0x0076" name="v" /> <!-- v -->
+        <map code="0x0077" name="w" /> <!-- w -->
+        <map code="0x0078" name="x" /> <!-- x -->
+        <map code="0x0079" name="y" /> <!-- y -->
+        <map code="0x007A" name="z" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="a" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="b" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="c" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="d" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="e" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="f" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="g" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="h" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="i" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="j" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="k" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="l" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="m" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="n" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="o" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="p" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="q" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="r" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="s" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="t" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="u" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="v" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="w" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="x" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="y" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+    <TTGlyph name="z" xMin="0" yMin="0" xMax="50" yMax="50">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+        <pt x="50" y="25" on="1"/>
+        <pt x="0" y="50" on="1"/>
+      </contour>
+      <instructions />
+    </TTGlyph>
+  </glyf>
+
+  <fvar>
+    <Axis>
+      <!-- Weight axis but no effects to the glyph. -->
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>400.0</DefaultValue>
+      <MaxValue>1000.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <!-- Italic axis but no effects to the glyph. -->
+      <AxisTag>ital</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Asca</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascb</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascc</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascd</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Asce</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascf</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascg</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Asch</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Asci</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascj</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Asck</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascl</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascm</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascn</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Asco</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascp</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascq</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascr</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascs</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Asct</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascu</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascv</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascw</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascx</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascy</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+    <Axis>
+      <AxisTag>Ascz</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+  </fvar>
+
+  <HVAR>
+    <Version value="0x00010000"/>
+    <VarStore Format="1">
+      <Format value="1" />
+      <VarRegionList>
+        <Region index="0">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="1">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="2">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="3">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="4">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="5">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="6">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="7">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="8">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="9">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="10">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="11">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="12">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="13">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="14">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="15">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="16">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="17">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="18">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="19">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="20">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="21">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="22">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="23">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="24">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="25">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+        <Region index="26">
+          <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+          <VarRegionAxis index="27"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <VarData index="0">
+        <NumShorts value="0" />
+        <VarRegionIndex index="0" value="0" />
+        <VarRegionIndex index="1" value="1" />
+        <VarRegionIndex index="2" value="2" />
+        <VarRegionIndex index="3" value="3" />
+        <VarRegionIndex index="4" value="4" />
+        <VarRegionIndex index="5" value="5" />
+        <VarRegionIndex index="6" value="6" />
+        <VarRegionIndex index="7" value="7" />
+        <VarRegionIndex index="8" value="8" />
+        <VarRegionIndex index="9" value="9" />
+        <VarRegionIndex index="10" value="10" />
+        <VarRegionIndex index="11" value="11" />
+        <VarRegionIndex index="12" value="12" />
+        <VarRegionIndex index="13" value="13" />
+        <VarRegionIndex index="14" value="14" />
+        <VarRegionIndex index="15" value="15" />
+        <VarRegionIndex index="16" value="16" />
+        <VarRegionIndex index="17" value="17" />
+        <VarRegionIndex index="18" value="18" />
+        <VarRegionIndex index="19" value="19" />
+        <VarRegionIndex index="20" value="20" />
+        <VarRegionIndex index="21" value="21" />
+        <VarRegionIndex index="22" value="22" />
+        <VarRegionIndex index="23" value="23" />
+        <VarRegionIndex index="24" value="24" />
+        <VarRegionIndex index="25" value="25" />
+        <VarRegionIndex index="26" value="26" />
+        <Item index="0" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="1" value="[0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="2" value="[0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="3" value="[0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="4" value="[0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="5" value="[0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="6" value="[0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="7" value="[0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="8" value="[0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="9" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="10" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="11" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="12" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="13" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="14" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="15" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="16" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="17" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="18" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="19" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0]" />
+        <Item index="20" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0]" />
+        <Item index="21" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0]" />
+        <Item index="22" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0]" />
+        <Item index="23" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0]" />
+        <Item index="24" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0]" />
+        <Item index="25" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0]" />
+        <Item index="26" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100]" />
+      </VarData>
+    </VarStore>
+    <AdvWidthMap>
+      <Map index="0" outer="0" inner="0" />
+      <Map index="1" outer="0" inner="1" />
+      <Map index="2" outer="0" inner="2" />
+      <Map index="3" outer="0" inner="3" />
+      <Map index="4" outer="0" inner="4" />
+      <Map index="5" outer="0" inner="5" />
+      <Map index="6" outer="0" inner="6" />
+      <Map index="7" outer="0" inner="7" />
+      <Map index="8" outer="0" inner="8" />
+      <Map index="9" outer="0" inner="9" />
+      <Map index="10" outer="0" inner="10" />
+      <Map index="11" outer="0" inner="11" />
+      <Map index="12" outer="0" inner="12" />
+      <Map index="13" outer="0" inner="13" />
+      <Map index="14" outer="0" inner="14" />
+      <Map index="15" outer="0" inner="15" />
+      <Map index="16" outer="0" inner="16" />
+      <Map index="17" outer="0" inner="17" />
+      <Map index="18" outer="0" inner="18" />
+      <Map index="19" outer="0" inner="19" />
+      <Map index="20" outer="0" inner="20" />
+      <Map index="21" outer="0" inner="21" />
+      <Map index="22" outer="0" inner="22" />
+      <Map index="23" outer="0" inner="23" />
+      <Map index="24" outer="0" inner="24" />
+      <Map index="25" outer="0" inner="25" />
+      <Map index="26" outer="0" inner="26" />
+    </AdvWidthMap>
+  </HVAR>
+
+  <gvar>
+    <version value="1" />
+    <reserved value="0" />
+    <glyphVariations glyph="a">
+      <tuple>
+        <coord axis="Asca" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="b">
+      <tuple>
+        <coord axis="Ascb" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="c">
+      <tuple>
+        <coord axis="Ascc" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="d">
+      <tuple>
+        <coord axis="Ascd" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="e">
+      <tuple>
+        <coord axis="Asce" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="f">
+      <tuple>
+        <coord axis="Ascf" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="g">
+      <tuple>
+        <coord axis="Ascg" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="h">
+      <tuple>
+        <coord axis="Asch" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="i">
+      <tuple>
+        <coord axis="Asci" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="j">
+      <tuple>
+        <coord axis="Ascj" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="k">
+      <tuple>
+        <coord axis="Asck" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="l">
+      <tuple>
+        <coord axis="Ascl" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="m">
+      <tuple>
+        <coord axis="Ascm" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="n">
+      <tuple>
+        <coord axis="Ascn" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="o">
+      <tuple>
+        <coord axis="Asco" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="p">
+      <tuple>
+        <coord axis="Ascp" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="q">
+      <tuple>
+        <coord axis="Ascq" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="r">
+      <tuple>
+        <coord axis="Ascr" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="s">
+      <tuple>
+        <coord axis="Ascs" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="t">
+      <tuple>
+        <coord axis="Asct" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="u">
+      <tuple>
+        <coord axis="Ascu" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="v">
+      <tuple>
+        <coord axis="Ascv" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="w">
+      <tuple>
+        <coord axis="Ascw" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="x">
+      <tuple>
+        <coord axis="Ascx" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="y">
+      <tuple>
+        <coord axis="Ascy" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="z">
+      <tuple>
+        <coord axis="Ascz" value="1.0" />
+        <delta pt="0" x="0" y="0" />
+        <delta pt="1" x="100" y="0" />
+        <delta pt="2" x="0" y="0" />
+        <!-- deltas for phantom points -->
+        <delta pt="3" x="0" y="0" />  <!-- (left, 0) -->
+        <delta pt="4" x="100" y="0" />  <!-- (right, 0) -->
+        <delta pt="5" x="0" y="0" />  <!-- (0, top) -->
+        <delta pt="6" x="0" y="0" />  <!-- (0, bottom) -->
+      </tuple>
+    </glyphVariations>
+  </gvar>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      3 em signal
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttf b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttf
new file mode 100644
index 0000000..f220eb3
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttf
Binary files differ
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttx b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttx
new file mode 100644
index 0000000..ebedcb6
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:10 2018"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="100"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="3em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttf b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttf
new file mode 100644
index 0000000..b9ffb84
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttf
Binary files differ
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttx b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttx
new file mode 100644
index 0000000..def6a29
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:11 2018"/>
+    <macStyle value="00000000 00000010"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="100"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 00000001"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="3em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttf b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttf
new file mode 100644
index 0000000..075b068
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttf
Binary files differ
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttx b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttx
new file mode 100644
index 0000000..d201183
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:11 2018"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="200"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="3em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttf b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttf
new file mode 100644
index 0000000..5b47f0d
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttf
Binary files differ
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttx b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttx
new file mode 100644
index 0000000..7c801a0
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:11 2018"/>
+    <macStyle value="00000000 00000010"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="200"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 00000001"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="3em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttf b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttf
new file mode 100644
index 0000000..3e9133b
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttf
Binary files differ
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttx b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttx
new file mode 100644
index 0000000..acb2006
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:11 2018"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="300"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="3em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttf b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttf
new file mode 100644
index 0000000..3ba3feb
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttf
Binary files differ
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttx b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttx
new file mode 100644
index 0000000..452ff66
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:11 2018"/>
+    <macStyle value="00000000 00000010"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="300"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 00000001"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="3em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttf b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttf
new file mode 100644
index 0000000..b3175a1
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttf
Binary files differ
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttx b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttx
new file mode 100644
index 0000000..34a638f
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:11 2018"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="3em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttf b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttf
new file mode 100644
index 0000000..099c4f146
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttf
Binary files differ
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttx b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttx
new file mode 100644
index 0000000..b18af66
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:12 2018"/>
+    <macStyle value="00000000 00000010"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 00000001"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="3em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttf b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttf
new file mode 100644
index 0000000..b8edcb6
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttf
Binary files differ
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttx b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttx
new file mode 100644
index 0000000..6daf8da
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:12 2018"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="500"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="3em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttf b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttf
new file mode 100644
index 0000000..6d7dde9
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttf
Binary files differ
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttx b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttx
new file mode 100644
index 0000000..c5843f0
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:12 2018"/>
+    <macStyle value="00000000 00000010"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="500"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 00000001"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="3em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttf b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttf
new file mode 100644
index 0000000..eb6d7d1
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttf
Binary files differ
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttx b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttx
new file mode 100644
index 0000000..d46059f
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:12 2018"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="600"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="3em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttf b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttf
new file mode 100644
index 0000000..f509e94
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttf
Binary files differ
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttx b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttx
new file mode 100644
index 0000000..03073d3
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:12 2018"/>
+    <macStyle value="00000000 00000010"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="600"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 00000001"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="3em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttf b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttf
new file mode 100644
index 0000000..062f299
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttf
Binary files differ
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttx b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttx
new file mode 100644
index 0000000..ca24fde
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:12 2018"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="700"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="3em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttf b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttf
new file mode 100644
index 0000000..fdd0239
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttf
Binary files differ
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttx b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttx
new file mode 100644
index 0000000..468d591
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:13 2018"/>
+    <macStyle value="00000000 00000010"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="700"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 00000001"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="3em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_names.txt b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_names.txt
new file mode 100644
index 0000000..986d712
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_names.txt
@@ -0,0 +1,18 @@
+ascii_a3em_weight100_upright.ttf
+ascii_b3em_weight100_italic.ttf
+ascii_c3em_weight200_upright.ttf
+ascii_d3em_weight200_italic.ttf
+ascii_e3em_weight300_upright.ttf
+ascii_f3em_weight300_italic.ttf
+ascii_g3em_weight400_upright.ttf
+ascii_h3em_weight400_italic.ttf
+ascii_i3em_weight500_upright.ttf
+ascii_j3em_weight500_italic.ttf
+ascii_k3em_weight600_upright.ttf
+ascii_l3em_weight600_italic.ttf
+ascii_m3em_weight700_upright.ttf
+ascii_n3em_weight700_italic.ttf
+ascii_o3em_weight800_upright.ttf
+ascii_p3em_weight800_italic.ttf
+ascii_q3em_weight900_upright.ttf
+ascii_r3em_weight900_italic.ttf
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttf b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttf
new file mode 100644
index 0000000..993e7fa
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttf
Binary files differ
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttx b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttx
new file mode 100644
index 0000000..b8c712f
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:13 2018"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="800"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="3em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttf b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttf
new file mode 100644
index 0000000..f0d54f0
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttf
Binary files differ
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttx b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttx
new file mode 100644
index 0000000..5d8ee18
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:13 2018"/>
+    <macStyle value="00000000 00000010"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="800"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 00000001"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="3em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttf b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttf
new file mode 100644
index 0000000..c776c78
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttf
Binary files differ
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttx b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttx
new file mode 100644
index 0000000..169fd73
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:13 2018"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="900"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="3em" /> <!-- q -->
+        <map code="0x0072" name="1em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttf b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttf
new file mode 100644
index 0000000..02f6246
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttf
Binary files differ
diff --git a/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttx b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttx
new file mode 100644
index 0000000..131d7b1
--- /dev/null
+++ b/tests/tests/graphics/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Feb 15 18:29:13 2018"/>
+    <macStyle value="00000000 00000010"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="900"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 00000001"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+        <map code="0x0061" name="1em" /> <!-- a -->
+        <map code="0x0062" name="1em" /> <!-- b -->
+        <map code="0x0063" name="1em" /> <!-- c -->
+        <map code="0x0064" name="1em" /> <!-- d -->
+        <map code="0x0065" name="1em" /> <!-- e -->
+        <map code="0x0066" name="1em" /> <!-- f -->
+        <map code="0x0067" name="1em" /> <!-- g -->
+        <map code="0x0068" name="1em" /> <!-- h -->
+        <map code="0x0069" name="1em" /> <!-- i -->
+        <map code="0x006A" name="1em" /> <!-- j -->
+        <map code="0x006B" name="1em" /> <!-- k -->
+        <map code="0x006C" name="1em" /> <!-- l -->
+        <map code="0x006D" name="1em" /> <!-- m -->
+        <map code="0x006E" name="1em" /> <!-- n -->
+        <map code="0x006F" name="1em" /> <!-- o -->
+        <map code="0x0070" name="1em" /> <!-- p -->
+        <map code="0x0071" name="1em" /> <!-- q -->
+        <map code="0x0072" name="3em" /> <!-- r -->
+        <map code="0x0073" name="1em" /> <!-- s -->
+        <map code="0x0074" name="1em" /> <!-- t -->
+        <map code="0x0075" name="1em" /> <!-- u -->
+        <map code="0x0076" name="1em" /> <!-- v -->
+        <map code="0x0077" name="1em" /> <!-- w -->
+        <map code="0x0078" name="1em" /> <!-- x -->
+        <map code="0x0079" name="1em" /> <!-- y -->
+        <map code="0x007A" name="1em" /> <!-- z -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2018 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/tests/graphics/jni/Android.mk b/tests/tests/graphics/jni/Android.mk
index 2906bf5..d8c3770 100644
--- a/tests/tests/graphics/jni/Android.mk
+++ b/tests/tests/graphics/jni/Android.mk
@@ -32,6 +32,7 @@
 	android_graphics_cts_MediaVulkanGpuTest.cpp \
 	android_graphics_cts_VulkanFeaturesTest.cpp \
 	android_graphics_cts_VulkanPreTransformCtsActivity.cpp \
+	android_graphics_cts_VulkanSurfaceSupportTest.cpp \
 	CameraTestHelpers.cpp \
 	ImageReaderTestHelpers.cpp \
 	MediaTestHelpers.cpp \
diff --git a/tests/tests/graphics/jni/CtsGraphicsJniOnLoad.cpp b/tests/tests/graphics/jni/CtsGraphicsJniOnLoad.cpp
index bb46671..a435ab4 100644
--- a/tests/tests/graphics/jni/CtsGraphicsJniOnLoad.cpp
+++ b/tests/tests/graphics/jni/CtsGraphicsJniOnLoad.cpp
@@ -26,6 +26,7 @@
 extern int register_android_graphics_cts_MediaVulkanGpuTest(JNIEnv*);
 extern int register_android_graphics_cts_VulkanFeaturesTest(JNIEnv*);
 extern int register_android_graphics_cts_VulkanPreTransformCtsActivity(JNIEnv*);
+extern int register_android_graphics_cts_VulkanSurfaceSupportTest(JNIEnv*);
 
 jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) {
     JNIEnv* env = nullptr;
@@ -47,6 +48,8 @@
         return JNI_ERR;
     if (register_android_graphics_cts_VulkanPreTransformCtsActivity(env))
         return JNI_ERR;
+    if (register_android_graphics_cts_VulkanSurfaceSupportTest(env))
+        return JNI_ERR;
     if (register_android_graphics_cts_BasicVulkanGpuTest(env))
         return JNI_ERR;
     if (register_android_graphics_cts_CameraVulkanGpuTest(env))
diff --git a/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.cpp b/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.cpp
index 7019ea4..ca240a0 100644
--- a/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.cpp
+++ b/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.cpp
@@ -15,6 +15,12 @@
  *
  */
 
+#define LOG_TAG "VulkanPreTransformTestHelpers"
+
+#ifndef VK_USE_PLATFORM_ANDROID_KHR
+#define VK_USE_PLATFORM_ANDROID_KHR
+#endif
+
 #include <android/log.h>
 #include <cstring>
 
@@ -25,7 +31,7 @@
 #define ASSERT(a)                                              \
     if (!(a)) {                                                \
         ALOGE("Failure: " #a " at " __FILE__ ":%d", __LINE__); \
-        return -1;                                             \
+        return VK_TEST_ERROR;                                  \
     }
 #define VK_CALL(a) ASSERT(VK_SUCCESS == (a))
 
@@ -124,7 +130,7 @@
     }
 }
 
-int32_t DeviceInfo::init(JNIEnv* env, jobject jSurface) {
+VkTestResult DeviceInfo::init(JNIEnv* env, jobject jSurface) {
     ASSERT(jSurface);
 
     mWindow = ANativeWindow_fromSurface(env, jSurface);
@@ -164,7 +170,7 @@
     VK_CALL(vkEnumeratePhysicalDevices(mInstance, &gpuCount, nullptr));
     if (gpuCount == 0) {
         ALOGD("No physical device available");
-        return 1;
+        return VK_TEST_PHYSICAL_DEVICE_NOT_EXISTED;
     }
 
     std::vector<VkPhysicalDevice> gpus(gpuCount, VK_NULL_HANDLE);
@@ -205,6 +211,13 @@
     ASSERT(queueFamilyIndex < queueFamilyCount);
     mQueueFamilyIndex = queueFamilyIndex;
 
+    VkBool32 supported = VK_FALSE;
+    VK_CALL(vkGetPhysicalDeviceSurfaceSupportKHR(mGpu, mQueueFamilyIndex, mSurface, &supported));
+    if (supported == VK_FALSE) {
+        ALOGD("Surface format not supported");
+        return VK_TEST_SURFACE_FORMAT_NOT_SUPPORTED;
+    }
+
     const float priority = 1.0f;
     const VkDeviceQueueCreateInfo queueCreateInfo = {
             .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
@@ -229,7 +242,7 @@
 
     vkGetDeviceQueue(mDevice, mQueueFamilyIndex, 0, &mQueue);
 
-    return 0;
+    return VK_TEST_SUCCESS;
 }
 
 SwapchainInfo::SwapchainInfo(const DeviceInfo* const deviceInfo)
@@ -246,7 +259,7 @@
     }
 }
 
-int32_t SwapchainInfo::init(bool setPreTransform) {
+VkTestResult SwapchainInfo::init(bool setPreTransform) {
     VkSurfaceCapabilitiesKHR surfaceCapabilities;
     VK_CALL(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(mDeviceInfo->gpu(), mDeviceInfo->surface(),
                                                       &surfaceCapabilities));
@@ -323,7 +336,7 @@
     VK_CALL(vkGetSwapchainImagesKHR(mDeviceInfo->device(), mSwapchain, &mSwapchainLength, nullptr));
     ALOGD("Swapchain length = %u", mSwapchainLength);
 
-    return 0;
+    return VK_TEST_SUCCESS;
 }
 
 Renderer::Renderer(const DeviceInfo* const deviceInfo, const SwapchainInfo* const swapchainInfo)
@@ -366,7 +379,7 @@
     }
 }
 
-int32_t Renderer::createRenderPass() {
+VkTestResult Renderer::createRenderPass() {
     const VkAttachmentDescription attachmentDescription = {
             .flags = 0,
             .format = mSwapchainInfo->format(),
@@ -408,10 +421,10 @@
     VK_CALL(vkCreateRenderPass(mDeviceInfo->device(), &renderPassCreateInfo, nullptr,
                                &mRenderPass));
 
-    return 0;
+    return VK_TEST_SUCCESS;
 }
 
-int32_t Renderer::createFrameBuffers() {
+VkTestResult Renderer::createFrameBuffers() {
     uint32_t swapchainLength = mSwapchainInfo->swapchainLength();
     std::vector<VkImage> images(swapchainLength, VK_NULL_HANDLE);
     VK_CALL(vkGetSwapchainImagesKHR(mDeviceInfo->device(), mSwapchainInfo->swapchain(),
@@ -463,10 +476,10 @@
                                     &mFramebuffers[i]));
     }
 
-    return 0;
+    return VK_TEST_SUCCESS;
 }
 
-int32_t Renderer::createVertexBuffers() {
+VkTestResult Renderer::createVertexBuffers() {
     const uint32_t queueFamilyIndex = mDeviceInfo->queueFamilyIndex();
     const VkBufferCreateInfo bufferCreateInfo = {
             .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
@@ -515,17 +528,15 @@
 
     VK_CALL(vkBindBufferMemory(mDeviceInfo->device(), mVertexBuffer, mDeviceMemory, 0));
 
-    return 0;
+    return VK_TEST_SUCCESS;
 }
 
-int32_t Renderer::loadShaderFromFile(const char* filePath, VkShaderModule* const outShader) {
+VkTestResult Renderer::loadShaderFromFile(const char* filePath, VkShaderModule* const outShader) {
     ASSERT(filePath);
 
     AAsset* file = AAssetManager_open(mAssetManager, filePath, AASSET_MODE_BUFFER);
-    if (!file) {
-        ALOGE("Failed to open shader file");
-        return -1;
-    }
+    ASSERT(file);
+
     size_t fileLength = AAsset_getLength(file);
     std::vector<char> fileContent(fileLength);
     AAsset_read(file, fileContent.data(), fileLength);
@@ -541,10 +552,10 @@
     VK_CALL(vkCreateShaderModule(mDeviceInfo->device(), &shaderModuleCreateInfo, nullptr,
                                  outShader));
 
-    return 0;
+    return VK_TEST_SUCCESS;
 }
 
-int32_t Renderer::createGraphicsPipeline() {
+VkTestResult Renderer::createGraphicsPipeline() {
     const VkPushConstantRange pushConstantRange = {
             .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
             .offset = 0,
@@ -713,10 +724,10 @@
     mVertexShader = VK_NULL_HANDLE;
     mFragmentShader = VK_NULL_HANDLE;
 
-    return 0;
+    return VK_TEST_SUCCESS;
 }
 
-int32_t Renderer::init(JNIEnv* env, jobject jAssetManager) {
+VkTestResult Renderer::init(JNIEnv* env, jobject jAssetManager) {
     mAssetManager = AAssetManager_fromJava(env, jAssetManager);
     ASSERT(mAssetManager);
 
@@ -823,10 +834,10 @@
     };
     VK_CALL(vkCreateSemaphore(mDeviceInfo->device(), &semaphoreCreateInfo, nullptr, &mSemaphore));
 
-    return 0;
+    return VK_TEST_SUCCESS;
 }
 
-int32_t Renderer::drawFrame() {
+VkTestResult Renderer::drawFrame() {
     uint32_t nextIndex;
     VK_CALL(vkAcquireNextImageKHR(mDeviceInfo->device(), mSwapchainInfo->swapchain(), UINT64_MAX,
                                   mSemaphore, VK_NULL_HANDLE, &nextIndex));
@@ -862,5 +873,5 @@
     };
     VK_CALL(vkQueuePresentKHR(mDeviceInfo->queue(), &presentInfo));
 
-    return 0;
+    return VK_TEST_SUCCESS;
 }
diff --git a/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.h b/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.h
index f876aad..027ce74 100644
--- a/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.h
+++ b/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.h
@@ -17,23 +17,24 @@
 #ifndef VULKAN_PRE_TRANSFORM_TEST_HELPERS_H
 #define VULKAN_PRE_TRANSFORM_TEST_HELPERS_H
 
-#define LOG_TAG "vulkan"
-
-#ifndef VK_USE_PLATFORM_ANDROID_KHR
-#define VK_USE_PLATFORM_ANDROID_KHR
-#endif
-
 #include <android/asset_manager_jni.h>
 #include <android/native_window_jni.h>
 #include <jni.h>
 #include <vulkan/vulkan.h>
 #include <vector>
 
+typedef enum VkTestResult {
+    VK_TEST_ERROR = -1,
+    VK_TEST_SUCCESS = 0,
+    VK_TEST_PHYSICAL_DEVICE_NOT_EXISTED = 1,
+    VK_TEST_SURFACE_FORMAT_NOT_SUPPORTED = 2,
+} VkTestResult;
+
 class DeviceInfo {
 public:
     DeviceInfo();
     ~DeviceInfo();
-    int32_t init(JNIEnv* env, jobject jSurface);
+    VkTestResult init(JNIEnv* env, jobject jSurface);
     VkPhysicalDevice gpu() const { return mGpu; }
     VkSurfaceKHR surface() const { return mSurface; }
     uint32_t queueFamilyIndex() const { return mQueueFamilyIndex; }
@@ -54,7 +55,7 @@
 public:
     SwapchainInfo(const DeviceInfo* const deviceInfo);
     ~SwapchainInfo();
-    int32_t init(bool setPreTransform);
+    VkTestResult init(bool setPreTransform);
     VkFormat format() const { return mFormat; }
     VkExtent2D displaySize() const { return mDisplaySize; }
     VkSwapchainKHR swapchain() const { return mSwapchain; }
@@ -73,15 +74,15 @@
 public:
     Renderer(const DeviceInfo* const deviceInfo, const SwapchainInfo* const swapchainInfo);
     ~Renderer();
-    int32_t init(JNIEnv* env, jobject assetManager);
-    int32_t drawFrame();
+    VkTestResult init(JNIEnv* env, jobject assetManager);
+    VkTestResult drawFrame();
 
 private:
-    int32_t createRenderPass();
-    int32_t createFrameBuffers();
-    int32_t createVertexBuffers();
-    int32_t loadShaderFromFile(const char* filePath, VkShaderModule* const outShader);
-    int32_t createGraphicsPipeline();
+    VkTestResult createRenderPass();
+    VkTestResult createFrameBuffers();
+    VkTestResult createVertexBuffers();
+    VkTestResult loadShaderFromFile(const char* filePath, VkShaderModule* const outShader);
+    VkTestResult createGraphicsPipeline();
 
     const DeviceInfo* const mDeviceInfo;
     const SwapchainInfo* const mSwapchainInfo;
diff --git a/tests/tests/graphics/jni/android_graphics_cts_VulkanPreTransformCtsActivity.cpp b/tests/tests/graphics/jni/android_graphics_cts_VulkanPreTransformCtsActivity.cpp
index 2a347b2..cce6406 100644
--- a/tests/tests/graphics/jni/android_graphics_cts_VulkanPreTransformCtsActivity.cpp
+++ b/tests/tests/graphics/jni/android_graphics_cts_VulkanPreTransformCtsActivity.cpp
@@ -15,11 +15,7 @@
  *
  */
 
-#define LOG_TAG "vulkan"
-
-#ifndef VK_USE_PLATFORM_ANDROID_KHR
-#define VK_USE_PLATFORM_ANDROID_KHR
-#endif
+#define LOG_TAG "VulkanPreTransformCtsActivity"
 
 #include <android/log.h>
 #include <jni.h>
@@ -50,21 +46,23 @@
     ASSERT(jSurface, "jSurface is NULL");
 
     DeviceInfo deviceInfo;
-    int ret = deviceInfo.init(env, jSurface);
-    ASSERT(ret >= 0, "Failed to initialize Vulkan device");
-    if (ret > 0) {
+    VkTestResult ret = deviceInfo.init(env, jSurface);
+    if (ret == VK_TEST_PHYSICAL_DEVICE_NOT_EXISTED) {
         ALOGD("Hardware not supported for this test");
         return;
     }
+    ASSERT(ret == VK_TEST_SUCCESS, "Failed to initialize Vulkan device");
 
     SwapchainInfo swapchainInfo(&deviceInfo);
-    ASSERT(!swapchainInfo.init(setPreTransform), "Failed to initialize Vulkan swapchain");
+    ASSERT(swapchainInfo.init(setPreTransform) == VK_TEST_SUCCESS,
+           "Failed to initialize Vulkan swapchain");
 
     Renderer renderer(&deviceInfo, &swapchainInfo);
-    ASSERT(!renderer.init(env, jAssetManager), "Failed to initialize Vulkan renderer");
+    ASSERT(renderer.init(env, jAssetManager) == VK_TEST_SUCCESS,
+           "Failed to initialize Vulkan renderer");
 
     for (uint32_t i = 0; i < 120; ++i) {
-        ASSERT(!renderer.drawFrame(), "Failed to draw frame");
+        ASSERT(renderer.drawFrame() == VK_TEST_SUCCESS, "Failed to draw frame");
     }
 
     ASSERT(validatePixelValues(env, setPreTransform), "Not properly rotated");
diff --git a/tests/tests/graphics/jni/android_graphics_cts_VulkanSurfaceSupportTest.cpp b/tests/tests/graphics/jni/android_graphics_cts_VulkanSurfaceSupportTest.cpp
new file mode 100644
index 0000000..8f3cf22
--- /dev/null
+++ b/tests/tests/graphics/jni/android_graphics_cts_VulkanSurfaceSupportTest.cpp
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ *
+ */
+
+#define LOG_TAG "VulkanSurfaceSupportTest"
+
+#ifndef VK_USE_PLATFORM_ANDROID_KHR
+#define VK_USE_PLATFORM_ANDROID_KHR
+#endif
+
+#include <android/log.h>
+#include <jni.h>
+#include <array>
+
+#include "NativeTestHelpers.h"
+#include "VulkanPreTransformTestHelpers.h"
+
+#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
+namespace {
+
+void createNativeTest(JNIEnv* env, jclass /*clazz*/, jobject jAssetManager, jobject jSurface,
+                      jboolean jSupported) {
+    ASSERT(jAssetManager, "jAssetManager is NULL");
+    ASSERT(jSurface, "jSurface is NULL");
+
+    DeviceInfo deviceInfo;
+    VkTestResult ret = deviceInfo.init(env, jSurface);
+    if (ret == VK_TEST_PHYSICAL_DEVICE_NOT_EXISTED) {
+        ALOGD("Hardware not supported for this test");
+        return;
+    }
+    if (ret == VK_TEST_SURFACE_FORMAT_NOT_SUPPORTED) {
+        ASSERT(jSupported == false, "Surface format should not be supported");
+        return;
+    }
+    ASSERT(ret == VK_TEST_SUCCESS, "Failed to initialize Vulkan device");
+    ASSERT(jSupported == true, "Surface format should be supported");
+
+    SwapchainInfo swapchainInfo(&deviceInfo);
+    ASSERT(swapchainInfo.init(false) == VK_TEST_SUCCESS, "Failed to initialize Vulkan swapchain");
+
+    Renderer renderer(&deviceInfo, &swapchainInfo);
+    ASSERT(renderer.init(env, jAssetManager) == VK_TEST_SUCCESS,
+           "Failed to initialize Vulkan renderer");
+
+    for (uint32_t i = 0; i < 3; ++i) {
+        ASSERT(renderer.drawFrame() == VK_TEST_SUCCESS, "Failed to draw frame");
+    }
+}
+
+const std::array<JNINativeMethod, 1> JNI_METHODS = {{
+        {"nCreateNativeTest", "(Landroid/content/res/AssetManager;Landroid/view/Surface;Z)V",
+         (void*)createNativeTest},
+}};
+
+} // anonymous namespace
+
+int register_android_graphics_cts_VulkanSurfaceSupportTest(JNIEnv* env) {
+    jclass clazz = env->FindClass("android/graphics/cts/VulkanSurfaceSupportTest");
+    return env->RegisterNatives(clazz, JNI_METHODS.data(), JNI_METHODS.size());
+}
diff --git a/tests/tests/graphics/res/color/csl_with_theme_attr.xml b/tests/tests/graphics/res/color/csl_with_theme_attr.xml
new file mode 100644
index 0000000..19aed64
--- /dev/null
+++ b/tests/tests/graphics/res/color/csl_with_theme_attr.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:color="?attr/themeVectorDrawableFillColor"/>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/graphics/res/drawable/gradientdrawable.xml b/tests/tests/graphics/res/drawable/gradientdrawable.xml
index ed8ff96..3d147dd 100644
--- a/tests/tests/graphics/res/drawable/gradientdrawable.xml
+++ b/tests/tests/graphics/res/drawable/gradientdrawable.xml
@@ -15,7 +15,11 @@
  * limitations under the License.
  -->
 
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:opticalInsetLeft="1px"
+    android:opticalInsetTop="2px"
+    android:opticalInsetRight="3px"
+    android:opticalInsetBottom="4px">
     <gradient android:startColor="#ffffffff" android:centerColor="#ffff0000"
               android:endColor="#0000ffff" />
     <corners android:radius="8px" />
diff --git a/tests/tests/graphics/res/drawable/heart.xml b/tests/tests/graphics/res/drawable/heart.xml
new file mode 100644
index 0000000..31c69f1
--- /dev/null
+++ b/tests/tests/graphics/res/drawable/heart.xml
@@ -0,0 +1,34 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="256dp"
+        android:width="256dp"
+        android:viewportWidth="32"
+        android:viewportHeight="32">
+
+    <!-- draw a path -->
+    <path
+        android:name="path"
+        android:fillColor="@color/csl_with_theme_attr"
+        android:pathData="M20.5,9.5
+                        c-1.965,0,-3.83,1.268,-4.5,3
+                        c-0.17,-1.732,-2.547,-3,-4.5,-3
+                        C8.957,9.5,7,11.432,7,14
+                        c0,3.53,3.793,6.257,9,11.5
+                        c5.207,-5.242,9,-7.97,9,-11.5
+                        C25,11.432,23.043,9.5,20.5,9.5z" />
+</vector>
diff --git a/tests/tests/graphics/res/drawable/vector_drawable_grouping_1.xml b/tests/tests/graphics/res/drawable/vector_drawable_grouping_1.xml
index 7839ad1..1010ce3 100644
--- a/tests/tests/graphics/res/drawable/vector_drawable_grouping_1.xml
+++ b/tests/tests/graphics/res/drawable/vector_drawable_grouping_1.xml
@@ -16,6 +16,10 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:height="64dp"
         android:width="64dp"
+        android:opticalInsetLeft="10px"
+        android:opticalInsetTop="20px"
+        android:opticalInsetRight="30px"
+        android:opticalInsetBottom="40px"
         android:viewportHeight="256"
         android:viewportWidth="256" >
 
diff --git a/tests/tests/graphics/res/drawable/vector_icon_create.xml b/tests/tests/graphics/res/drawable/vector_icon_create.xml
index 7db4ad5..55113f3 100644
--- a/tests/tests/graphics/res/drawable/vector_icon_create.xml
+++ b/tests/tests/graphics/res/drawable/vector_icon_create.xml
@@ -19,6 +19,10 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:height="64dp"
         android:width="64dp"
+        android:opticalInsetLeft="1px"
+        android:opticalInsetTop="2px"
+        android:opticalInsetRight="3px"
+        android:opticalInsetBottom="4px"
         android:viewportHeight="24"
         android:viewportWidth="24" >
 
diff --git a/tests/tests/graphics/res/font/ascii.ttc b/tests/tests/graphics/res/font/ascii.ttc
new file mode 100644
index 0000000..e404f2b
--- /dev/null
+++ b/tests/tests/graphics/res/font/ascii.ttc
Binary files differ
diff --git a/tests/tests/graphics/res/font/ascii_a3em_weight100_upright.ttf b/tests/tests/graphics/res/font/ascii_a3em_weight100_upright.ttf
new file mode 100644
index 0000000..f220eb3
--- /dev/null
+++ b/tests/tests/graphics/res/font/ascii_a3em_weight100_upright.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/ascii_b3em_weight100_italic.ttf b/tests/tests/graphics/res/font/ascii_b3em_weight100_italic.ttf
new file mode 100644
index 0000000..b9ffb84
--- /dev/null
+++ b/tests/tests/graphics/res/font/ascii_b3em_weight100_italic.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/ascii_c3em_weight200_upright.ttf b/tests/tests/graphics/res/font/ascii_c3em_weight200_upright.ttf
new file mode 100644
index 0000000..075b068
--- /dev/null
+++ b/tests/tests/graphics/res/font/ascii_c3em_weight200_upright.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/ascii_d3em_weight200_italic.ttf b/tests/tests/graphics/res/font/ascii_d3em_weight200_italic.ttf
new file mode 100644
index 0000000..5b47f0d
--- /dev/null
+++ b/tests/tests/graphics/res/font/ascii_d3em_weight200_italic.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/ascii_e3em_weight300_upright.ttf b/tests/tests/graphics/res/font/ascii_e3em_weight300_upright.ttf
new file mode 100644
index 0000000..3e9133b
--- /dev/null
+++ b/tests/tests/graphics/res/font/ascii_e3em_weight300_upright.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/ascii_f3em_weight300_italic.ttf b/tests/tests/graphics/res/font/ascii_f3em_weight300_italic.ttf
new file mode 100644
index 0000000..3ba3feb
--- /dev/null
+++ b/tests/tests/graphics/res/font/ascii_f3em_weight300_italic.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/ascii_g3em_weight400_upright.ttf b/tests/tests/graphics/res/font/ascii_g3em_weight400_upright.ttf
new file mode 100644
index 0000000..b3175a1
--- /dev/null
+++ b/tests/tests/graphics/res/font/ascii_g3em_weight400_upright.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/ascii_h3em_weight400_italic.ttf b/tests/tests/graphics/res/font/ascii_h3em_weight400_italic.ttf
new file mode 100644
index 0000000..099c4f146
--- /dev/null
+++ b/tests/tests/graphics/res/font/ascii_h3em_weight400_italic.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/ascii_i3em_weight500_upright.ttf b/tests/tests/graphics/res/font/ascii_i3em_weight500_upright.ttf
new file mode 100644
index 0000000..b8edcb6
--- /dev/null
+++ b/tests/tests/graphics/res/font/ascii_i3em_weight500_upright.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/ascii_j3em_weight500_italic.ttf b/tests/tests/graphics/res/font/ascii_j3em_weight500_italic.ttf
new file mode 100644
index 0000000..6d7dde9
--- /dev/null
+++ b/tests/tests/graphics/res/font/ascii_j3em_weight500_italic.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/ascii_k3em_weight600_upright.ttf b/tests/tests/graphics/res/font/ascii_k3em_weight600_upright.ttf
new file mode 100644
index 0000000..eb6d7d1
--- /dev/null
+++ b/tests/tests/graphics/res/font/ascii_k3em_weight600_upright.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/ascii_l3em_weight600_italic.ttf b/tests/tests/graphics/res/font/ascii_l3em_weight600_italic.ttf
new file mode 100644
index 0000000..f509e94
--- /dev/null
+++ b/tests/tests/graphics/res/font/ascii_l3em_weight600_italic.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/ascii_m3em_weight700_upright.ttf b/tests/tests/graphics/res/font/ascii_m3em_weight700_upright.ttf
new file mode 100644
index 0000000..062f299
--- /dev/null
+++ b/tests/tests/graphics/res/font/ascii_m3em_weight700_upright.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/ascii_n3em_weight700_italic.ttf b/tests/tests/graphics/res/font/ascii_n3em_weight700_italic.ttf
new file mode 100644
index 0000000..fdd0239
--- /dev/null
+++ b/tests/tests/graphics/res/font/ascii_n3em_weight700_italic.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/ascii_o3em_weight800_upright.ttf b/tests/tests/graphics/res/font/ascii_o3em_weight800_upright.ttf
new file mode 100644
index 0000000..993e7fa
--- /dev/null
+++ b/tests/tests/graphics/res/font/ascii_o3em_weight800_upright.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/ascii_p3em_weight800_italic.ttf b/tests/tests/graphics/res/font/ascii_p3em_weight800_italic.ttf
new file mode 100644
index 0000000..f0d54f0
--- /dev/null
+++ b/tests/tests/graphics/res/font/ascii_p3em_weight800_italic.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/ascii_q3em_weight900_upright.ttf b/tests/tests/graphics/res/font/ascii_q3em_weight900_upright.ttf
new file mode 100644
index 0000000..c776c78
--- /dev/null
+++ b/tests/tests/graphics/res/font/ascii_q3em_weight900_upright.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/ascii_r3em_weight900_italic.ttf b/tests/tests/graphics/res/font/ascii_r3em_weight900_italic.ttf
new file mode 100644
index 0000000..02f6246
--- /dev/null
+++ b/tests/tests/graphics/res/font/ascii_r3em_weight900_italic.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/ascii_vf.ttf b/tests/tests/graphics/res/font/ascii_vf.ttf
new file mode 100644
index 0000000..792b7d7
--- /dev/null
+++ b/tests/tests/graphics/res/font/ascii_vf.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/values/attrs.xml b/tests/tests/graphics/res/values/attrs.xml
index 4c3d9db..dbbf3e2 100644
--- a/tests/tests/graphics/res/values/attrs.xml
+++ b/tests/tests/graphics/res/values/attrs.xml
@@ -142,4 +142,5 @@
     <attr name="themeGravity" />
     <attr name="themeTileMode" />
     <attr name="themeAngle" />
+    <attr name="themeVectorDrawableFillColor" />
 </resources>
diff --git a/tests/tests/graphics/res/values/styles.xml b/tests/tests/graphics/res/values/styles.xml
index 80780fb..95c9169 100644
--- a/tests/tests/graphics/res/values/styles.xml
+++ b/tests/tests/graphics/res/values/styles.xml
@@ -164,6 +164,7 @@
         <item name="themeGravity">48</item>
         <item name="themeTileMode">2</item>
         <item name="themeType">0</item>
+        <item name="themeVectorDrawableFillColor">#F00F</item>
     </style>
 
     <style name="WhiteBackgroundNoWindowAnimation"
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java
index 7645e25..2ea6e16 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java
@@ -355,13 +355,15 @@
             ColorSpace cs = b.getColorSpace();
             assertNotNull(cs);
             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
-
+            assertTrue(b.isMutable());
             verifySetPixel(b, newColor, expectedColor);
 
             b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
+            assertTrue(b.isMutable());
             verifySetPixel(b, newColor, expectedColor);
 
             b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
+            assertTrue(b.isMutable());
             verifySetPixel(b, newColor, expectedColor);
         } catch (IOException e) {
             fail();
@@ -370,6 +372,7 @@
 
     private static void verifySetPixel(@NonNull Bitmap b,
             @ColorInt int newColor, @ColorInt int expectedColor) {
+        assertTrue(b.isMutable());
         b.setPixel(0, 0, newColor);
 
         ByteBuffer dst = ByteBuffer.allocate(b.getByteCount());
@@ -399,9 +402,11 @@
             verifySetPixels(b, newColor, expectedColor);
 
             b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
+            assertTrue(b.isMutable());
             verifySetPixels(b, newColor, expectedColor);
 
             b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
+            assertTrue(b.isMutable());
             verifySetPixels(b, newColor, expectedColor);
         } catch (IOException e) {
             fail();
@@ -410,6 +415,7 @@
 
     private static void verifySetPixels(@NonNull Bitmap b,
             @ColorInt int newColor, @ColorInt int expectedColor) {
+        assertTrue(b.isMutable());
         int[] pixels = new int[b.getWidth() * b.getHeight()];
         Arrays.fill(pixels, newColor);
         b.setPixels(pixels, 0, b.getWidth(), 0, 0, b.getWidth(), b.getHeight());
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapRGBAF16Test.java b/tests/tests/graphics/src/android/graphics/cts/BitmapRGBAF16Test.java
index 02c9425..6b9eb5a 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapRGBAF16Test.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapRGBAF16Test.java
@@ -25,6 +25,7 @@
 import android.graphics.Bitmap.Config;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -107,9 +108,9 @@
     @Test
     public void testGetPixel() {
         // Opaque pixels from opaque bitmap
-        assertEquals(0xff0f131f, mOpaqueBitmap.getPixel(0, 0));
-        assertEquals(0xff0f1421, mOpaqueBitmap.getPixel(1, 0));
-        assertEquals(0xff101523, mOpaqueBitmap.getPixel(2, 0));
+        validatePixel(0xff0f131f, mOpaqueBitmap.getPixel(0, 0));
+        validatePixel(0xff0f1421, mOpaqueBitmap.getPixel(1, 0));
+        validatePixel(0xff101523, mOpaqueBitmap.getPixel(2, 0));
 
         // Opaque pixels from transparent bitmap
         assertEquals(0xffff0000, mTransparentBitmap.getPixel(0, 0));
@@ -148,4 +149,11 @@
         Bitmap b = mask.copy(Config.RGBA_F16, false);
         assertNotNull(b);
     }
+
+    private void validatePixel(int expected, int actual) {
+        assertEquals(Color.alpha(expected), Color.alpha(actual));
+        assertEquals(Color.red(expected), Color.red(actual), 1);
+        assertEquals(Color.green(expected), Color.green(actual), 1);
+        assertEquals(Color.blue(expected), Color.blue(actual), 1);
+    }
 }
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
index 1aec304..5289726 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
@@ -30,8 +30,10 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorSpace;
+import android.graphics.ColorSpace.Named;
 import android.graphics.Matrix;
 import android.graphics.Paint;
+import android.graphics.Picture;
 import android.os.Debug;
 import android.os.Parcel;
 import android.os.StrictMode;
@@ -207,8 +209,10 @@
     public void testCreateBitmap1() {
         int[] colors = createColors(100);
         Bitmap bitmap = Bitmap.createBitmap(colors, 10, 10, Config.RGB_565);
+        assertFalse(bitmap.isMutable());
         Bitmap ret = Bitmap.createBitmap(bitmap);
         assertNotNull(ret);
+        assertFalse(ret.isMutable());
         assertEquals(10, ret.getWidth());
         assertEquals(10, ret.getHeight());
         assertEquals(Config.RGB_565, ret.getConfig());
@@ -223,8 +227,10 @@
     public void testCreateBitmap2() {
         // special case: output bitmap is equal to the input bitmap
         mBitmap = Bitmap.createBitmap(new int[100 * 100], 100, 100, Config.ARGB_8888);
+        assertFalse(mBitmap.isMutable()); // createBitmap w/ colors should be immutable
         Bitmap ret = Bitmap.createBitmap(mBitmap, 0, 0, 100, 100);
         assertNotNull(ret);
+        assertFalse(ret.isMutable()); // createBitmap from subset should be immutable
         assertTrue(mBitmap.equals(ret));
 
         //normal case
@@ -277,19 +283,33 @@
         mBitmap = Bitmap.createBitmap(new int[100 * 100], 100, 100, Config.ARGB_8888);
         Bitmap ret = Bitmap.createBitmap(mBitmap, 0, 0, 100, 100, null, false);
         assertNotNull(ret);
+        assertFalse(ret.isMutable()); // subset should be immutable
         assertTrue(mBitmap.equals(ret));
 
         // normal case
         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
         ret = Bitmap.createBitmap(mBitmap, 10, 10, 50, 50, new Matrix(), true);
+        assertTrue(ret.isMutable());
         assertNotNull(ret);
         assertFalse(mBitmap.equals(ret));
     }
 
     @Test
+    public void testCreateBitmapFromHardwareBitmap() {
+        Bitmap hardwareBitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot,
+                HARDWARE_OPTIONS);
+        assertEquals(Config.HARDWARE, hardwareBitmap.getConfig());
+
+        Bitmap ret = Bitmap.createBitmap(hardwareBitmap, 0, 0, 100, 100, null, false);
+        assertEquals(Config.HARDWARE, ret.getConfig());
+        assertFalse(ret.isMutable());
+    }
+
+    @Test
     public void testCreateBitmap4() {
         Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
         assertNotNull(ret);
+        assertTrue(ret.isMutable());
         assertEquals(100, ret.getWidth());
         assertEquals(200, ret.getHeight());
         assertEquals(Config.RGB_565, ret.getConfig());
@@ -306,6 +326,7 @@
     public void testCreateBitmap_matrix() {
         int[] colorArray = new int[] { Color.RED, Color.GREEN, Color.BLUE, Color.BLACK };
         Bitmap src = Bitmap.createBitmap(2, 2, Config.ARGB_8888);
+        assertTrue(src.isMutable());
         src.setPixels(colorArray,0, 2, 0, 0, 2, 2);
 
         // baseline
@@ -313,21 +334,25 @@
 
         // null
         Bitmap dst = Bitmap.createBitmap(src, 0, 0, 2, 2, null, false);
+        assertTrue(dst.isMutable());
         verify2x2BitmapContents(colorArray, dst);
 
         // identity matrix
         Matrix matrix = new Matrix();
         dst = Bitmap.createBitmap(src, 0, 0, 2, 2, matrix, false);
+        assertTrue(dst.isMutable());
         verify2x2BitmapContents(colorArray, dst);
 
         // big scale - only red visible
         matrix.setScale(10, 10);
         dst = Bitmap.createBitmap(src, 0, 0, 2, 2, matrix, false);
+        assertTrue(dst.isMutable());
         verify2x2BitmapContents(new int[] { Color.RED, Color.RED, Color.RED, Color.RED }, dst);
 
         // rotation
         matrix.setRotate(90);
         dst = Bitmap.createBitmap(src, 0, 0, 2, 2, matrix, false);
+        assertTrue(dst.isMutable());
         verify2x2BitmapContents(
                 new int[] { Color.BLUE, Color.RED, Color.BLACK, Color.GREEN }, dst);
     }
@@ -379,6 +404,7 @@
         // normal case
         Bitmap ret = Bitmap.createBitmap(colors, 5, 10, 10, 5, Config.RGB_565);
         assertNotNull(ret);
+        assertFalse(ret.isMutable());
         assertEquals(10, ret.getWidth());
         assertEquals(5, ret.getHeight());
         assertEquals(Config.RGB_565, ret.getConfig());
@@ -409,8 +435,26 @@
         assertEquals(metrics.densityDpi, bitmap.getDensity());
 
         int[] colors = createColors(100);
-        assertNotNull(Bitmap.createBitmap(metrics, colors, 0, 10, 10, 10, Config.ARGB_8888));
-        assertNotNull(Bitmap.createBitmap(metrics, colors, 10, 10, Config.ARGB_8888));
+        bitmap = Bitmap.createBitmap(metrics, colors, 0, 10, 10, 10, Config.ARGB_8888);
+        assertNotNull(bitmap);
+        assertFalse(bitmap.isMutable());
+
+        bitmap = Bitmap.createBitmap(metrics, colors, 10, 10, Config.ARGB_8888);
+        assertNotNull(bitmap);
+        assertFalse(bitmap.isMutable());
+    }
+
+    @Test
+    public void testCreateBitmap_noDisplayMetrics_mutable() {
+        Bitmap bitmap;
+        bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
+        assertTrue(bitmap.isMutable());
+
+        bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888, true);
+        assertTrue(bitmap.isMutable());
+
+        bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888, true, ColorSpace.get(Named.SRGB));
+        assertTrue(bitmap.isMutable());
     }
 
     @Test
@@ -430,12 +474,51 @@
     }
 
     @Test
+    public void testCreateBitmap_noDisplayMetrics_immutable() {
+        int[] colors = createColors(100);
+        Bitmap bitmap;
+        bitmap = Bitmap.createBitmap(colors, 0, 10, 10, 10, Config.ARGB_8888);
+        assertFalse(bitmap.isMutable());
+
+        bitmap = Bitmap.createBitmap(colors, 10, 10, Config.ARGB_8888);
+        assertFalse(bitmap.isMutable());
+    }
+
+    @Test
+    public void testCreateBitmap_Picture_immutable() {
+        Picture picture = new Picture();
+        Canvas canvas = picture.beginRecording(200, 100);
+
+        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+        p.setColor(0x88FF0000);
+        canvas.drawCircle(50, 50, 40, p);
+
+        p.setColor(Color.GREEN);
+        p.setTextSize(30);
+        canvas.drawText("Pictures", 60, 60, p);
+        picture.endRecording();
+
+        Bitmap bitmap;
+        bitmap = Bitmap.createBitmap(picture);
+        assertFalse(bitmap.isMutable());
+
+        bitmap = Bitmap.createBitmap(picture, 100, 100, Config.HARDWARE);
+        assertFalse(bitmap.isMutable());
+
+        bitmap = Bitmap.createBitmap(picture, 100, 100, Config.ARGB_8888);
+        assertFalse(bitmap.isMutable());
+    }
+
+    @Test
     public void testCreateScaledBitmap() {
         mBitmap = Bitmap.createBitmap(100, 200, Config.RGB_565);
+        assertTrue(mBitmap.isMutable());
         Bitmap ret = Bitmap.createScaledBitmap(mBitmap, 50, 100, false);
         assertNotNull(ret);
         assertEquals(50, ret.getWidth());
         assertEquals(100, ret.getHeight());
+        assertTrue(ret.isMutable());
     }
 
     @Test
@@ -1457,6 +1540,14 @@
         nValidateNdkAccessAfterRecycle(bitmap);
     }
 
+    @Test
+    public void bitmapIsMutable() {
+        Bitmap b = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
+        assertTrue("CreateBitmap w/ params should be mutable", b.isMutable());
+        assertTrue("CreateBitmap from bitmap should be mutable",
+                Bitmap.createBitmap(b).isMutable());
+    }
+
     private void runGcAndFinalizersSync() {
         final CountDownLatch fence = new CountDownLatch(1);
         new Object() {
diff --git a/tests/tests/graphics/src/android/graphics/cts/InsetsTest.java b/tests/tests/graphics/src/android/graphics/cts/InsetsTest.java
new file mode 100644
index 0000000..caaddf2
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/cts/InsetsTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.graphics.cts;
+
+import static org.junit.Assert.assertEquals;
+
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class InsetsTest {
+
+    @Test
+    public void testEmptyInsets() {
+        assertEquals(Insets.NONE, Insets.of(0, 0, 0, 0));
+    }
+
+    @Test
+    public void testInsetsAppliedInOrder() {
+        Insets insets = Insets.of(1, 2, 3, 4);
+        assertEquals(1, insets.left);
+        assertEquals(2, insets.top);
+        assertEquals(3, insets.right);
+        assertEquals(4, insets.bottom);
+    }
+
+    @Test
+    public void testInsetsFromNullRect() {
+        assertEquals(Insets.NONE, Insets.of(null));
+    }
+
+    @Test
+    public void testInsetsFromRect() {
+        Rect rect = new Rect(1, 2, 3, 4);
+        Insets insets = Insets.of(rect);
+        assertEquals(1, insets.left);
+        assertEquals(2, insets.top);
+        assertEquals(3, insets.right);
+        assertEquals(4, insets.bottom);
+    }
+
+    @Test
+    public void testInsetsEquality() {
+        Rect rect = new Rect(10, 20, 30, 40);
+        Insets insets1 = Insets.of(rect);
+        Insets insets2 = Insets.of(10, 20, 30, 40);
+        assertEquals(insets1, insets2);
+    }
+}
diff --git a/tests/tests/graphics/src/android/graphics/cts/PictureTest.java b/tests/tests/graphics/src/android/graphics/cts/PictureTest.java
index d83eecf..d566b41 100644
--- a/tests/tests/graphics/src/android/graphics/cts/PictureTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/PictureTest.java
@@ -34,9 +34,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class PictureTest {
@@ -49,7 +46,6 @@
     // In particular, this test verifies that, in the following situations,
     // the created picture (effectively) has balanced saves and restores:
     //   - copy constructed picture from actively recording picture
-    //   - writeToStream/createFromStream created picture from actively recording picture
     //   - actively recording picture after draw call
     @Test
     public void testSaveRestoreBalance() {
@@ -65,17 +61,6 @@
 
         assertEquals(expectedSaveCount, canvas.getSaveCount());
 
-        ByteArrayOutputStream bout = new ByteArrayOutputStream();
-        original.writeToStream(bout);
-
-        assertEquals(expectedSaveCount, canvas.getSaveCount());
-
-        Picture serialized = Picture.createFromStream(new ByteArrayInputStream(bout.toByteArray()));
-        // The serialization/deserialization process will balance the saves and restores
-        verifyBalance(serialized);
-
-        assertEquals(expectedSaveCount, canvas.getSaveCount());
-
         Bitmap bitmap = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
         Canvas drawDest = new Canvas(bitmap);
         original.draw(drawDest);
@@ -118,7 +103,6 @@
     @Test
     public void testPicture() {
         Picture picture = new Picture();
-        ByteArrayOutputStream bout = new ByteArrayOutputStream();
 
         Canvas canvas = picture.beginRecording(TEST_WIDTH, TEST_HEIGHT);
         assertNotNull(canvas);
@@ -131,16 +115,6 @@
         verifySize(picture);
         verifyBitmap(bitmap);
 
-        picture.writeToStream(bout);
-        picture = Picture.createFromStream(new ByteArrayInputStream(bout.toByteArray()));
-
-        // create a new Canvas with a new bitmap
-        bitmap = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
-        canvas = new Canvas(bitmap);
-        picture.draw(canvas);
-        verifySize(picture);
-        verifyBitmap(bitmap);
-
         Picture pic = new Picture(picture);
         bitmap = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
         canvas = new Canvas(bitmap);
diff --git a/tests/tests/graphics/src/android/graphics/cts/VulkanPreTransformCtsActivity.java b/tests/tests/graphics/src/android/graphics/cts/VulkanPreTransformCtsActivity.java
index 0194044..f692a90 100644
--- a/tests/tests/graphics/src/android/graphics/cts/VulkanPreTransformCtsActivity.java
+++ b/tests/tests/graphics/src/android/graphics/cts/VulkanPreTransformCtsActivity.java
@@ -35,7 +35,7 @@
         System.loadLibrary("ctsgraphics_jni");
     }
 
-    private static final String TAG = "vulkan";
+    private static final String TAG = VulkanPreTransformCtsActivity.class.getSimpleName();
 
     private static boolean sOrientationRequested = false;
 
diff --git a/tests/tests/graphics/src/android/graphics/cts/VulkanPreTransformTest.java b/tests/tests/graphics/src/android/graphics/cts/VulkanPreTransformTest.java
index 06f92d3..60ea0eb 100644
--- a/tests/tests/graphics/src/android/graphics/cts/VulkanPreTransformTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/VulkanPreTransformTest.java
@@ -72,7 +72,7 @@
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class VulkanPreTransformTest {
-    private static final String TAG = "vulkan";
+    private static final String TAG = VulkanPreTransformTest.class.getSimpleName();
     private static final boolean DEBUG = false;
     private static VulkanPreTransformCtsActivity sActivity = null;
 
diff --git a/tests/tests/graphics/src/android/graphics/cts/VulkanSurfaceSupportTest.java b/tests/tests/graphics/src/android/graphics/cts/VulkanSurfaceSupportTest.java
new file mode 100644
index 0000000..bd144c7
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/cts/VulkanSurfaceSupportTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.graphics.cts;
+
+import android.content.res.AssetManager;
+import android.graphics.ImageFormat;
+import android.media.ImageReader;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.Surface;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class VulkanSurfaceSupportTest {
+
+    static {
+        System.loadLibrary("ctsgraphics_jni");
+    }
+
+    private static final String TAG = VulkanSurfaceSupportTest.class.getSimpleName();
+    private static final boolean DEBUG = false;
+
+    @Test
+    public void testVulkanUnsupportedFormat() {
+        ImageReader reader = ImageReader.newInstance(640, 480, ImageFormat.YUV_420_888, 3);
+        nCreateNativeTest(
+                InstrumentationRegistry.getContext().getAssets(), reader.getSurface(), false);
+    }
+
+    @Test
+    public void testVulkanSupportedFormat() {
+        ImageReader reader = ImageReader.newInstance(640, 480, ImageFormat.RGB_565, 3);
+        nCreateNativeTest(
+                InstrumentationRegistry.getContext().getAssets(), reader.getSurface(), true);
+    }
+
+    private static native void nCreateNativeTest(
+            AssetManager manager, Surface surface, boolean supported);
+}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableParameterizedTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableParameterizedTest.java
index 422f06d..4b98f22 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableParameterizedTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableParameterizedTest.java
@@ -29,7 +29,6 @@
 import android.graphics.Rect;
 import android.graphics.cts.R;
 import android.graphics.drawable.AnimatedVectorDrawable;
-import androidx.annotation.Nullable;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
@@ -226,7 +225,7 @@
         });
     }
 
-    @MediumTest
+    @LargeTest
     @Test
     public void testEmptyAnimatorSet() throws Throwable {
         int resId = R.drawable.avd_empty_animator;
@@ -246,7 +245,7 @@
         AnimatedVectorDrawableTest.waitForAVDStop(callback, MAX_TIMEOUT_MS);
         // Check that the AVD with empty AnimatorSet has finished
         callback.assertEnded(true);
-        callback.assertAVDRuntime(0, TimeUnit.MILLISECONDS.toNanos(64)); // 4 frames
+        callback.assertAVDRuntime(0, TimeUnit.MILLISECONDS.toNanos(300));
     }
 
     // Does a fuzzy comparison between two images.
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableTest.java
index f399cf6..2cd8bc0 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableTest.java
@@ -28,13 +28,12 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Insets;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.cts.R;
-import android.graphics.drawable.Animatable2;
 import android.graphics.drawable.AnimatedVectorDrawable;
-import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Drawable.ConstantState;
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.SmallTest;
@@ -108,6 +107,17 @@
         }
     }
 
+    @SmallTest
+    @Test
+    public void testGetOpticalInsets() throws Exception {
+        XmlPullParser parser = mResources.getXml(mResId);
+        AttributeSet attrs = Xml.asAttributeSet(parser);
+        AnimatedVectorDrawable drawable = new AnimatedVectorDrawable();
+        drawable.inflate(mResources, parser, attrs);
+
+        assertEquals(Insets.of(10, 20, 30, 40), drawable.getOpticalInsets());
+    }
+
     @Test
     public void testGetChangingConfigurations() {
         AnimatedVectorDrawable avd = new AnimatedVectorDrawable();
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
index 536c16f..fbafcf4 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
@@ -25,43 +25,36 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import android.content.res.Resources.Theme;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
 import android.graphics.BitmapFactory;
-import android.graphics.Rect;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Insets;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.Shader;
+import android.graphics.Shader.TileMode;
 import android.graphics.cts.R;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable.ConstantState;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.AttributeSet;
+import android.view.Gravity;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.xmlpull.v1.XmlPullParserException;
 
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.XmlResourceParser;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.Shader;
-import android.graphics.Bitmap.Config;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.Shader.TileMode;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Drawable.ConstantState;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.util.AttributeSet;
-import android.util.LayoutDirection;
-import android.util.Xml;
-import android.view.Gravity;
-
 import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
@@ -143,6 +136,23 @@
     }
 
     @Test
+    public void testBitmapDrawableOpticalInset() {
+        InputStream source = mContext.getResources().openRawResource(R.raw.testimage);
+        BitmapDrawable bitmapDrawable = new BitmapDrawable(source);
+
+        int intrinsicWidth = bitmapDrawable.getIntrinsicWidth();
+        int intrinsicHeight = bitmapDrawable.getIntrinsicHeight();
+
+        bitmapDrawable.setGravity(Gravity.CENTER);
+        bitmapDrawable.setBounds(0, 0, intrinsicWidth * 3, intrinsicHeight * 3);
+
+        Insets opticalInsets = bitmapDrawable.getOpticalInsets();
+        Insets expected = Insets.of(intrinsicWidth, intrinsicHeight, intrinsicWidth,
+                    intrinsicHeight);
+        assertEquals(expected, opticalInsets);
+    }
+
+    @Test
     public void testAccessMipMap() {
         Bitmap source = BitmapFactory.decodeResource(mContext.getResources(), R.raw.testimage);
         BitmapDrawable bitmapDrawable = new BitmapDrawable(source);
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/ColorDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/ColorDrawableTest.java
index ee34254..675f15c 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/ColorDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/ColorDrawableTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.fail;
 
 import android.content.res.Resources;
@@ -148,6 +149,15 @@
     }
 
     @Test
+    public void testGetColorFilter() {
+        final ColorDrawable d = new ColorDrawable(Color.WHITE);
+        PorterDuffColorFilter colorFilter = new PorterDuffColorFilter(Color.BLACK, Mode.SRC_OVER);
+        d.setColorFilter(colorFilter);
+
+        assertSame(colorFilter, d.getColorFilter());
+    }
+
+    @Test
     public void testSetTint() {
         final ColorDrawable d = new ColorDrawable(Color.WHITE);
         assertEquals(Color.WHITE, DrawableTestUtils.getPixel(d, 0, 0));
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java
index 9b93a48..b8f5cbb 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java
@@ -41,6 +41,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
+import android.graphics.Insets;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuff.Mode;
@@ -53,6 +54,8 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
+import androidx.annotation.Nullable;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -825,6 +828,22 @@
         assertEquals(true, mDrawableContainer.isStateful());
     }
 
+    @Test
+    public void testGetOpticalBoundsWithNoInternalDrawable() {
+        DrawableContainer container = new DrawableContainer();
+        assertEquals(Insets.NONE, container.getOpticalInsets());
+    }
+
+    @Test
+    public void testGetOpticalBoundsFromInternalDrawable() {
+        mMockDrawableContainer.setConstantState(mDrawableContainerState);
+        MockDrawable mockDrawable = new MockDrawable();
+        mockDrawable.setInsets(Insets.of(20, 40, 60, 100));
+
+        addAndSelectDrawable(mockDrawable);
+        assertEquals(Insets.of(20, 40, 60, 100), mDrawableContainer.getOpticalInsets());
+    }
+
     private void addAndSelectDrawable(Drawable drawable) {
         int pos = mDrawableContainerState.addChild(drawable);
         mDrawableContainer.selectDrawable(pos);
@@ -861,6 +880,8 @@
         private boolean mHasCalledOnStateChanged;
         private boolean mHasCalledOnLevelChanged;
 
+        private Insets mInsets = null;
+
         @Override
         public int getOpacity() {
             return PixelFormat.OPAQUE;
@@ -878,6 +899,15 @@
         public void setColorFilter(ColorFilter colorFilter) {
         }
 
+        public void setInsets(@Nullable Insets insets) {
+            mInsets = insets;
+        }
+
+        @Override
+        public Insets getOpticalInsets() {
+            return mInsets != null ? mInsets : Insets.NONE;
+        }
+
         public boolean hasOnBoundsChangedCalled() {
             return mHasCalledOnBoundsChanged;
         }
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java
index 3c26f39..4fd64b8 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java
@@ -16,44 +16,6 @@
 
 package android.graphics.drawable.cts;
 
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.Resources.Theme;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.cts.R;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Drawable.Callback;
-import android.net.Uri;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.AndroidTestCase;
-import android.util.AttributeSet;
-import android.util.StateSet;
-import android.util.TypedValue;
-import android.util.Xml;
-import android.view.View;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -66,6 +28,43 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Insets;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.cts.R;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Drawable.Callback;
+import android.net.Uri;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.AttributeSet;
+import android.util.StateSet;
+import android.util.TypedValue;
+import android.util.Xml;
+import android.view.View;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DrawableTest {
@@ -722,6 +721,12 @@
         assertSame(mockDrawable, mockDrawable.mutate());
     }
 
+    @Test
+    public void testDefaultOpticalInsetsIsNone() {
+        Drawable mockDrawable = new MockDrawable();
+        assertEquals(Insets.NONE, mockDrawable.getOpticalInsets());
+    }
+
     // Since Mockito can't mock or spy on protected methods, we have a custom extension
     // of Drawable to track calls to protected methods. This class also has empty implementations
     // of the base abstract methods.
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableWrapperTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableWrapperTest.java
index c7acccc..6987b01 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableWrapperTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableWrapperTest.java
@@ -35,6 +35,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
+import android.graphics.Insets;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
@@ -49,6 +50,8 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.util.StateSet;
 
+import androidx.annotation.Nullable;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -377,6 +380,21 @@
         verify(mockDrawable, times(1)).getIntrinsicHeight();
     }
 
+    @Test
+    public void testGetOpticalInsetsNoInternalDrawable() {
+        DrawableWrapper wrapper = new MockDrawableWrapper(null);
+        assertEquals(Insets.NONE, wrapper.getOpticalInsets());
+    }
+
+    @Test
+    public void testGetOpticalInsetsFromInternalDrawable() {
+        MockDrawable drawable = new MockDrawable();
+        drawable.setInsets(Insets.of(30, 60, 90, 120));
+        DrawableWrapper wrapper = new MockDrawableWrapper(drawable);
+
+        assertEquals(Insets.of(30, 60, 90, 120), wrapper.getOpticalInsets());
+    }
+
     @SuppressWarnings("deprecation")
     @Test
     public void testGetConstantState() {
@@ -390,6 +408,7 @@
     private static class MockDrawable extends Drawable {
         private boolean mCalledOnLevelChange = false;
         private ColorFilter mColorFilter;
+        private Insets mInsets = null;
 
         @Override
         public void draw(Canvas canvas) {
@@ -414,6 +433,15 @@
             return mColorFilter;
         }
 
+        public void setInsets(@Nullable Insets insets) {
+            mInsets = insets;
+        }
+
+        @Override
+        public Insets getOpticalInsets() {
+            return mInsets != null ? mInsets : Insets.NONE;
+        }
+
         @Override
         protected boolean onLevelChange(int level) {
             mCalledOnLevelChange = true;
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 4a05376..bf91296 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
@@ -30,6 +30,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
+import android.graphics.Insets;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.cts.R;
@@ -367,9 +368,9 @@
         gradientDrawable.setColor(color);
         assertEquals("Color was set to RED", color, gradientDrawable.getColor());
 
-        color = null;
-        gradientDrawable.setColor(color);
-        assertEquals("Color was set to null (TRANSPARENT)", color, gradientDrawable.getColor());
+        gradientDrawable.setColor(null);
+        assertEquals("Color was set to null (TRANSPARENT)",
+                ColorStateList.valueOf(Color.TRANSPARENT), gradientDrawable.getColor());
     }
 
     @Test
@@ -557,6 +558,13 @@
         }
     }
 
+    @Test
+    public void testOpticalInsets() {
+        GradientDrawable drawable =
+                (GradientDrawable) mResources.getDrawable(R.drawable.gradientdrawable);
+        assertEquals(Insets.of(1, 2, 3, 4), drawable.getOpticalInsets());
+    }
+
     private void verifyPreloadDensityInner(Resources res, int densityDpi)
             throws XmlPullParserException, IOException {
         final Rect tempPadding = new Rect();
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/InsetDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/InsetDrawableTest.java
index b1c1f03..01685a8 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/InsetDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/InsetDrawableTest.java
@@ -26,15 +26,13 @@
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.XmlResourceParser;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
+import android.graphics.Insets;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.cts.R;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable.ConstantState;
 import android.graphics.drawable.InsetDrawable;
 import android.support.test.InstrumentationRegistry;
@@ -459,6 +457,12 @@
         }
     }
 
+    @Test
+    public void testOpticalInsets() {
+        InsetDrawable drawable = new InsetDrawable(mPassDrawable, 1, 2, 3, 4);
+        assertEquals(Insets.of(1, 2, 3, 4), drawable.getOpticalInsets());
+    }
+
     private void verifyPreloadDensityInner(Resources res, int densityDpi)
             throws XmlPullParserException, IOException {
         // Capture initial state at default density.
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawableTest.java
index d563102..11f8fc5 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawableTest.java
@@ -373,7 +373,7 @@
         a.setShape(new OvalShape());
 
         ShapeDrawable b = (ShapeDrawable) a.getConstantState().newDrawable();
-        assertSame(a.getShape(), b.getShape());
+        assertEquals(a.getShape(), b.getShape());
         a.mutate();
 
         assertNotNull(a.getShape());
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/StateListDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/StateListDrawableTest.java
index c96a1ca..a7b20c6 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/StateListDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/StateListDrawableTest.java
@@ -30,6 +30,7 @@
 import android.graphics.cts.R;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Drawable.ConstantState;
 import android.graphics.drawable.DrawableContainer.DrawableContainerState;
 import android.graphics.drawable.StateListDrawable;
@@ -332,6 +333,66 @@
     }
 
     @Test
+    public void testGetStateCount() {
+        StateListDrawable stateList = new StateListDrawable();
+        stateList.addState(new int[]{0}, new ColorDrawable(Color.RED));
+
+        assertEquals(1, stateList.getStateCount());
+
+        stateList.addState(new int[]{1}, new ColorDrawable(Color.GREEN));
+
+        assertEquals(2, stateList.getStateCount());
+
+        stateList.addState(new int[]{2}, new ColorDrawable(Color.BLUE));
+
+        assertEquals(3, stateList.getStateCount());
+    }
+
+    @Test
+    public void testGetStateDrawable() {
+        StateListDrawable stateList = new StateListDrawable();
+
+        ColorDrawable colorDrawable = new ColorDrawable(Color.RED);
+        int[] stateSet = new int[]{1};
+        stateList.addState(stateSet, colorDrawable);
+
+        Drawable drawable = stateList.getStateDrawable(0);
+        assertSame(colorDrawable, drawable);
+    }
+
+    @Test
+    public void testGetStateSet() {
+        StateListDrawable stateList = new StateListDrawable();
+
+        ColorDrawable colorDrawable = new ColorDrawable(Color.GREEN);
+        int[] stateSet = new int[]{0};
+
+        stateList.addState(stateSet, colorDrawable);
+        int[] resolvedStateSet = stateList.getStateSet(0);
+        assertEquals(stateSet, resolvedStateSet);
+    }
+
+    @Test
+    public void testGetStateDrawableIndex() {
+        StateListDrawable stateList = new StateListDrawable();
+
+        ColorDrawable drawable1 = new ColorDrawable(Color.CYAN);
+        ColorDrawable drawable2 = new ColorDrawable(Color.YELLOW);
+        ColorDrawable drawable3 = new ColorDrawable(Color.GREEN);
+        int[] stateSet1 = new int[]{42};
+        int[] stateSet2 = new int[]{27};
+        int[] stateSet3 = new int[]{57};
+
+        stateList.addState(stateSet1, drawable1);
+        stateList.addState(stateSet2, drawable2);
+        stateList.addState(stateSet3, drawable3);
+
+        assertEquals(0, stateList.getStateDrawableIndex(stateSet1));
+        assertEquals(1, stateList.getStateDrawableIndex(stateSet2));
+        assertEquals(2, stateList.getStateDrawableIndex(stateSet3));
+    }
+
+    @Test
     public void testMutate() {
         StateListDrawable d1 =
             (StateListDrawable) mResources.getDrawable(R.drawable.statelistdrawable);
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/ThemedDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/ThemedDrawableTest.java
index 1a3f774..72a7cdb2 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/ThemedDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/ThemedDrawableTest.java
@@ -23,6 +23,8 @@
 import android.content.Context;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.Shader.TileMode;
@@ -33,6 +35,7 @@
 import android.graphics.drawable.LayerDrawable;
 import android.graphics.drawable.NinePatchDrawable;
 import android.graphics.drawable.RippleDrawable;
+import android.graphics.drawable.VectorDrawable;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -170,4 +173,14 @@
         NinePatchDrawable ninePatchDrawable = (NinePatchDrawable) d.getDrawable(1);
         verifyNinePatchDrawable(ninePatchDrawable);
     }
+
+    @Test
+    public void testVectorDrawableWithStateListColorThemeAttrs() {
+        VectorDrawable d = (VectorDrawable) mContext.getDrawable(R.drawable.heart);
+        d.setBounds(0, 0, 64, 64);
+        Bitmap bitmap = Bitmap.createBitmap(64, 64, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+        d.draw(canvas);
+        assertEquals(0xff0000ff, bitmap.getPixel(32, 32));
+    }
 }
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java
index 76b6e2c..673a1b1 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java
@@ -27,28 +27,27 @@
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Insets;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff.Mode;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.cts.R;
 import android.graphics.drawable.Drawable.ConstantState;
 import android.graphics.drawable.VectorDrawable;
-import androidx.annotation.Nullable;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.util.Xml;
 
+import androidx.annotation.Nullable;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
-import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
 
 @SmallTest
@@ -425,6 +424,13 @@
         }
     }
 
+    @Test
+    public void testOpticalInsets() {
+        VectorDrawable drawable =
+                (VectorDrawable) mResources.getDrawable(R.drawable.vector_icon_create);
+        assertEquals(Insets.of(1, 2, 3, 4), drawable.getOpticalInsets());
+    }
+
     private void verifyPreloadDensityInner(Resources res, int densityDpi)
             throws XmlPullParserException, IOException {
         // Capture initial state at default density.
diff --git a/tests/tests/graphics/src/android/graphics/fonts/FontFamilyTest.java b/tests/tests/graphics/src/android/graphics/fonts/FontFamilyTest.java
new file mode 100644
index 0000000..8f8b70f
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/fonts/FontFamilyTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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.graphics.fonts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import android.content.res.AssetManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class FontFamilyTest {
+    private static final String TAG = "FontFamilyTest";
+    private static final String FONT_DIR = "fonts_for_family_selection/fonts/";
+
+    @Test
+    public void testBuilder_SingleFont() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        Font font = new Font.Builder(am, FONT_DIR + "ascii_g3em_weight400_upright.ttf").build();
+        FontFamily family = new FontFamily.Builder(font).build();
+        assertNotNull(family);
+        assertEquals(1, family.getFontCount());
+        assertSame(font, family.getFont(0));
+    }
+
+    @Test
+    public void testBuilder_MultipleFont() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        Font regularFont = new Font.Builder(
+                am, FONT_DIR + "ascii_g3em_weight400_upright.ttf").build();
+        Font boldFont = new Font.Builder(
+                am, FONT_DIR + "ascii_m3em_weight700_upright.ttf").build();
+        FontFamily family = new FontFamily.Builder(regularFont).addFont(boldFont).build();
+        assertNotNull(family);
+        assertEquals(2, family.getFontCount());
+        assertNotSame(family.getFont(0), family.getFont(1));
+        assertTrue(family.getFont(0) == regularFont || family.getFont(0) == boldFont);
+        assertTrue(family.getFont(1) == regularFont || family.getFont(1) == boldFont);
+    }
+
+    @Test
+    public void testBuilder_MultipleFont_overrideWeight() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        Font regularFont = new Font.Builder(
+                am, FONT_DIR + "ascii_g3em_weight400_upright.ttf").build();
+        Font boldFont = new Font.Builder(am, FONT_DIR + "ascii_g3em_weight400_upright.ttf")
+                .setWeight(700).build();
+        FontFamily family = new FontFamily.Builder(regularFont).addFont(boldFont).build();
+        assertNotNull(family);
+        assertEquals(2, family.getFontCount());
+        assertNotSame(family.getFont(0), family.getFont(1));
+        assertTrue(family.getFont(0) == regularFont || family.getFont(0) == boldFont);
+        assertTrue(family.getFont(1) == regularFont || family.getFont(1) == boldFont);
+    }
+
+    @Test
+    public void testBuilder_MultipleFont_overrideItalic() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        Font regularFont = new Font.Builder(
+                am, FONT_DIR + "ascii_g3em_weight400_upright.ttf").build();
+        Font italicFont = new Font.Builder(am, FONT_DIR + "ascii_g3em_weight400_upright.ttf")
+                .setItalic(true).build();
+        FontFamily family = new FontFamily.Builder(regularFont).addFont(italicFont).build();
+        assertNotNull(family);
+        assertEquals(2, family.getFontCount());
+        assertNotSame(family.getFont(0), family.getFont(1));
+        assertTrue(family.getFont(0) == regularFont || family.getFont(0) == italicFont);
+        assertTrue(family.getFont(1) == regularFont || family.getFont(1) == italicFont);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testBuilder_MultipleFont_SameStyle() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        Font regularFont = new Font.Builder(
+                am, FONT_DIR + "ascii_g3em_weight400_upright.ttf").build();
+        Font regularFont2 = new Font.Builder(
+                am, FONT_DIR + "ascii_g3em_weight400_upright.ttf").build();
+        new FontFamily.Builder(regularFont).addFont(regularFont2).build();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testBuilder_MultipleFont_SameStyle_overrideWeight() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        Font regularFont = new Font.Builder(
+                am, FONT_DIR + "ascii_g3em_weight400_upright.ttf").build();
+        Font regularFont2 = new Font.Builder(am, FONT_DIR + "ascii_m3em_weight700_upright.ttf")
+                .setWeight(400).build();
+        new FontFamily.Builder(regularFont).addFont(regularFont2).build();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testBuilder_MultipleFont_SameStyle_overrideItalic() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        Font regularFont = new Font.Builder(
+                am, FONT_DIR + "ascii_g3em_weight400_upright.ttf").build();
+        Font regularFont2 = new Font.Builder(am, FONT_DIR + "ascii_h3em_weight400_italic.ttf")
+                .setItalic(false).build();
+        new FontFamily.Builder(regularFont).addFont(regularFont2).build();
+    }
+}
diff --git a/tests/tests/graphics/src/android/graphics/fonts/FontTest.java b/tests/tests/graphics/src/android/graphics/fonts/FontTest.java
new file mode 100644
index 0000000..9061e25
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/fonts/FontTest.java
@@ -0,0 +1,847 @@
+/*
+ * 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.graphics.fonts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.graphics.cts.R;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+import android.util.Pair;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class FontTest {
+    private static final String TAG = "FontFileUtilTest";
+    private static final String CACHE_FILE_PREFIX = ".font";
+
+    /**
+     * Create new temporary file.
+     *
+     * Caller must delete the file after used.
+     */
+    private static File getTempFile() {
+        Context ctx = InstrumentationRegistry.getTargetContext();
+        final String prefix = CACHE_FILE_PREFIX;
+        for (int i = 0; i < 100; ++i) {
+            final File file = new File(ctx.getCacheDir(), prefix + i);
+            try {
+                if (file.createNewFile()) {
+                    return file;
+                }
+            } catch (IOException e) {
+                // ignore. Try next file.
+            }
+        }
+        return null;
+    }
+
+    private static ByteBuffer mmap(AssetManager am, String path) {
+        File file = getTempFile();
+        try (InputStream is = am.open(path)) {
+            if (!copyToFile(file, is)) {
+                return null;
+            }
+            return mmap(file);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to open assets");
+            return null;
+        } finally {
+            file.delete();
+        }
+    }
+
+    private static ByteBuffer mmap(File file) {
+        try (FileInputStream fis = new FileInputStream(file)) {
+            FileChannel channel = fis.getChannel();
+            return channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    private static boolean copyToFile(File file, InputStream is) {
+        return copyToFile(file, is, null, null);
+    }
+
+    private static boolean copyToFile(File file, InputStream is, byte[] prepend, byte[] append) {
+        try (FileOutputStream os = new FileOutputStream(file, false)) {
+            byte[] buffer = new byte[1024];
+            int readLen;
+            if (prepend != null) {
+                os.write(prepend, 0, prepend.length);
+            }
+            while ((readLen = is.read(buffer)) != -1) {
+                os.write(buffer, 0, readLen);
+            }
+            if (append != null) {
+                os.write(append, 0, append.length);
+            }
+            return true;
+        } catch (IOException e) {
+            Log.e(TAG, "Error copying resource contents to temp file: " + e.getMessage());
+            return false;
+        }
+    }
+
+    @Test
+    public void testBuilder_buffer() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getFontPathFromStyle(weight, italic);
+
+            ByteBuffer buffer = mmap(am, path);
+
+            Font font = new Font.Builder(buffer).build();
+            assertEquals(path, weight, font.getWeight());
+            assertEquals(path, italic, font.isItalic());
+            assertEquals(path, 0, font.getTtcIndex());
+            assertNull(path, font.getAxes());
+        }
+    }
+
+    @Test
+    public void testBuilder_buffer_ttc() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getTtcFontFileInAsset();
+
+            ByteBuffer buffer = mmap(am, path);
+            int ttcIndex = FontTestUtil.getTtcIndexFromStyle(weight, italic);
+
+            Font font = new Font.Builder(buffer).setTtcIndex(ttcIndex).build();
+            assertEquals(path, weight, font.getWeight());
+            assertEquals(path, italic, font.isItalic());
+            assertEquals(path, ttcIndex, font.getTtcIndex());
+            assertNull(path, font.getAxes());
+        }
+    }
+
+    @Test
+    public void testBuilder_buffer_vf() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getVFFontInAsset();
+
+            ByteBuffer buffer = mmap(am, path);
+            FontVariationAxis[] axes = FontVariationAxis.fromFontVariationSettings(
+                    FontTestUtil.getVarSettingsFromStyle(weight, italic));
+
+            Font font = new Font.Builder(buffer).setFontVariationSettings(axes).build();
+            assertEquals(path, weight, font.getWeight());
+            assertEquals(path, italic, font.isItalic());
+            assertEquals(path, 0, font.getTtcIndex());
+            assertEquals(path, axes, font.getAxes());
+        }
+    }
+
+    @Test
+    public void testBuilder_buffer_override() throws IOException {
+        int customWeight = 350;
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getFontPathFromStyle(weight, italic);
+
+            ByteBuffer buffer = mmap(am, path);
+
+            Font font = new Font.Builder(buffer).setWeight(customWeight).build();
+            assertEquals(path, customWeight, font.getWeight());
+            assertEquals(path, italic, font.isItalic());
+            assertEquals(path, 0, font.getTtcIndex());
+            assertNull(path, font.getAxes());
+        }
+
+        boolean customItalic = true;
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getFontPathFromStyle(weight, italic);
+
+            ByteBuffer buffer = mmap(am, path);
+
+            Font font = new Font.Builder(buffer).setItalic(customItalic).build();
+            assertEquals(path, weight, font.getWeight());
+            assertEquals(path, customItalic, font.isItalic());
+            assertEquals(path, 0, font.getTtcIndex());
+            assertNull(path, font.getAxes());
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testBuilder_buffer_invalid_null() throws IOException {
+        ByteBuffer buf = null;
+        new Font.Builder(buf);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testBuilder_buffer_invalid_not_direct() throws IOException {
+        ByteBuffer buf = ByteBuffer.allocate(1024);
+        new Font.Builder(buf);
+    }
+
+    @Test
+    public void testBuilder_file() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getFontPathFromStyle(weight, italic);
+
+            File file = getTempFile();
+            try (InputStream is = am.open(path)) {
+                assertTrue(copyToFile(file, is));
+
+                Font font = new Font.Builder(file).build();
+                assertEquals(path, weight, font.getWeight());
+                assertEquals(path, italic, font.isItalic());
+                assertEquals(path, 0, font.getTtcIndex());
+                assertNull(path, font.getAxes());
+            } finally {
+                file.delete();
+            }
+        }
+    }
+
+    @Test
+    public void testBuilder_file_ttc() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getTtcFontFileInAsset();
+            int ttcIndex = FontTestUtil.getTtcIndexFromStyle(weight, italic);
+
+            File file = getTempFile();
+            try (InputStream is = am.open(path)) {
+                assertTrue(copyToFile(file, is));
+
+                Font font = new Font.Builder(file).setTtcIndex(ttcIndex).build();
+                assertEquals(path, weight, font.getWeight());
+                assertEquals(path, italic, font.isItalic());
+                assertEquals(path, ttcIndex, font.getTtcIndex());
+                assertNull(path, font.getAxes());
+            } finally {
+                file.delete();
+            }
+        }
+    }
+
+    @Test
+    public void testBuilder_file_vf() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getVFFontInAsset();
+            FontVariationAxis[] axes = FontVariationAxis.fromFontVariationSettings(
+                    FontTestUtil.getVarSettingsFromStyle(weight, italic));
+
+            File file = getTempFile();
+            try (InputStream is = am.open(path)) {
+                assertTrue(copyToFile(file, is));
+
+                Font font = new Font.Builder(file).setFontVariationSettings(axes).build();
+                assertEquals(path, weight, font.getWeight());
+                assertEquals(path, italic, font.isItalic());
+                assertEquals(path, 0, font.getTtcIndex());
+                assertEquals(path, axes, font.getAxes());
+            } finally {
+                file.delete();
+            }
+        }
+    }
+
+    @Test
+    public void testBuilder_file_override() throws IOException {
+        int customWeight = 350;
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getFontPathFromStyle(weight, italic);
+
+            File file = getTempFile();
+            try (InputStream is = am.open(path)) {
+                assertTrue(copyToFile(file, is));
+
+                Font font = new Font.Builder(file).setWeight(customWeight).build();
+                assertEquals(path, customWeight, font.getWeight());
+                assertEquals(path, italic, font.isItalic());
+                assertEquals(path, 0, font.getTtcIndex());
+                assertNull(path, font.getAxes());
+            } finally {
+                file.delete();
+            }
+        }
+        boolean customItalic = true;
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getFontPathFromStyle(weight, italic);
+
+            File file = getTempFile();
+            try (InputStream is = am.open(path)) {
+                assertTrue(copyToFile(file, is));
+
+                Font font = new Font.Builder(file).setItalic(customItalic).build();
+                assertEquals(path, weight, font.getWeight());
+                assertEquals(path, customItalic, font.isItalic());
+                assertEquals(path, 0, font.getTtcIndex());
+                assertNull(path, font.getAxes());
+            } finally {
+                file.delete();
+            }
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testBuilder_file_invalid_null_file() throws IOException {
+        File file = null;
+        new Font.Builder(file);
+    }
+
+    @Test(expected = IOException.class)
+    public void testBuilder_file_invalid_not_found_file() throws IOException {
+        File file = new File("/no/such/file");
+        new Font.Builder(file);
+    }
+
+    @Test
+    public void testBuilder_fd() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getFontPathFromStyle(weight, italic);
+
+            File file = getTempFile();
+            try (InputStream is = am.open(path)) {
+                assertTrue(copyToFile(file, is));
+
+                try (FileInputStream fis = new FileInputStream(file)) {
+                    Font font = new Font.Builder(fis.getFD()).build();
+                    assertEquals(path, weight, font.getWeight());
+                    assertEquals(path, italic, font.isItalic());
+                    assertEquals(path, 0, font.getTtcIndex());
+                    assertNull(path, font.getAxes());
+                }
+            } finally {
+                file.delete();
+            }
+        }
+    }
+
+    @Test
+    public void testBuilder_fd_ttc() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getTtcFontFileInAsset();
+            int ttcIndex = FontTestUtil.getTtcIndexFromStyle(weight, italic);
+
+            File file = getTempFile();
+            try (InputStream is = am.open(path)) {
+                assertTrue(copyToFile(file, is));
+
+                try (FileInputStream fis = new FileInputStream(file)) {
+                    Font font = new Font.Builder(fis.getFD()).setTtcIndex(ttcIndex).build();
+                    assertEquals(path, weight, font.getWeight());
+                    assertEquals(path, italic, font.isItalic());
+                    assertEquals(path, ttcIndex, font.getTtcIndex());
+                    assertNull(path, font.getAxes());
+                }
+            } finally {
+                file.delete();
+            }
+        }
+    }
+
+    @Test
+    public void testBuilder_fd_vf() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getVFFontInAsset();
+            FontVariationAxis[] axes = FontVariationAxis.fromFontVariationSettings(
+                    FontTestUtil.getVarSettingsFromStyle(weight, italic));
+
+            File file = getTempFile();
+            try (InputStream is = am.open(path)) {
+                assertTrue(copyToFile(file, is));
+
+                try (FileInputStream fis = new FileInputStream(file)) {
+                    Font font = new Font.Builder(fis.getFD()).setFontVariationSettings(axes)
+                            .build();
+                    assertEquals(path, weight, font.getWeight());
+                    assertEquals(path, italic, font.isItalic());
+                    assertEquals(path, 0, font.getTtcIndex());
+                    assertEquals(path, axes, font.getAxes());
+                }
+            } finally {
+                file.delete();
+            }
+        }
+    }
+
+    @Test
+    public void testBuilder_fd_override() throws IOException {
+        int customWeight = 350;
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getFontPathFromStyle(weight, italic);
+
+            File file = getTempFile();
+            try (InputStream is = am.open(path)) {
+                assertTrue(copyToFile(file, is));
+
+                try (FileInputStream fis = new FileInputStream(file)) {
+                    Font font = new Font.Builder(fis.getFD()).setWeight(customWeight).build();
+                    assertEquals(path, customWeight, font.getWeight());
+                    assertEquals(path, italic, font.isItalic());
+                    assertEquals(path, 0, font.getTtcIndex());
+                    assertNull(path, font.getAxes());
+                }
+            } finally {
+                file.delete();
+            }
+        }
+        boolean customItalic = true;
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getFontPathFromStyle(weight, italic);
+
+            File file = getTempFile();
+            try (InputStream is = am.open(path)) {
+                assertTrue(copyToFile(file, is));
+
+                try (FileInputStream fis = new FileInputStream(file)) {
+                    Font font = new Font.Builder(fis.getFD()).setItalic(customItalic).build();
+                    assertEquals(path, weight, font.getWeight());
+                    assertEquals(path, customItalic, font.isItalic());
+                    assertEquals(path, 0, font.getTtcIndex());
+                    assertNull(path, font.getAxes());
+                }
+            } finally {
+                file.delete();
+            }
+        }
+    }
+
+    @Test
+    public void testBuilder_fd_subdata() throws IOException {
+        byte[] dummy = { (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef };
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getFontPathFromStyle(weight, italic);
+
+            File file = getTempFile();
+            try (InputStream is = am.open(path)) {
+                assertTrue(copyToFile(file, is, dummy, dummy));
+
+                try (FileInputStream fis = new FileInputStream(file)) {
+                    Font font = new Font.Builder(
+                            fis.getFD(), dummy.length, file.length() - dummy.length * 2).build();
+                    assertEquals(path, weight, font.getWeight());
+                    assertEquals(path, italic, font.isItalic());
+                    assertEquals(path, 0, font.getTtcIndex());
+                    assertNull(path, font.getAxes());
+                }
+            } finally {
+                file.delete();
+            }
+        }
+    }
+
+    @Test
+    public void testBuilder_fd_subdata_ttc() throws IOException {
+        byte[] dummy = { (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef };
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getTtcFontFileInAsset();
+            int ttcIndex = FontTestUtil.getTtcIndexFromStyle(weight, italic);
+
+            File file = getTempFile();
+            try (InputStream is = am.open(path)) {
+                assertTrue(copyToFile(file, is, dummy, dummy));
+
+                try (FileInputStream fis = new FileInputStream(file)) {
+                    Font font = new Font.Builder(
+                            fis.getFD(), dummy.length, file.length() - dummy.length * 2)
+                            .setTtcIndex(ttcIndex).build();
+                    assertEquals(path, weight, font.getWeight());
+                    assertEquals(path, italic, font.isItalic());
+                    assertEquals(path, ttcIndex, font.getTtcIndex());
+                    assertNull(path, font.getAxes());
+                }
+            } finally {
+                file.delete();
+            }
+        }
+    }
+
+    @Test
+    public void testBuilder_fd_subdata_vf() throws IOException {
+        byte[] dummy = { (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef };
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getVFFontInAsset();
+            FontVariationAxis[] axes = FontVariationAxis.fromFontVariationSettings(
+                    FontTestUtil.getVarSettingsFromStyle(weight, italic));
+
+            File file = getTempFile();
+            try (InputStream is = am.open(path)) {
+                assertTrue(copyToFile(file, is, dummy, dummy));
+
+                try (FileInputStream fis = new FileInputStream(file)) {
+                    Font font = new Font.Builder(
+                            fis.getFD(), dummy.length, file.length() - dummy.length * 2)
+                            .setFontVariationSettings(axes).build();
+                    assertEquals(path, weight, font.getWeight());
+                    assertEquals(path, italic, font.isItalic());
+                    assertEquals(path, 0, font.getTtcIndex());
+                    assertEquals(path, axes, font.getAxes());
+                }
+            } finally {
+                file.delete();
+            }
+        }
+    }
+
+    @Test
+    public void testBuilder_fd_subdata_override() throws IOException {
+        int customWeight = 350;
+        byte[] dummy = { (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef };
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getFontPathFromStyle(weight, italic);
+
+            File file = getTempFile();
+            try (InputStream is = am.open(path)) {
+                assertTrue(copyToFile(file, is, dummy, dummy));
+
+                try (FileInputStream fis = new FileInputStream(file)) {
+                    Font font = new Font.Builder(
+                            fis.getFD(), dummy.length, file.length() - dummy.length * 2)
+                            .setWeight(customWeight).build();
+                    assertEquals(path, customWeight, font.getWeight());
+                    assertEquals(path, italic, font.isItalic());
+                    assertEquals(path, 0, font.getTtcIndex());
+                    assertNull(path, font.getAxes());
+                }
+            } finally {
+                file.delete();
+            }
+        }
+
+        boolean customItalic = true;
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getFontPathFromStyle(weight, italic);
+
+            File file = getTempFile();
+            try (InputStream is = am.open(path)) {
+                assertTrue(copyToFile(file, is, dummy, dummy));
+
+                try (FileInputStream fis = new FileInputStream(file)) {
+                    Font font = new Font.Builder(
+                            fis.getFD(), dummy.length, file.length() - dummy.length * 2)
+                            .setItalic(customItalic).build();
+                    assertEquals(path, weight, font.getWeight());
+                    assertEquals(path, customItalic, font.isItalic());
+                    assertEquals(path, 0, font.getTtcIndex());
+                    assertNull(path, font.getAxes());
+                }
+            } finally {
+                file.delete();
+            }
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testBuilder_fd_invalid_null() throws IOException {
+        FileDescriptor fd = null;
+        new Font.Builder(fd);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testBuilder_fd_subadata_invalid_null() throws IOException {
+        FileDescriptor fd = null;
+        new Font.Builder(fd, 0, -1);
+    }
+
+    @Test(expected = IOException.class)
+    public void testBuilder_fd_subadata_invalid_invalid_size() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        String path = FontTestUtil.getFontPathFromStyle(400, false);
+
+        File file = getTempFile();
+        try (InputStream is = am.open(path)) {
+            assertTrue(copyToFile(file, is));
+            try (FileInputStream fis = new FileInputStream(file)) {
+                new Font.Builder(fis.getFD(), 0, Integer.MAX_VALUE);
+            }
+        }
+    }
+
+    @Test(expected = IOException.class)
+    public void testBuilder_fd_subadata_invalid_invalid_offset() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        String path = FontTestUtil.getFontPathFromStyle(400, false);
+
+        File file = getTempFile();
+        try (InputStream is = am.open(path)) {
+            assertTrue(copyToFile(file, is));
+            try (FileInputStream fis = new FileInputStream(file)) {
+                new Font.Builder(fis.getFD(), Integer.MAX_VALUE, file.length());
+            }
+        }
+    }
+
+    @Test
+    public void testBuilder_asset() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getFontPathFromStyle(weight, italic);
+
+            Font font = new Font.Builder(am, path).build();
+            assertEquals(path, weight, font.getWeight());
+            assertEquals(path, italic, font.isItalic());
+            assertEquals(path, 0, font.getTtcIndex());
+            assertNull(path, font.getAxes());
+        }
+    }
+
+    @Test
+    public void testBuilder_asset_ttc() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getTtcFontFileInAsset();
+            int ttcIndex = FontTestUtil.getTtcIndexFromStyle(weight, italic);
+
+            Font font = new Font.Builder(am, path).setTtcIndex(ttcIndex).build();
+            assertEquals(path, weight, font.getWeight());
+            assertEquals(path, italic, font.isItalic());
+            assertEquals(path, ttcIndex, font.getTtcIndex());
+            assertNull(path, font.getAxes());
+        }
+    }
+
+    @Test
+    public void testBuilder_asset_vf() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getVFFontInAsset();
+            FontVariationAxis[] axes = FontVariationAxis.fromFontVariationSettings(
+                    FontTestUtil.getVarSettingsFromStyle(weight, italic));
+
+            Font font = new Font.Builder(am, path).setFontVariationSettings(axes).build();
+            assertEquals(path, weight, font.getWeight());
+            assertEquals(path, italic, font.isItalic());
+            assertEquals(path, 0, font.getTtcIndex());
+            assertEquals(path, axes, font.getAxes());
+        }
+    }
+
+    @Test
+    public void testBuilder_asset_override() throws IOException {
+        int customWeight = 350;
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getFontPathFromStyle(weight, italic);
+
+            Font font = new Font.Builder(am, path).setWeight(customWeight).build();
+            assertEquals(path, customWeight, font.getWeight());
+            assertEquals(path, italic, font.isItalic());
+            assertEquals(path, 0, font.getTtcIndex());
+            assertNull(path, font.getAxes());
+        }
+        boolean customItalic = true;
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            String path = FontTestUtil.getFontPathFromStyle(weight, italic);
+
+            Font font = new Font.Builder(am, path).setItalic(customItalic).build();
+            assertEquals(path, weight, font.getWeight());
+            assertEquals(path, customItalic, font.isItalic());
+            assertEquals(path, 0, font.getTtcIndex());
+            assertNull(path, font.getAxes());
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testBuilder_asset_invalid_null_asset() throws IOException {
+        AssetManager am = null;
+        new Font.Builder(am, "/some/path");
+    }
+
+    @Test(expected = IOException.class)
+    public void testBuilder_asset_invalid_not_found() throws IOException {
+        AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+        new Font.Builder(am, "/no/such/file");
+    }
+
+    @Test
+    public void testBuilder_resource() throws IOException {
+        Resources res = InstrumentationRegistry.getTargetContext().getResources();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            int resId = FontTestUtil.getFontResourceIdFromStyle(weight, italic);
+
+            Font font = new Font.Builder(res, resId).build();
+            assertEquals("ResId=#" + resId, weight, font.getWeight());
+            assertEquals("ResId=#" + resId, italic, font.isItalic());
+            assertEquals("ResId=#" + resId, 0, font.getTtcIndex());
+            assertNull("ResId=#" + resId, font.getAxes());
+        }
+    }
+
+    @Test
+    public void testBuilder_resource_ttc() throws IOException {
+        Resources res = InstrumentationRegistry.getTargetContext().getResources();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            int resId = FontTestUtil.getTtcFontFileResourceId();
+            int ttcIndex = FontTestUtil.getTtcIndexFromStyle(weight, italic);
+
+            Font font = new Font.Builder(res, resId).setTtcIndex(ttcIndex).build();
+            assertEquals("ResId=#" + resId, weight, font.getWeight());
+            assertEquals("ResId=#" + resId, italic, font.isItalic());
+            assertEquals("ResId=#" + resId, ttcIndex, font.getTtcIndex());
+            assertNull("ResId=#" + resId, font.getAxes());
+        }
+    }
+
+    @Test
+    public void testBuilder_resource_vf() throws IOException {
+        Resources res = InstrumentationRegistry.getTargetContext().getResources();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            int resId = FontTestUtil.getVFFontResourceId();
+            FontVariationAxis[] axes = FontVariationAxis.fromFontVariationSettings(
+                    FontTestUtil.getVarSettingsFromStyle(weight, italic));
+
+            Font font = new Font.Builder(res, resId).setFontVariationSettings(axes).build();
+            assertEquals("ResId=#" + resId, weight, font.getWeight());
+            assertEquals("ResId=#" + resId, italic, font.isItalic());
+            assertEquals("ResId=#" + resId, 0, font.getTtcIndex());
+            assertEquals("ResId=#" + resId, axes, font.getAxes());
+        }
+    }
+
+    @Test
+    public void testBuilder_resource_override() throws IOException {
+        int customWeight = 350;
+        Resources res = InstrumentationRegistry.getTargetContext().getResources();
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            int resId = FontTestUtil.getFontResourceIdFromStyle(weight, italic);
+
+            Font font = new Font.Builder(res, resId).setWeight(customWeight).build();
+            assertEquals("ResId=#" + resId, customWeight, font.getWeight());
+            assertEquals("ResId=#" + resId, italic, font.isItalic());
+            assertEquals("ResId=#" + resId, 0, font.getTtcIndex());
+            assertNull("ResId=#" + resId, font.getAxes());
+        }
+
+        boolean customItalic = true;
+        for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+            int weight = style.first.intValue();
+            boolean italic = style.second.booleanValue();
+            int resId = FontTestUtil.getFontResourceIdFromStyle(weight, italic);
+
+            Font font = new Font.Builder(res, resId).setItalic(customItalic).build();
+            assertEquals("ResId=#" + resId, weight, font.getWeight());
+            assertEquals("ResId=#" + resId, customItalic, font.isItalic());
+            assertEquals("ResId=#" + resId, 0, font.getTtcIndex());
+            assertNull("ResId=#" + resId, font.getAxes());
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testBuilder_resource_invalid_null_resource() throws IOException {
+        Resources res = null;
+        new Font.Builder(res, R.font.ascii);
+    }
+
+    @Test(expected = NotFoundException.class)
+    public void testBuilder_resource_invalid_res_id() throws IOException {
+        Resources res = InstrumentationRegistry.getTargetContext().getResources();
+        new Font.Builder(res, -1);
+    }
+
+    @Test(expected = IOException.class)
+    public void testBuilder_asset_invalid_xml_font() throws IOException {
+        Resources res = InstrumentationRegistry.getTargetContext().getResources();
+        new Font.Builder(res, R.font.multiweight_family /* XML font */);
+    }
+
+}
diff --git a/tests/tests/graphics/src/android/graphics/fonts/FontTestUtil.java b/tests/tests/graphics/src/android/graphics/fonts/FontTestUtil.java
new file mode 100644
index 0000000..f5b35f0
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/fonts/FontTestUtil.java
@@ -0,0 +1,255 @@
+/*
+ * 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.graphics.fonts;
+
+import android.graphics.cts.R;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Provides a utility for testing fonts
+ *
+ * For the purpose of testing font selection of families or fallbacks, this class provies following
+ * regular font files.
+ *
+ * - ascii_a3em_weight100_upright.ttf
+ *   'a' has 3em width and others have 1em width. The metadata has weight=100, non-italic value.
+ * - ascii_b3em_weight100_italic.ttf
+ *   'b' has 3em width and others have 1em width. The metadata has weight=100, italic value.
+ * - ascii_c3em_weight200_upright.ttf
+ *   'c' has 3em width and others have 1em width. The metadata has weight=200, non-italic value.
+ * - ascii_d3em_weight200_italic.ttf
+ *   'd' has 3em width and others have 1em width. The metadata has weight=200, italic value.
+ * - ascii_e3em_weight300_upright.ttf
+ *   'e' has 3em width and others have 1em width. The metadata has weight=300, non-italic value.
+ * - ascii_f3em_weight300_italic.ttf
+ *   'f' has 3em width and others have 1em width. The metadata has weight=300, italic value.
+ * - ascii_g3em_weight400_upright.ttf
+ *   'g' has 3em width and others have 1em width. The metadata has weight=400, non-italic value.
+ * - ascii_h3em_weight400_italic.ttf
+ *   'h' has 3em width and others have 1em width. The metadata has weight=400, italic value.
+ * - ascii_i3em_weight500_upright.ttf
+ *   'i' has 3em width and others have 1em width. The metadata has weight=500, non-italic value.
+ * - ascii_j3em_weight500_italic.ttf
+ *   'j' has 3em width and others have 1em width. The metadata has weight=500, italic value.
+ * - ascii_k3em_weight600_upright.ttf
+ *   'k' has 3em width and others have 1em width. The metadata has weight=600, non-italic value.
+ * - ascii_l3em_weight600_italic.ttf
+ *   'l' has 3em width and others have 1em width. The metadata has weight=600, italic value.
+ * - ascii_m3em_weight700_upright.ttf
+ *   'm' has 3em width and others have 1em width. The metadata has weight=700, non-italic value.
+ * - ascii_n3em_weight700_italic.ttf
+ *   'n' has 3em width and others have 1em width. The metadata has weight=700, italic value.
+ * - ascii_o3em_weight800_upright.ttf
+ *   'o' has 3em width and others have 1em width. The metadata has weight=800, non-italic value.
+ * - ascii_p3em_weight800_italic.ttf
+ *   'p' has 3em width and others have 1em width. The metadata has weight=800, italic value.
+ * - ascii_q3em_weight900_upright.ttf
+ *   'q' has 3em width and others have 1em width. The metadata has weight=900, non-italic value.
+ * - ascii_r3em_weight900_italic.ttf
+ *   'r' has 3em width and others have 1em width. The metadata has weight=900, italic value.
+ *
+ * In addition to above font files, this class provides a font collection file and a variable font
+ * file.
+ * - ascii.ttc
+ *   The collection of above 18 fonts with above order.
+ * - ascii_vf.ttf
+ *   This font supports a-z characters and all characters has 1em width. This font supports 'wght',
+ *   'ital' axes but no effect for the glyph width. This font also supports 'Asc[a-z]' 26 axes which
+ *   makes glyph width 3em. For example, 'Asca 1.0' makes a glyph width of 'a' 3em, 'Ascb 1.0' makes
+ *   a glyph width of 'b' 3em. With these axes, above font can be replicated like
+ *   - 'Asca' 1.0, 'wght' 100.0' is equivalent with ascii_a3em_width100_upright.ttf
+ *   - 'Ascb' 1.0, 'wght' 100.0, 'ital' 1.0' is equivalent with ascii_b3em_width100_italic.ttf
+ */
+public class FontTestUtil {
+    private static final String FAMILY_SELECTION_FONT_PATH_IN_ASSET = "fonts_for_family_selection";
+    private static final List<Pair<Integer, Boolean>> sStyleList;
+    private static final Map<Pair<Integer, Boolean>, String> sFontMap;
+    private static final Map<Pair<Integer, Boolean>, Integer> sTtcMap;
+    private static final Map<Pair<Integer, Boolean>, String> sVariationSettingsMap;
+    private static final Map<Pair<Integer, Boolean>, Integer> sResourceMap;
+    private static final String[] sFontList = {  // Same order of ascii.ttc
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_a3em_weight100_upright.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_b3em_weight100_italic.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_c3em_weight200_upright.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_d3em_weight200_italic.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_e3em_weight300_upright.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_f3em_weight300_italic.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_g3em_weight400_upright.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_h3em_weight400_italic.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_i3em_weight500_upright.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_j3em_weight500_italic.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_k3em_weight600_upright.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_l3em_weight600_italic.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_m3em_weight700_upright.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_n3em_weight700_italic.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_o3em_weight800_upright.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_p3em_weight800_italic.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_q3em_weight900_upright.ttf",
+            FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_r3em_weight900_italic.ttf",
+    };
+
+    private static final String[] FONT_VARIATION_SETTING_LIST = {
+            "'Asca' 1.0, 'wght' 100.0",
+            "'Ascb' 1.0, 'wght' 100.0, 'ital' 1.0",
+            "'Ascc' 1.0, 'wght' 200.0",
+            "'Ascd' 1.0, 'wght' 200.0, 'ital' 1.0",
+            "'Asce' 1.0, 'wght' 300.0",
+            "'Ascf' 1.0, 'wght' 300.0, 'ital' 1.0",
+            "'Ascg' 1.0, 'wght' 400.0",
+            "'Asch' 1.0, 'wght' 400.0, 'ital' 1.0",
+            "'Asci' 1.0, 'wght' 500.0",
+            "'Ascj' 1.0, 'wght' 500.0, 'ital' 1.0",
+            "'Asck' 1.0, 'wght' 600.0",
+            "'Ascl' 1.0, 'wght' 600.0, 'ital' 1.0",
+            "'Ascm' 1.0, 'wght' 700.0",
+            "'Ascn' 1.0, 'wght' 700.0, 'ital' 1.0",
+            "'Asco' 1.0, 'wght' 800.0",
+            "'Ascp' 1.0, 'wght' 800.0, 'ital' 1.0",
+            "'Ascq' 1.0, 'wght' 900.0",
+            "'Ascr' 1.0, 'wght' 900.0, 'ital' 1.0",
+    };
+
+    private static final int[] FONT_RESOURCE_ID_LIST = {
+            R.font.ascii_a3em_weight100_upright,
+            R.font.ascii_b3em_weight100_italic,
+            R.font.ascii_c3em_weight200_upright,
+            R.font.ascii_d3em_weight200_italic,
+            R.font.ascii_e3em_weight300_upright,
+            R.font.ascii_f3em_weight300_italic,
+            R.font.ascii_g3em_weight400_upright,
+            R.font.ascii_h3em_weight400_italic,
+            R.font.ascii_i3em_weight500_upright,
+            R.font.ascii_j3em_weight500_italic,
+            R.font.ascii_k3em_weight600_upright,
+            R.font.ascii_l3em_weight600_italic,
+            R.font.ascii_m3em_weight700_upright,
+            R.font.ascii_n3em_weight700_italic,
+            R.font.ascii_o3em_weight800_upright,
+            R.font.ascii_p3em_weight800_italic,
+            R.font.ascii_q3em_weight900_upright,
+            R.font.ascii_r3em_weight900_italic,
+    };
+
+    static {
+        // Style list with the same order of sFontList.
+        ArrayList<Pair<Integer, Boolean>> styles = new ArrayList<>();
+        styles.add(new Pair<>(100, false));
+        styles.add(new Pair<>(100, true));
+        styles.add(new Pair<>(200, false));
+        styles.add(new Pair<>(200, true));
+        styles.add(new Pair<>(300, false));
+        styles.add(new Pair<>(300, true));
+        styles.add(new Pair<>(400, false));
+        styles.add(new Pair<>(400, true));
+        styles.add(new Pair<>(500, false));
+        styles.add(new Pair<>(500, true));
+        styles.add(new Pair<>(600, false));
+        styles.add(new Pair<>(600, true));
+        styles.add(new Pair<>(700, false));
+        styles.add(new Pair<>(700, true));
+        styles.add(new Pair<>(800, false));
+        styles.add(new Pair<>(800, true));
+        styles.add(new Pair<>(900, false));
+        styles.add(new Pair<>(900, true));
+        sStyleList = Collections.unmodifiableList(styles);
+
+        HashMap<Pair<Integer, Boolean>, String> map = new HashMap<>();
+        HashMap<Pair<Integer, Boolean>, Integer> ttcMap = new HashMap<>();
+        HashMap<Pair<Integer, Boolean>, String> variationMap = new HashMap<>();
+        HashMap<Pair<Integer, Boolean>, Integer> resourceMap = new HashMap<>();
+        HashMap<Character, Pair<Integer, Boolean>> reverseMap = new HashMap<>();
+        for (int i = 0; i < sFontList.length; ++i) {
+            map.put(sStyleList.get(i), sFontList[i]);
+            ttcMap.put(sStyleList.get(i), i);
+            variationMap.put(sStyleList.get(i), FONT_VARIATION_SETTING_LIST[i]);
+            resourceMap.put(sStyleList.get(i), FONT_RESOURCE_ID_LIST[i]);
+        }
+        sFontMap = Collections.unmodifiableMap(map);
+        sTtcMap = Collections.unmodifiableMap(ttcMap);
+        sVariationSettingsMap = Collections.unmodifiableMap(variationMap);
+        sResourceMap = Collections.unmodifiableMap(resourceMap);
+    }
+
+    /**
+     * Returns a path to the font collection file in asset directory.
+     */
+    public static String getTtcFontFileInAsset() {
+        return FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ascii.ttc";
+    }
+
+    /**
+     * Returns a resource id for the font collection file.
+     */
+    public static int getTtcFontFileResourceId() {
+        return R.font.ascii;
+    }
+
+    /**
+     * Returns a path to the variable font file in asset directory.
+     */
+    public static String getVFFontInAsset() {
+        return FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ascii_vf.ttf";
+    }
+
+    /**
+     * Returns a resource id for the variable font.
+     */
+    public static int getVFFontResourceId() {
+        return R.font.ascii_vf;
+    }
+
+    /**
+     * Returns a ttc index of the specified style.
+     */
+    public static int getTtcIndexFromStyle(int weight, boolean italic) {
+        return sTtcMap.get(new Pair<>(weight, italic)).intValue();
+    }
+
+    /**
+     * Returns a variation settings string of the specified style.
+     */
+    public static String getVarSettingsFromStyle(int weight, boolean italic) {
+        return sVariationSettingsMap.get(new Pair<>(weight, italic));
+    }
+
+    /**
+     * Returns a font resource ID of the specific style.
+     */
+    public static int getFontResourceIdFromStyle(int weight, boolean italic) {
+        return sResourceMap.get(new Pair<>(weight, italic));
+    }
+
+    /**
+     * Returns a font path from the specified style.
+     */
+    public static String getFontPathFromStyle(int weight, boolean italic) {
+        return sFontMap.get(new Pair<>(weight, italic));
+    }
+
+    /**
+     * Returns all supported styles.
+     */
+    public static List<Pair<Integer, Boolean>> getAllStyles() {
+        return sStyleList;
+    }
+}
diff --git a/tests/tests/hardware/Android.mk b/tests/tests/hardware/Android.mk
index 4e8aaa7..f247e177 100644
--- a/tests/tests/hardware/Android.mk
+++ b/tests/tests/hardware/Android.mk
@@ -41,7 +41,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsHardwareTestCases
-LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_SDK_VERSION := test_current
 
 include $(BUILD_CTS_PACKAGE)
 
diff --git a/tests/tests/hardware/jni/Android.mk b/tests/tests/hardware/jni/Android.mk
index 0cd95e7..6df6667 100644
--- a/tests/tests/hardware/jni/Android.mk
+++ b/tests/tests/hardware/jni/Android.mk
@@ -30,7 +30,9 @@
 
 LOCAL_SHARED_LIBRARIES := libandroid libnativehelper_compat_libc++ liblog
 
-LOCAL_CXX_STL := libc++_static
+LOCAL_NDK_STL_VARIANT := none
+
+LOCAL_SDK_VERSION := current
 
 LOCAL_CLANG := true
 
diff --git a/tests/tests/hardware/jni/android_hardware_cts_HardwareBufferTest.cpp b/tests/tests/hardware/jni/android_hardware_cts_HardwareBufferTest.cpp
index c5b5c35..8df230f 100644
--- a/tests/tests/hardware/jni/android_hardware_cts_HardwareBufferTest.cpp
+++ b/tests/tests/hardware/jni/android_hardware_cts_HardwareBufferTest.cpp
@@ -20,7 +20,6 @@
 #include <jni.h>
 
 #include <android/hardware_buffer_jni.h>
-#include <utils/Errors.h>
 
 #define LOG_TAG "HardwareBufferTest"
 
@@ -35,7 +34,9 @@
     desc.usage = usage;
     desc.format = format;
     int res = AHardwareBuffer_allocate(&desc, &buffer);
-    if (res == android::NO_ERROR) {
+
+    // TODO: Change this to res == NO_ERROR after b/77153085 is fixed
+    if (res == 0) {
         return AHardwareBuffer_toHardwareBuffer(env, buffer);
     } else {
         return 0;
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java
index 9089529..0b7f7a0 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java
@@ -24,7 +24,6 @@
 import android.hardware.input.cts.InputCallback;
 import android.hardware.input.cts.InputCtsActivity;
 import android.os.ParcelFileDescriptor;
-import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.rule.ActivityTestRule;
 import android.view.KeyEvent;
@@ -40,8 +39,6 @@
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
-import libcore.io.IoUtils;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -72,7 +69,7 @@
         new ActivityTestRule<>(InputCtsActivity.class);
 
     @Before
-    public void setUp() throws Exception {
+    public void setUp() {
         clearKeys();
         clearMotions();
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -81,8 +78,10 @@
     }
 
     @After
-    public void tearDown() throws Exception {
-        IoUtils.closeQuietly(mOutputStream);
+    public void tearDown() {
+        try {
+            mOutputStream.close();
+        } catch (IOException ignored) {}
     }
 
     /**
@@ -104,7 +103,6 @@
         } catch (InterruptedException ex) {
             fail("Unexpectedly interrupted while waiting for device added notification.");
         }
-        SystemClock.sleep(100);
     }
 
     /**
@@ -179,12 +177,14 @@
         mMotions.clear();
     }
 
-    private void setupPipes() throws IOException {
+    private void setupPipes() {
         UiAutomation ui = mInstrumentation.getUiAutomation();
         ParcelFileDescriptor[] pipes = ui.executeShellCommandRw(HID_COMMAND);
 
         mOutputStream = new ParcelFileDescriptor.AutoCloseOutputStream(pipes[1]);
-        IoUtils.closeQuietly(pipes[0]); // hid command is write-only
+        try {
+          pipes[0].close(); // hid command is write-only
+        } catch (IOException ignored) {}
     }
 
     private String getEvents(int id) throws IOException {
@@ -203,6 +203,7 @@
         return baos.toString();
     }
 
+
     private class InputListener implements InputCallback {
         @Override
         public void onKeyEvent(KeyEvent ev) {
diff --git a/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java b/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
index c0fbdc3..3b3c972 100644
--- a/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
+++ b/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
@@ -47,6 +47,7 @@
     private final static String VENDOR_CONFIG_FILE = "/vendor/etc/public.libraries.txt";
     private final static String[] PUBLIC_SYSTEM_LIBRARIES = {
         "libaaudio.so",
+        "libamidi.so",
         "libandroid.so",
         "libc.so",
         "libcamera2ndk.so",
diff --git a/tests/tests/keystore/AndroidTest.xml b/tests/tests/keystore/AndroidTest.xml
index e2b4986..4a2dd9e 100644
--- a/tests/tests/keystore/AndroidTest.xml
+++ b/tests/tests/keystore/AndroidTest.xml
@@ -25,5 +25,6 @@
         <option name="runtime-hint" value="16m39s" />
         <!-- test-timeout unit is ms, value = 10 min -->
         <option name="test-timeout" value="600000" />
+        <option name="hidden-api-checks" value="false" />
     </test>
 </configuration>
diff --git a/tests/tests/keystore/src/android/server/am/ActivityAndWindowManagersState.java b/tests/tests/keystore/src/android/server/am/ActivityAndWindowManagersState.java
index be769cf..c52b59d 100644
--- a/tests/tests/keystore/src/android/server/am/ActivityAndWindowManagersState.java
+++ b/tests/tests/keystore/src/android/server/am/ActivityAndWindowManagersState.java
@@ -16,7 +16,7 @@
 
 package android.server.am;
 
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
diff --git a/tests/tests/keystore/src/android/server/am/ActivityManagerTestBase.java b/tests/tests/keystore/src/android/server/am/ActivityManagerTestBase.java
index b960d2f..6db2f09 100644
--- a/tests/tests/keystore/src/android/server/am/ActivityManagerTestBase.java
+++ b/tests/tests/keystore/src/android/server/am/ActivityManagerTestBase.java
@@ -35,6 +35,7 @@
 
 import android.accessibilityservice.AccessibilityService;
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.SystemClock;
@@ -77,6 +78,7 @@
 
     protected Context mContext;
     protected ActivityManager mAm;
+    protected ActivityTaskManager mAtm;
 
     /**
      * @return the am command to start the given activity with the following extra key/value pairs.
@@ -117,6 +119,7 @@
     public void setUp() throws Exception {
         mContext = InstrumentationRegistry.getContext();
         mAm = mContext.getSystemService(ActivityManager.class);
+        mAtm = mContext.getSystemService(ActivityTaskManager.class);
 
         pressWakeupButton();
         pressUnlockButton();
@@ -137,7 +140,7 @@
     }
 
     protected void removeStacksWithActivityTypes(int... activityTypes) {
-        mAm.removeStacksWithActivityTypes(activityTypes);
+        mAtm.removeStacksWithActivityTypes(activityTypes);
         waitForIdle();
     }
 
@@ -178,7 +181,7 @@
 
     protected void setActivityTaskWindowingMode(ComponentName activityName, int windowingMode) {
         final int taskId = getActivityTaskId(activityName);
-        mAm.setTaskWindowingMode(taskId, windowingMode, true /* toTop */);
+        mAtm.setTaskWindowingMode(taskId, windowingMode, true /* toTop */);
         mAmWmState.waitForValidState(new WaitForValidActivityState.Builder(activityName)
                 .setActivityType(ACTIVITY_TYPE_STANDARD)
                 .setWindowingMode(windowingMode)
@@ -238,7 +241,7 @@
     }
 
     protected boolean supportsSplitScreenMultiWindow() {
-        return ActivityManager.supportsSplitScreenMultiWindow(mContext);
+        return ActivityTaskManager.supportsSplitScreenMultiWindow(mContext);
     }
 
     protected boolean hasDeviceFeature(final String requiredFeature) {
diff --git a/tests/tests/keystore/src/android/server/am/WaitForValidActivityState.java b/tests/tests/keystore/src/android/server/am/WaitForValidActivityState.java
index 05806a7..5fac1c0 100644
--- a/tests/tests/keystore/src/android/server/am/WaitForValidActivityState.java
+++ b/tests/tests/keystore/src/android/server/am/WaitForValidActivityState.java
@@ -16,7 +16,7 @@
 
 package android.server.am;
 
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
diff --git a/tests/tests/keystore/src/android/server/am/WindowManagerState.java b/tests/tests/keystore/src/android/server/am/WindowManagerState.java
index ae6e547..420ce56 100644
--- a/tests/tests/keystore/src/android/server/am/WindowManagerState.java
+++ b/tests/tests/keystore/src/android/server/am/WindowManagerState.java
@@ -43,6 +43,7 @@
 import com.android.server.wm.nano.StackProto;
 import com.android.server.wm.nano.TaskProto;
 import com.android.server.wm.nano.WindowContainerProto;
+import com.android.server.wm.nano.WindowFramesProto;
 import com.android.server.wm.nano.WindowManagerServiceDumpProto;
 import com.android.server.wm.nano.WindowStateAnimatorProto;
 import com.android.server.wm.nano.WindowStateProto;
@@ -491,10 +492,10 @@
         private int mDisplayId;
         private int mStackId;
         private boolean mShown;
-        private Rect mContainingFrame = new Rect();
-        private Rect mParentFrame = new Rect();
-        private Rect mContentFrame = new Rect();
-        private Rect mFrame = new Rect();
+        private Rect mContainingFrame;
+        private Rect mParentFrame;
+        private Rect mContentFrame;
+        private Rect mFrame;
         private Rect mCrop = new Rect();
 
         WindowState(WindowStateProto proto) {
@@ -515,10 +516,13 @@
                 }
                 mCrop = extract(animatorProto.lastClipRect);
             }
-            mFrame = extract(proto.frame);
-            mContainingFrame = extract(proto.containingFrame);
-            mParentFrame = extract(proto.parentFrame);
-            mContentFrame = extract(proto.contentFrame);
+            WindowFramesProto windowFramesProto = proto.windowFrames;
+            if (windowFramesProto != null) {
+                mFrame = extract(windowFramesProto.frame);
+                mContainingFrame = extract(windowFramesProto.containingFrame);
+                mParentFrame = extract(windowFramesProto.parentFrame);
+                mContentFrame = extract(windowFramesProto.contentFrame);
+            }
             if (mName.startsWith(STARTING_WINDOW_PREFIX)) {
                 mWindowType = WINDOW_TYPE_STARTING;
                 // Existing code depends on the prefix being removed
diff --git a/tests/tests/location/AndroidTest.xml b/tests/tests/location/AndroidTest.xml
index c69e224..47c105c 100644
--- a/tests/tests/location/AndroidTest.xml
+++ b/tests/tests/location/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS Location test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="location" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
@@ -24,6 +25,7 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.location.cts" />
         <option name="runtime-hint" value="18m8s" />
+        <option name="hidden-api-checks" value="false" />
     </test>
 
 </configuration>
diff --git a/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java b/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java
index 7852361..940146e 100644
--- a/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java
+++ b/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java
@@ -593,6 +593,15 @@
                             "0 >= X <= 7 days",
                             String.valueOf(sv_time_days),
                             sv_time_days >= 0 && sv_time_days <= 7);
+                } else if ((state & GnssMeasurement.STATE_TOW_KNOWN)
+                        == GnssMeasurement.STATE_TOW_KNOWN) {
+                    softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
+                                    "GNSS_MEASUREMENT_STATE_TOW_KNOWN",
+                                    "GnssStatus.CONSTELLATION_BEIDOU"),
+                            timeInNs,
+                            "0 >= X <= 7 days",
+                            String.valueOf(sv_time_days),
+                            sv_time_days >= 0 && sv_time_days <= 7);
                 } else if ((state & GnssMeasurement.STATE_SUBFRAME_SYNC)
                         == GnssMeasurement.STATE_SUBFRAME_SYNC) {
                     softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
@@ -675,6 +684,15 @@
                     "0 >= X <= 7 days",
                     String.valueOf(sv_time_days),
                     sv_time_days >= 0 && sv_time_days <= 7);
+        } else if ((state & GnssMeasurement.STATE_TOW_KNOWN)
+                == GnssMeasurement.STATE_TOW_KNOWN) {
+            softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
+                            "GNSS_MEASUREMENT_STATE_TOW_KNOWN",
+                            constellationType),
+                    timeInNs,
+                    "0 >= X <= 7 days",
+                    String.valueOf(sv_time_days),
+                    sv_time_days >= 0 && sv_time_days <= 7);
         } else if ((state & GnssMeasurement.STATE_SUBFRAME_SYNC)
                 == GnssMeasurement.STATE_SUBFRAME_SYNC) {
             softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
diff --git a/tests/tests/location2/AndroidTest.xml b/tests/tests/location2/AndroidTest.xml
index 412fd1d..e193a7e 100644
--- a/tests/tests/location2/AndroidTest.xml
+++ b/tests/tests/location2/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS Location test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="location" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/media/Android.mk b/tests/tests/media/Android.mk
index d2348db..c2e93b8 100644
--- a/tests/tests/media/Android.mk
+++ b/tests/tests/media/Android.mk
@@ -56,7 +56,7 @@
     truth-prebuilt \
     mockito-target-minus-junit4 \
     androidx.heifwriter_heifwriter \
-    androidx.media_media \
+    androidx.media2_media2 \
 
 LOCAL_JNI_SHARED_LIBRARIES := \
     libaudio_jni \
diff --git a/tests/tests/media/AndroidTest.xml b/tests/tests/media/AndroidTest.xml
index 1a5377a..7dfe63f 100644
--- a/tests/tests/media/AndroidTest.xml
+++ b/tests/tests/media/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS Media 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" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
         <option name="images-only" value="true" />
     </target_preparer>
diff --git a/tests/tests/media/libndkaudio/Android.mk b/tests/tests/media/libndkaudio/Android.mk
index 982c630..9480dcd 100644
--- a/tests/tests/media/libndkaudio/Android.mk
+++ b/tests/tests/media/libndkaudio/Android.mk
@@ -57,5 +57,6 @@
 LOCAL_CERTIFICATE := platform
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 23
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/tests/media/src/android/media/cts/CodecState.java b/tests/tests/media/src/android/media/cts/CodecState.java
index 12ebeae..d55dc92 100644
--- a/tests/tests/media/src/android/media/cts/CodecState.java
+++ b/tests/tests/media/src/android/media/cts/CodecState.java
@@ -82,7 +82,7 @@
         mPresentationTimeUs = 0;
 
         String mime = mFormat.getString(MediaFormat.KEY_MIME);
-        Log.d(TAG, "CodecState::onOutputFormatChanged " + mime);
+        Log.d(TAG, "CodecState::CodecState " + mime);
         mIsAudio = mime.startsWith("audio/");
     }
 
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java b/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java
index bd62d87..5b2ad2f 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java
@@ -78,7 +78,8 @@
     private Map<UUID, byte[]> mPsshInitData;
     private MediaCrypto mCrypto;
     private MediaCas mMediaCas;
-    private MediaDescrambler mDescrambler;
+    private MediaDescrambler mAudioDescrambler;
+    private MediaDescrambler mVideoDescrambler;
     private MediaExtractor mAudioExtractor;
     private MediaExtractor mVideoExtractor;
     private SurfaceHolder mSurfaceHolder;
@@ -193,7 +194,7 @@
         return PSSH;
     }
 
-    private void prepareAudio() throws IOException {
+    private void prepareAudio() throws IOException, MediaCasException {
         boolean hasAudio = false;
         for (int i = mAudioExtractor.getTrackCount(); i-- > 0;) {
             MediaFormat format = mAudioExtractor.getTrackFormat(i);
@@ -208,6 +209,14 @@
                   " Channel count:" +
                   getMediaFormatInteger(format, MediaFormat.KEY_CHANNEL_COUNT));
 
+            if (mScrambled) {
+                MediaExtractor.CasInfo casInfo = mAudioExtractor.getCasInfo(i);
+                if (casInfo != null && casInfo.getSession() != null) {
+                    mAudioDescrambler = new MediaDescrambler(casInfo.getSystemId());
+                    mAudioDescrambler.setMediaCasSession(casInfo.getSession());
+                }
+            }
+
             if (!hasAudio) {
                 mAudioExtractor.selectTrack(i);
                 addTrack(i, format, mEncryptedAudio);
@@ -230,7 +239,7 @@
         }
     }
 
-    private void prepareVideo() throws IOException {
+    private void prepareVideo() throws IOException, MediaCasException {
         boolean hasVideo = false;
 
         for (int i = mVideoExtractor.getTrackCount(); i-- > 0;) {
@@ -245,10 +254,11 @@
             Log.d(TAG, "video track #" + i + " " + format + " " + mime +
                   " Width:" + mMediaFormatWidth + ", Height:" + mMediaFormatHeight);
 
-            if (mScrambled && mime.startsWith("video/")) {
+            if (mScrambled) {
                 MediaExtractor.CasInfo casInfo = mVideoExtractor.getCasInfo(i);
                 if (casInfo != null && casInfo.getSession() != null) {
-                    mDescrambler.setMediaCasSession(casInfo.getSession());
+                    mVideoDescrambler = new MediaDescrambler(casInfo.getSystemId());
+                    mVideoDescrambler.setMediaCasSession(casInfo.getSession());
                 }
             }
 
@@ -295,13 +305,14 @@
             android.media.MediaFormat format = extractor.getTrackFormat(trackId);
             String mime = format.getString(android.media.MediaFormat.KEY_MIME);
             Log.d(TAG, "track "+ trackId + ": " + mime);
-            if ("video/scrambled".equals(mime) || "audio/scrambled".equals(mime)) {
+            if (MediaFormat.MIMETYPE_VIDEO_SCRAMBLED.equals(mime) ||
+                    MediaFormat.MIMETYPE_AUDIO_SCRAMBLED.equals(mime)) {
                 MediaExtractor.CasInfo casInfo = extractor.getCasInfo(trackId);
                 if (casInfo != null) {
                     mMediaCas = new MediaCas(casInfo.getSystemId());
-                    mDescrambler = new MediaDescrambler(casInfo.getSystemId());
                     mMediaCas.provision(sProvisionStr);
                     extractor.setMediaCas(mMediaCas);
+                    break;
                 }
             }
         }
@@ -389,7 +400,7 @@
                     format,
                     isVideo ? mSurfaceHolder.getSurface() : null,
                     0,
-                    isVideo ? mDescrambler : null);
+                    isVideo ? mVideoDescrambler : mAudioDescrambler);
         }
 
         CodecState state;
@@ -549,9 +560,14 @@
             mMediaCas = null;
         }
 
-        if (mDescrambler != null) {
-            mDescrambler.close();
-            mDescrambler = null;
+        if (mAudioDescrambler != null) {
+            mAudioDescrambler.close();
+            mAudioDescrambler = null;
+        }
+
+        if (mVideoDescrambler != null) {
+            mVideoDescrambler.close();
+            mVideoDescrambler = null;
         }
 
         mDurationUs = -1;
@@ -596,7 +612,6 @@
         } catch (IllegalStateException e) {
             throw new Error("Aduio CodecState.feedInputBuffer IllegalStateException " + e);
         }
-
     }
 
     public long getNowUs() {
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayer2DrmTestBase.java b/tests/tests/media/src/android/media/cts/MediaPlayer2DrmTestBase.java
index 314c051..967aa24 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayer2DrmTestBase.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayer2DrmTestBase.java
@@ -31,8 +31,6 @@
 import android.content.IntentFilter;
 import android.content.res.Resources;
 import android.media.MediaDrm;
-import android.media.ResourceBusyException;
-import android.media.UnsupportedSchemeException;
 import android.media.cts.TestUtils.Monitor;
 import android.net.Uri;
 import android.os.PowerManager;
@@ -45,9 +43,10 @@
 import android.view.WindowManager;
 
 import androidx.annotation.CallSuper;
-import androidx.media.DataSourceDesc;
-import androidx.media.MediaPlayer2;
-import androidx.media.MediaPlayer2.DrmInfo;
+import androidx.media2.DataSourceDesc2;
+import androidx.media2.MediaPlayer2;
+import androidx.media2.MediaPlayer2.DrmInfo;
+import androidx.media2.UriDataSourceDesc2;
 
 import org.json.JSONArray;
 import org.json.JSONException;
@@ -120,7 +119,7 @@
         try {
             mActivityRule.runOnUiThread(new Runnable() {
                 public void run() {
-                    mPlayer = MediaPlayer2.create();
+                    mPlayer = MediaPlayer2.create(mInstrumentation.getTargetContext());
                 }
             });
         } catch (Throwable e) {
@@ -274,28 +273,28 @@
         mCallStatus = MediaPlayer2.CALL_STATUS_NO_ERROR;
         mECb = new MediaPlayer2.EventCallback() {
                 @Override
-                public void onVideoSizeChanged(MediaPlayer2 mp, DataSourceDesc dsd, int w, int h) {
+                public void onVideoSizeChanged(MediaPlayer2 mp, DataSourceDesc2 dsd, int w, int h) {
                     Log.v(TAG, "VideoSizeChanged" + " w:" + w + " h:" + h);
                     mOnVideoSizeChangedCalled.signal();
                 }
 
                 @Override
-                public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+                public void onError(MediaPlayer2 mp, DataSourceDesc2 dsd, int what, int extra) {
                     fail("Media player had error " + what + " playing video");
                 }
 
                 @Override
-                public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+                public void onInfo(MediaPlayer2 mp, DataSourceDesc2 dsd, int what, int extra) {
                     if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
                         mOnPreparedCalled.signal();
-                    } else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
+                    } else if (what == MediaPlayer2.MEDIA_INFO_DATA_SOURCE_END) {
                         Log.v(TAG, "playLoadedVideo: onInfo_PlaybackComplete");
                         mOnPlaybackCompleted.signal();
                     }
                 }
 
                 @Override
-                public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd,
+                public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc2 dsd,
                         int what, int status) {
                     if (what == MediaPlayer2.CALL_COMPLETED_SET_DATA_SOURCE) {
                         mCallStatus = status;
@@ -306,7 +305,7 @@
 
         mPlayer.setEventCallback(mExecutor, mECb);
         Log.v(TAG, "playLoadedVideo: setDataSource()");
-        mPlayer.setDataSource(new DataSourceDesc.Builder().setDataSource(mContext, file).build());
+        mPlayer.setDataSource(new UriDataSourceDesc2.Builder(mContext, file).build());
         mSetDataSourceCallCompleted.waitForSignal();
         if (mCallStatus != MediaPlayer2.CALL_STATUS_NO_ERROR) {
             throw new PrepareFailedException();
@@ -386,7 +385,7 @@
 
         mPlayer.setDrmEventCallback(mExecutor, new MediaPlayer2.DrmEventCallback() {
             @Override
-            public void onDrmInfo(MediaPlayer2 mp, DataSourceDesc dsd, DrmInfo drmInfo) {
+            public void onDrmInfo(MediaPlayer2 mp, DataSourceDesc2 dsd, DrmInfo drmInfo) {
                 Log.v(TAG, "preparePlayerAndDrm_V1: onDrmInfo" + drmInfo);
 
                 // in the callback (async mode) so handling exceptions here
@@ -420,7 +419,7 @@
     private void preparePlayerAndDrm_V2_syncDrmSetupPlusConfig() throws Exception {
         mPlayer.setOnDrmConfigHelper(new MediaPlayer2.OnDrmConfigHelper() {
             @Override
-            public void onDrmConfig(MediaPlayer2 mp, DataSourceDesc dsd) {
+            public void onDrmConfig(MediaPlayer2 mp, DataSourceDesc2 dsd) {
                 String widevineSecurityLevel3 = "L3";
                 String securityLevelProperty = "securityLevel";
 
@@ -462,7 +461,7 @@
 
         mPlayer.setDrmEventCallback(mExecutor, new MediaPlayer2.DrmEventCallback() {
             @Override
-            public void onDrmInfo(MediaPlayer2 mp, DataSourceDesc dsd, DrmInfo drmInfo) {
+            public void onDrmInfo(MediaPlayer2 mp, DataSourceDesc2 dsd, DrmInfo drmInfo) {
                 Log.v(TAG, "preparePlayerAndDrm_V3: onDrmInfo" + drmInfo);
 
                 // DRM preperation
@@ -500,7 +499,7 @@
             }
 
             @Override
-            public void onDrmPrepared(MediaPlayer2 mp, DataSourceDesc dsd, int status) {
+            public void onDrmPrepared(MediaPlayer2 mp, DataSourceDesc2 dsd, int status) {
                 Log.v(TAG, "preparePlayerAndDrm_V3: onDrmPrepared status: " + status);
 
                 assertTrue("preparePlayerAndDrm_V3: onDrmPrepared did not succeed",
@@ -560,8 +559,7 @@
                 mPlayer.setEventCallback(mExecutor, mECb);
 
                 Log.v(TAG, "playLoadedVideo: setDataSource()");
-                mPlayer.setDataSource(
-                        new DataSourceDesc.Builder().setDataSource(mContext, file).build());
+                mPlayer.setDataSource(new UriDataSourceDesc2.Builder(mContext, file).build());
 
                 Log.v(TAG, "playLoadedVideo: prepare()");
                 mPlayer.prepare();
@@ -751,22 +749,6 @@
             Log.d(TAG, "setupDrm: NoDrmSchemeException");
             e.printStackTrace();
             throw e;
-        } catch (MediaPlayer2.ProvisioningNetworkErrorException e) {
-            Log.d(TAG, "setupDrm: ProvisioningNetworkErrorException");
-            e.printStackTrace();
-            throw e;
-        } catch (MediaPlayer2.ProvisioningServerErrorException e) {
-            Log.d(TAG, "setupDrm: ProvisioningServerErrorException");
-            e.printStackTrace();
-            throw e;
-        } catch (UnsupportedSchemeException e) {
-            Log.d(TAG, "setupDrm: UnsupportedSchemeException");
-            e.printStackTrace();
-            throw e;
-        } catch (ResourceBusyException e) {
-            Log.d(TAG, "setupDrm: ResourceBusyException");
-            e.printStackTrace();
-            throw e;
         } catch (Exception e) {
             Log.d(TAG, "setupDrm: Exception " + e);
             e.printStackTrace();
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayer2Test.java b/tests/tests/media/src/android/media/cts/MediaPlayer2Test.java
index 2538cea..025cc6b 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayer2Test.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayer2Test.java
@@ -136,7 +136,7 @@
                 .build());
         Monitor onPrepareCalled = new Monitor();
         Monitor onErrorCalled = new Monitor();
-        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+        MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
             @Override
             public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
                 if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
@@ -149,11 +149,11 @@
                 onErrorCalled.signal();
             }
         };
-        mp2.setMediaPlayer2EventCallback(mExecutor, ecb);
+        mp2.registerEventCallback(mExecutor, ecb);
         mp2.prepare();
         onPrepareCalled.waitForSignal();
         afd2.close();
-        mp2.clearMediaPlayer2EventCallback();
+        mp2.unregisterEventCallback(ecb);
 
         mp2.loopCurrent(true);
         mp2.play();
@@ -165,7 +165,7 @@
                         .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
                             afd.getLength())
                         .build());
-                mp.setMediaPlayer2EventCallback(mExecutor, ecb);
+                mp.registerEventCallback(mExecutor, ecb);
                 onPrepareCalled.reset();
                 mp.prepare();
                 onErrorCalled.waitForSignal();
@@ -185,7 +185,7 @@
             return;
         }
         Monitor onSetDataSourceCalled = new Monitor();
-        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+        MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
             @Override
             public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
                 if (what == MediaPlayer2.CALL_COMPLETED_SET_DATA_SOURCE) {
@@ -228,7 +228,7 @@
         Monitor onPlayCalled = new Monitor();
         Monitor onSeekToCalled = new Monitor();
         Monitor onLoopCurrentCalled = new Monitor();
-        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+        MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
             @Override
             public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
                 if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
@@ -247,7 +247,7 @@
                 }
             }
         };
-        mp.setMediaPlayer2EventCallback(mExecutor, ecb);
+        mp.registerEventCallback(mExecutor, ecb);
 
         try {
             AudioAttributes attributes = new AudioAttributes.Builder()
@@ -289,7 +289,7 @@
 
             // test stop and restart
             mp.reset();
-            mp.setMediaPlayer2EventCallback(mExecutor, ecb);
+            mp.registerEventCallback(mExecutor, ecb);
             mp.setDataSource(new DataSourceDesc.Builder()
                     .setDataSource(mContext, uri)
                     .build());
@@ -327,7 +327,7 @@
         Monitor onPlayCalled = new Monitor();
         Monitor onSeekToCalled = new Monitor();
         Monitor onLoopCurrentCalled = new Monitor();
-        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+        MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
             @Override
             public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
                 if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
@@ -346,7 +346,7 @@
                 }
             }
         };
-        mp.setMediaPlayer2EventCallback(mExecutor, ecb);
+        mp.registerEventCallback(mExecutor, ecb);
 
         try {
             AudioAttributes attributes = new AudioAttributes.Builder()
@@ -393,7 +393,7 @@
                     .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
                     .build());
 
-            mp.setMediaPlayer2EventCallback(mExecutor, ecb);
+            mp.registerEventCallback(mExecutor, ecb);
             onPrepareCalled.reset();
             mp.prepare();
             onPrepareCalled.waitForSignal();
@@ -428,8 +428,8 @@
             for (MediaPlayer2 mp : mps) {
                 Monitor onPlayCalled = new Monitor();
                 Monitor onLoopCurrentCalled = new Monitor();
-                MediaPlayer2.MediaPlayer2EventCallback ecb =
-                    new MediaPlayer2.MediaPlayer2EventCallback() {
+                MediaPlayer2.EventCallback ecb =
+                    new MediaPlayer2.EventCallback() {
                         @Override
                         public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd,
                                 int what, int status) {
@@ -440,7 +440,7 @@
                             }
                         }
                     };
-                mp.setMediaPlayer2EventCallback(mExecutor, ecb);
+                mp.registerEventCallback(mExecutor, ecb);
 
                 AudioAttributes attributes = new AudioAttributes.Builder()
                         .setInternalLegacyStreamType(AudioManager.STREAM_MUSIC)
@@ -492,8 +492,8 @@
             mp.loopCurrent(true);
             Monitor onCompletionCalled = new Monitor();
             Monitor onPlayCalled = new Monitor();
-            MediaPlayer2.MediaPlayer2EventCallback ecb =
-                new MediaPlayer2.MediaPlayer2EventCallback() {
+            MediaPlayer2.EventCallback ecb =
+                new MediaPlayer2.EventCallback() {
                     @Override
                     public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd,
                             int what, int extra) {
@@ -509,7 +509,7 @@
                         }
                     }
                 };
-            mp.setMediaPlayer2EventCallback(mExecutor, ecb);
+            mp.registerEventCallback(mExecutor, ecb);
 
             assertFalse(mp.isPlaying());
             onPlayCalled.reset();
@@ -549,8 +549,8 @@
         Monitor onPrepareCalled = new Monitor();
         Monitor onSeekToCalled = new Monitor();
         Monitor onLoopCurrentCalled = new Monitor();
-        MediaPlayer2.MediaPlayer2EventCallback ecb =
-            new MediaPlayer2.MediaPlayer2EventCallback() {
+        MediaPlayer2.EventCallback ecb =
+            new MediaPlayer2.EventCallback() {
                 @Override
                 public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
                     if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
@@ -568,7 +568,7 @@
                     }
                 }
             };
-        mp.setMediaPlayer2EventCallback(mExecutor, ecb);
+        mp.registerEventCallback(mExecutor, ecb);
 
         try {
             AudioAttributes attributes = new AudioAttributes.Builder()
@@ -602,7 +602,7 @@
                     .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
                     .build());
 
-            mp.setMediaPlayer2EventCallback(mExecutor, ecb);
+            mp.registerEventCallback(mExecutor, ecb);
             onPrepareCalled.reset();
             mp.prepare();
             onPrepareCalled.waitForSignal();
@@ -736,7 +736,7 @@
 
         final CountDownLatch seekDone = new CountDownLatch(1);
 
-        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+        MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
             @Override
             public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
                 if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
@@ -924,32 +924,66 @@
                 R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz)) {
             return; // skip
         }
-        DataSourceDesc dsd1 = createDataSourceDesc(
-                R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz);
-        DataSourceDesc dsd2 = createDataSourceDesc(
-                R.raw.testvideo);
+
+        int resid = R.raw.testvideo;
+        if (!MediaUtils.hasCodecsForResource(mContext, resid)) {
+            return;  // skip
+        }
+
+        AssetFileDescriptor afd2 = mResources.openRawResourceFd(resid);
+        DataSourceDesc dsd2 = new DataSourceDesc.Builder()
+                .setDataSource(afd2.getFileDescriptor(), afd2.getStartOffset(), afd2.getLength())
+                .build();
+
+        resid = R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz;
+        AssetFileDescriptor afd3 = mResources.openRawResourceFd(resid);
+        DataSourceDesc dsd3 = new DataSourceDesc.Builder()
+                .setDataSource(afd3.getFileDescriptor(), afd3.getStartOffset(), afd3.getLength())
+                .build();
+
         ArrayList<DataSourceDesc> nextDSDs = new ArrayList<DataSourceDesc>(2);
         nextDSDs.add(dsd2);
-        nextDSDs.add(dsd1);
+        nextDSDs.add(dsd3);
 
         mPlayer.setNextDataSources(nextDSDs);
 
-        Monitor onCompletion1Called = new Monitor();
+        Monitor onStartCalled = new Monitor();
+        Monitor onStart2Called = new Monitor();
+        Monitor onStart3Called = new Monitor();
         Monitor onCompletion2Called = new Monitor();
-        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+        Monitor onCompletion3Called = new Monitor();
+        Monitor onListCompletionCalled = new Monitor();
+        MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
             @Override
             public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
                 if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
                     Log.i(LOG_TAG, "testPlaylist: prepared dsd MediaId=" + dsd.getMediaId());
                     mOnPrepareCalled.signal();
-                } else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
-                    if (dsd == dsd1) {
-                        onCompletion1Called.signal();
-                    } else if (dsd == dsd2) {
-                        onCompletion2Called.signal();
+                } else if (what == MediaPlayer2.MEDIA_INFO_DATA_SOURCE_START) {
+                    if (dsd == dsd2) {
+                        Log.i(LOG_TAG, "testPlaylist: MEDIA_INFO_DATA_SOURCE_START dsd2");
+                        onStart2Called.signal();
+                    } else if (dsd == dsd3) {
+                        Log.i(LOG_TAG, "testPlaylist: MEDIA_INFO_DATA_SOURCE_START dsd3");
+                        onStart3Called.signal();
                     } else {
+                        Log.i(LOG_TAG, "testPlaylist: MEDIA_INFO_DATA_SOURCE_START other");
+                        onStartCalled.signal();
+                    }
+                } else if (what == MediaPlayer2.MEDIA_INFO_DATA_SOURCE_END) {
+                    if (dsd == dsd2) {
+                        Log.i(LOG_TAG, "testPlaylist: MEDIA_INFO_DATA_SOURCE_END dsd2");
+                        onCompletion2Called.signal();
+                    } else if (dsd == dsd3) {
+                        Log.i(LOG_TAG, "testPlaylist: MEDIA_INFO_DATA_SOURCE_END dsd3");
+                        onCompletion3Called.signal();
+                    } else {
+                        Log.i(LOG_TAG, "testPlaylist: MEDIA_INFO_DATA_SOURCE_END other");
                         mOnCompletionCalled.signal();
                     }
+                } else if (what == MediaPlayer2.MEDIA_INFO_DATA_SOURCE_LIST_END) {
+                    Log.i(LOG_TAG, "testPlaylist: MEDIA_INFO_DATA_SOURCE_LIST_END");
+                    onListCompletionCalled.signal();
                 }
             }
         };
@@ -958,8 +992,8 @@
         }
 
         mOnCompletionCalled.reset();
-        onCompletion1Called.reset();
         onCompletion2Called.reset();
+        onCompletion3Called.reset();
 
         mPlayer.setDisplay(mActivity.getSurfaceHolder());
 
@@ -967,9 +1001,13 @@
 
         mPlayer.play();
 
+        onStartCalled.waitForSignal();
+        onStart2Called.waitForSignal();
+        onStart3Called.waitForSignal();
         mOnCompletionCalled.waitForSignal();
         onCompletion2Called.waitForSignal();
-        onCompletion1Called.waitForSignal();
+        onCompletion3Called.waitForSignal();
+        onListCompletionCalled.waitForSignal();
 
         mPlayer.reset();
     }
@@ -986,12 +1024,12 @@
             return; // skip
         }
 
-        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+        MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
             @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) {
+                } else if (what == MediaPlayer2.MEDIA_INFO_DATA_SOURCE_END) {
                     mOnCompletionCalled.signal();
                 }
             }
@@ -1057,7 +1095,7 @@
             return; // skip
         }
 
-        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+        MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
             @Override
             public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
                 if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
@@ -1116,7 +1154,7 @@
             return; // skip
         }
 
-        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+        MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
             @Override
             public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
                 if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
@@ -1212,7 +1250,7 @@
         }
 
         Monitor onPauseCalled = new Monitor();
-        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+        MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
             @Override
             public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
                 if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
@@ -1247,7 +1285,7 @@
         assertEquals("MediaPlayer2 had error in clockRate " + ts1.getMediaClockRate(),
                 playbackRate, ts1.getMediaClockRate(), 0.001f);
         assertTrue("The nanoTime of Media timestamp should be taken when getTimestamp is called.",
-                nt1 <= ts1.getAnchorSytemNanoTime() && ts1.getAnchorSytemNanoTime() <= nt2);
+                nt1 <= ts1.getAnchorSystemNanoTime() && ts1.getAnchorSystemNanoTime() <= nt2);
 
         onPauseCalled.reset();
         mPlayer.pause();
@@ -1618,7 +1656,7 @@
 
         getInstrumentation().waitForIdleSync();
 
-        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+        MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
             @Override
             public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
                 if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
@@ -1639,19 +1677,17 @@
                     mOnDeselectTrackCalled.signal();
                 }
             }
-        };
-        synchronized (mEventCbLock) {
-            mEventCallbacks.add(ecb);
-        }
 
-        mPlayer.setOnSubtitleDataListener(new MediaPlayer2.OnSubtitleDataListener() {
             @Override
-            public void onSubtitleData(MediaPlayer2 mp, SubtitleData data) {
+            public void onSubtitleData(MediaPlayer2 mp, DataSourceDesc dsd, SubtitleData data) {
                 if (data != null && data.getData() != null) {
                     mOnSubtitleDataCalled.signal();
                 }
             }
-        });
+        };
+        synchronized (mEventCbLock) {
+            mEventCallbacks.add(ecb);
+        }
 
         mPlayer.setDisplay(getActivity().getSurfaceHolder());
         mPlayer.setScreenOnWhilePlaying(true);
@@ -1704,16 +1740,7 @@
             return; // skip;
         }
 
-        mPlayer.setOnSubtitleDataListener(new MediaPlayer2.OnSubtitleDataListener() {
-            @Override
-            public void onSubtitleData(MediaPlayer2 mp, SubtitleData data) {
-                if (data != null && data.getData() != null) {
-                    mOnSubtitleDataCalled.signal();
-                }
-            }
-        });
-
-        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+        MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
             @Override
             public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
                 if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
@@ -1729,6 +1756,13 @@
                     mOnPlayCalled.signal();
                 }
             }
+
+            @Override
+            public void onSubtitleData(MediaPlayer2 mp, DataSourceDesc dsd, SubtitleData data) {
+                if (data != null && data.getData() != null) {
+                    mOnSubtitleDataCalled.signal();
+                }
+            }
         };
         synchronized (mEventCbLock) {
             mEventCallbacks.add(ecb);
@@ -1777,7 +1811,7 @@
 
         getInstrumentation().waitForIdleSync();
 
-        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+        MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
             @Override
             public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
                 if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
@@ -1854,18 +1888,18 @@
             return 0; // skip
         }
         mOnCompletionCalled.reset();
-        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+        MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
             @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) {
+                } else if (what == MediaPlayer2.MEDIA_INFO_DATA_SOURCE_END) {
                     mOnCompletionCalled.signal();
                     mPlayer.play();
                 }
             }
         };
-        mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
+        mPlayer.registerEventCallback(mExecutor, ecb);
 
         mOnPrepareCalled.reset();
         mPlayer.prepare();
@@ -1912,12 +1946,12 @@
         mPlayer.setAudioAttributes(attributes);
 
         mOnCompletionCalled.reset();
-        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+        MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
             @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) {
+                } else if (what == MediaPlayer2.MEDIA_INFO_DATA_SOURCE_END) {
                     mOnCompletionCalled.signal();
                 }
             }
@@ -1929,7 +1963,7 @@
                 }
             }
         };
-        mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
+        mPlayer.registerEventCallback(mExecutor, ecb);
 
         mOnPrepareCalled.reset();
         mPlayer.prepare();
@@ -1965,7 +1999,7 @@
         mPlayer.setScreenOnWhilePlaying(true);
 
         mOnCompletionCalled.reset();
-        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+        MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
             @Override
             public void onVideoSizeChanged(MediaPlayer2 mp, DataSourceDesc dsd,
                     int width, int height) {
@@ -1983,7 +2017,7 @@
 
                 if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
                     mOnPrepareCalled.signal();
-                } else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
+                } else if (what == MediaPlayer2.MEDIA_INFO_DATA_SOURCE_END) {
                     mOnCompletionCalled.signal();
                 }
             }
@@ -2131,7 +2165,7 @@
         Thread.sleep(SLEEP_TIME);
         assertFalse(mPlayer.isPlaying());
 
-        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+        MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
             @Override
             public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
                 if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
@@ -2146,7 +2180,7 @@
                 }
             }
         };
-        mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
+        mPlayer.registerEventCallback(mExecutor, ecb);
 
         mOnPlayCalled.reset();
         mPlayer.play();
@@ -2159,7 +2193,7 @@
                 .setDataSource(dataSource)
                 .build());
 
-        mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
+        mPlayer.registerEventCallback(mExecutor, ecb);
 
         mOnPrepareCalled.reset();
         mPlayer.prepare();
@@ -2182,7 +2216,7 @@
         if (IGNORE_TESTS) {
             return;
         }
-        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+        MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
             @Override
             public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
                 if (what == MediaPlayer2.CALL_COMPLETED_SET_DATA_SOURCE) {
@@ -2191,7 +2225,7 @@
                 }
             }
         };
-        mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
+        mPlayer.registerEventCallback(mExecutor, ecb);
 
         mCallStatus = MediaPlayer2.CALL_STATUS_NO_ERROR;
         mPlayer.setDataSource((DataSourceDesc)null);
@@ -2203,7 +2237,7 @@
         if (IGNORE_TESTS) {
             return;
         }
-        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+        MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
             @Override
             public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
                 if (what == MediaPlayer2.CALL_COMPLETED_SET_DATA_SOURCE) {
@@ -2212,7 +2246,7 @@
                 }
             }
         };
-        mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
+        mPlayer.registerEventCallback(mExecutor, ecb);
 
         TestMedia2DataSource dataSource = new TestMedia2DataSource(new byte[0]);
         mPlayer.setDataSource(new DataSourceDesc.Builder()
@@ -2239,7 +2273,7 @@
                 .setDataSource(dataSource)
                 .build());
 
-        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+        MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
             @Override
             public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
                 if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
@@ -2276,7 +2310,7 @@
                 .build());
 
         setOnErrorListener();
-        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
+        MediaPlayer2.EventCallback ecb = new MediaPlayer2.EventCallback() {
             @Override
             public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
                 if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayer2TestBase.java b/tests/tests/media/src/android/media/cts/MediaPlayer2TestBase.java
index 2975d47..45c25f9 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayer2TestBase.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayer2TestBase.java
@@ -77,11 +77,11 @@
     protected MediaStubActivity mActivity;
 
     protected final Object mEventCbLock = new Object();
-    protected List<MediaPlayer2.MediaPlayer2EventCallback> mEventCallbacks =
-            new ArrayList<MediaPlayer2.MediaPlayer2EventCallback>();
+    protected List<MediaPlayer2.EventCallback> mEventCallbacks =
+            new ArrayList<MediaPlayer2.EventCallback>();
     protected final Object mEventCbLock2 = new Object();
-    protected List<MediaPlayer2.MediaPlayer2EventCallback> mEventCallbacks2 =
-            new ArrayList<MediaPlayer2.MediaPlayer2EventCallback>();
+    protected List<MediaPlayer2.EventCallback> mEventCallbacks2 =
+            new ArrayList<MediaPlayer2.EventCallback>();
 
     // convenience functions to create MediaPlayer2
     protected static MediaPlayer2 createMediaPlayer2(Context context, Uri uri) {
@@ -111,8 +111,8 @@
             }
             Monitor onPrepareCalled = new Monitor();
             ExecutorService executor = Executors.newFixedThreadPool(1);
-            MediaPlayer2.MediaPlayer2EventCallback ecb =
-                new MediaPlayer2.MediaPlayer2EventCallback() {
+            MediaPlayer2.EventCallback ecb =
+                new MediaPlayer2.EventCallback() {
                     @Override
                     public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
                         if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
@@ -120,10 +120,10 @@
                         }
                     }
                 };
-            mp.setMediaPlayer2EventCallback(executor, ecb);
+            mp.registerEventCallback(executor, ecb);
             mp.prepare();
             onPrepareCalled.waitForSignal();
-            mp.clearMediaPlayer2EventCallback();
+            mp.unregisterEventCallback(ecb);
             executor.shutdown();
             return mp;
         } catch (IllegalArgumentException ex) {
@@ -166,8 +166,8 @@
 
             Monitor onPrepareCalled = new Monitor();
             ExecutorService executor = Executors.newFixedThreadPool(1);
-            MediaPlayer2.MediaPlayer2EventCallback ecb =
-                new MediaPlayer2.MediaPlayer2EventCallback() {
+            MediaPlayer2.EventCallback ecb =
+                new MediaPlayer2.EventCallback() {
                     @Override
                     public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
                         if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
@@ -175,10 +175,10 @@
                         }
                     }
                 };
-            mp.setMediaPlayer2EventCallback(executor, ecb);
+            mp.registerEventCallback(executor, ecb);
             mp.prepare();
             onPrepareCalled.waitForSignal();
-            mp.clearMediaPlayer2EventCallback();
+            mp.unregisterEventCallback(ecb);
             afd.close();
             executor.shutdown();
             return mp;
@@ -242,12 +242,12 @@
     }
 
     protected void setUpMP2ECb(MediaPlayer2 mp, Object cbLock,
-            List<MediaPlayer2.MediaPlayer2EventCallback> ecbs) {
-        mp.setMediaPlayer2EventCallback(mExecutor, new MediaPlayer2.MediaPlayer2EventCallback() {
+            List<MediaPlayer2.EventCallback> ecbs) {
+        mp.registerEventCallback(mExecutor, new MediaPlayer2.EventCallback() {
             @Override
             public void onVideoSizeChanged(MediaPlayer2 mp, DataSourceDesc dsd, int w, int h) {
                 synchronized (cbLock) {
-                    for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
+                    for (MediaPlayer2.EventCallback ecb : ecbs) {
                         ecb.onVideoSizeChanged(mp, dsd, w, h);
                     }
                 }
@@ -256,7 +256,7 @@
             @Override
             public void onTimedText(MediaPlayer2 mp, DataSourceDesc dsd, TimedText text) {
                 synchronized (cbLock) {
-                    for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
+                    for (MediaPlayer2.EventCallback ecb : ecbs) {
                         ecb.onTimedText(mp, dsd, text);
                     }
                 }
@@ -266,7 +266,7 @@
             public void onTimedMetaDataAvailable(MediaPlayer2 mp, DataSourceDesc dsd,
                     TimedMetaData data) {
                 synchronized (cbLock) {
-                    for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
+                    for (MediaPlayer2.EventCallback ecb : ecbs) {
                         ecb.onTimedMetaDataAvailable(mp, dsd, data);
                     }
                 }
@@ -275,7 +275,7 @@
             @Override
             public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
                 synchronized (cbLock) {
-                    for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
+                    for (MediaPlayer2.EventCallback ecb : ecbs) {
                         ecb.onError(mp, dsd, what, extra);
                     }
                 }
@@ -284,7 +284,7 @@
             @Override
             public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
                 synchronized (cbLock) {
-                    for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
+                    for (MediaPlayer2.EventCallback ecb : ecbs) {
                         ecb.onInfo(mp, dsd, what, extra);
                     }
                 }
@@ -293,18 +293,18 @@
             @Override
             public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
                 synchronized (cbLock) {
-                    for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
+                    for (MediaPlayer2.EventCallback ecb : ecbs) {
                         ecb.onCallCompleted(mp, dsd, what, status);
                     }
                 }
             }
 
             @Override
-            public void onMediaTimeChanged(MediaPlayer2 mp, DataSourceDesc dsd,
+            public void onMediaTimeDiscontinuity(MediaPlayer2 mp, DataSourceDesc dsd,
                     MediaTimestamp timestamp) {
                 synchronized (cbLock) {
-                    for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
-                        ecb.onMediaTimeChanged(mp, dsd, timestamp);
+                    for (MediaPlayer2.EventCallback ecb : ecbs) {
+                        ecb.onMediaTimeDiscontinuity(mp, dsd, timestamp);
                     }
                 }
             }
@@ -312,7 +312,7 @@
             @Override
             public void onCommandLabelReached(MediaPlayer2 mp, Object label) {
                 synchronized (cbLock) {
-                    for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
+                    for (MediaPlayer2.EventCallback ecb : ecbs) {
                         ecb.onCommandLabelReached(mp, label);
                     }
                 }
@@ -462,7 +462,7 @@
         mPlayer.setScreenOnWhilePlaying(true);
 
         synchronized (mEventCbLock) {
-            mEventCallbacks.add(new MediaPlayer2.MediaPlayer2EventCallback() {
+            mEventCallbacks.add(new MediaPlayer2.EventCallback() {
                 @Override
                 public void onVideoSizeChanged(MediaPlayer2 mp, DataSourceDesc dsd, int w, int h) {
                     if (w == 0 && h == 0) {
@@ -568,7 +568,7 @@
             }
         }
 
-        mPlayer.stop();
+        mPlayer.pause();
     }
 
     private static class PrepareFailedException extends Exception {}
@@ -585,7 +585,7 @@
 
     protected void setOnErrorListener() {
         synchronized (mEventCbLock) {
-            mEventCallbacks.add(new MediaPlayer2.MediaPlayer2EventCallback() {
+            mEventCallbacks.add(new MediaPlayer2.EventCallback() {
                 @Override
                 public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
                     mOnErrorCalled.signal();
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
index 91bc3ee..83e43dd 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
@@ -1333,7 +1333,7 @@
         assertEquals("MediaPlayer had error in clockRate " + ts1.getMediaClockRate(),
                 playbackRate, ts1.getMediaClockRate(), 0.001f);
         assertTrue("The nanoTime of Media timestamp should be taken when getTimestamp is called.",
-                nt1 <= ts1.getAnchorSytemNanoTime() && ts1.getAnchorSytemNanoTime() <= nt2);
+                nt1 <= ts1.getAnchorSystemNanoTime() && ts1.getAnchorSystemNanoTime() <= nt2);
 
         mMediaPlayer.pause();
         ts1 = mMediaPlayer.getTimestamp();
diff --git a/tests/tests/media/src/android/media/cts/StreamingMediaPlayer2Test.java b/tests/tests/media/src/android/media/cts/StreamingMediaPlayer2Test.java
index c1769ac..0ab7dea 100644
--- a/tests/tests/media/src/android/media/cts/StreamingMediaPlayer2Test.java
+++ b/tests/tests/media/src/android/media/cts/StreamingMediaPlayer2Test.java
@@ -364,8 +364,8 @@
             mPlayer.setScreenOnWhilePlaying(true);
 
             mOnBufferingUpdateCalled.reset();
-            MediaPlayer2.MediaPlayer2EventCallback ecb =
-                new MediaPlayer2.MediaPlayer2EventCallback() {
+            MediaPlayer2.EventCallback ecb =
+                new MediaPlayer2.EventCallback() {
                     @Override
                     public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
                         fail("Media player had error " + what + " playing " + name);
@@ -380,7 +380,7 @@
                         }
                     }
                 };
-            mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
+            mPlayer.registerEventCallback(mExecutor, ecb);
 
             assertFalse(mOnBufferingUpdateCalled.isSignalled());
 
@@ -396,7 +396,7 @@
                 mPlayer.play();
                 Thread.sleep(SLEEP_TIME);
             }
-            mPlayer.stop();
+            mPlayer.pause();
             mPlayer.reset();
         } finally {
             mServer.shutdown();
@@ -428,8 +428,8 @@
 
             mOnBufferingUpdateCalled.reset();
 
-            MediaPlayer2.MediaPlayer2EventCallback ecb =
-                new MediaPlayer2.MediaPlayer2EventCallback() {
+            MediaPlayer2.EventCallback ecb =
+                new MediaPlayer2.EventCallback() {
                     @Override
                     public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
                         mOnErrorCalled.signal();
@@ -444,7 +444,7 @@
                         }
                     }
                 };
-            mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
+            mPlayer.registerEventCallback(mExecutor, ecb);
 
             assertFalse(mOnBufferingUpdateCalled.isSignalled());
             try {
@@ -471,8 +471,8 @@
             }
 
             Monitor onSetBufferingParamsCalled = new Monitor();
-            MediaPlayer2.MediaPlayer2EventCallback ecb =
-                new MediaPlayer2.MediaPlayer2EventCallback() {
+            MediaPlayer2.EventCallback ecb =
+                new MediaPlayer2.EventCallback() {
                     @Override
                     public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
                         fail("Media player had error " + what + " playing " + name);
@@ -492,7 +492,7 @@
                         }
                     }
                 };
-            mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
+            mPlayer.registerEventCallback(mExecutor, ecb);
 
             // getBufferingParams should be called after setDataSource.
             try {
@@ -602,19 +602,19 @@
             mPlayer.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
 
             final Object completion = new Object();
-            MediaPlayer2.MediaPlayer2EventCallback ecb =
-                new MediaPlayer2.MediaPlayer2EventCallback() {
+            MediaPlayer2.EventCallback ecb =
+                new MediaPlayer2.EventCallback() {
                     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) {
+                        } else if (what == MediaPlayer2.MEDIA_INFO_DATA_SOURCE_END) {
                             if (run++ == 0) {
                                 mPlayer.seekTo(0, MediaPlayer2.SEEK_PREVIOUS_SYNC);
                                 mPlayer.play();
                             } else {
-                                mPlayer.stop();
+                                mPlayer.pause();
                                 synchronized (completion) {
                                     completion.notify();
                                 }
@@ -662,7 +662,7 @@
                     }
                 }
             };
-            mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
+            mPlayer.registerEventCallback(mExecutor, ecb);
 
             mPlayer.prepare();
             mOnPrepareCalled.waitForSignal();
@@ -757,8 +757,8 @@
                     .setDataSource(mContext, uri)
                     .build());
 
-            MediaPlayer2.MediaPlayer2EventCallback ecb =
-                new MediaPlayer2.MediaPlayer2EventCallback() {
+            MediaPlayer2.EventCallback ecb =
+                new MediaPlayer2.EventCallback() {
                     @Override
                     public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
                         if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
@@ -766,7 +766,7 @@
                         }
                     }
                 };
-            mp.setMediaPlayer2EventCallback(mExecutor, ecb);
+            mp.registerEventCallback(mExecutor, ecb);
 
             mp.prepare();
             Thread.sleep(1000);
diff --git a/tests/tests/multiuser/AndroidTest.xml b/tests/tests/multiuser/AndroidTest.xml
index 2edb2ec..595fcd5 100644
--- a/tests/tests/multiuser/AndroidTest.xml
+++ b/tests/tests/multiuser/AndroidTest.xml
@@ -17,6 +17,7 @@
 <configuration description="Config for CTS Multiuser 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="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsMultiUserTestCases.apk" />
diff --git a/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp b/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
index ea3b033..bc72a0f 100644
--- a/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
+++ b/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
@@ -841,20 +841,31 @@
         ALOGI("Test skipped: sRGB hardware buffers require EGL_EXT_image_gl_colorspace");
         return false;
     }
-    if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP &&
-        !HasGLExtension("GL_EXT_EGL_image_storage")) {
-        ALOGI("Test skipped: cube map array hardware buffers require "
-              "GL_EXT_EGL_image_storage");
-        return false;
-    }
 
     int result = AHardwareBuffer_allocate(&desc, &mBuffer);
     // Skip if this format cannot be allocated.
     if (result != NO_ERROR) {
-        ALOGI("Test skipped: %s not supported",
+        ALOGI("Test skipped: format %s not supported",
               AHBFormatAsString(desc.format));
         return false;
     }
+
+    // The code below will only execute if allocating an AHardwareBuffer succeeded.
+    // Fail early if the buffer is mipmapped or a cube map, but the GL extension required
+    // to actually access it from GL is not present.
+    if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP &&
+        !HasGLExtension("GL_EXT_EGL_image_storage")) {
+        ADD_FAILURE() << "Cube map AHardwareBuffer allocation succeeded, but the extension "
+            "GL_EXT_EGL_image_storage is not present";
+        return false;
+    }
+    if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE &&
+            !HasGLExtension("GL_EXT_EGL_image_storage")) {
+        ADD_FAILURE() << "Mipmapped AHardwareBuffer allocation succeeded, but the extension "
+            "GL_EXT_EGL_image_storage is not present";
+        return false;
+    }
+
     // Do not create the EGLImage if this is a blob format.
     if (desc.format == AHARDWAREBUFFER_FORMAT_BLOB) return true;
 
@@ -866,7 +877,10 @@
     mEGLImage = eglCreateImageKHR(
         mDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
         eglGetNativeClientBufferANDROID(mBuffer), attrib_list);
-    EXPECT_NE(EGL_NO_IMAGE_KHR, mEGLImage);
+    EXPECT_NE(EGL_NO_IMAGE_KHR, mEGLImage) <<
+        "AHardwareBuffer allocation succeeded, but binding it to an EGLImage failed. "
+        "This is usually caused by a version mismatch between the gralloc implementation and "
+        "the OpenGL/EGL driver. Please contact your GPU vendor to resolve this problem.";
     return mEGLImage != EGL_NO_IMAGE_KHR;
 }
 
@@ -1016,11 +1030,6 @@
             glEGLImageTargetTexture2DOES(mTexTarget, static_cast<GLeglImageOES>(mEGLImage));
         }
     }
-    // If the texture does not have mipmaps, set a filter that does not require them.
-    if (!(desc.usage & AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE)) {
-        glTexParameteri(mTexTarget, GL_TEXTURE_MAX_LEVEL, 0);
-        glTexParameteri(mTexTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-    }
     ASSERT_EQ(GLenum{GL_NO_ERROR}, glGetError());
 }
 
@@ -1653,8 +1662,7 @@
         AHardwareBuffer_Desc{75, 33, 1, GL_RGB8, 0, kGlFormat, 0, 0},
         AHardwareBuffer_Desc{64, 80, 1, GL_RGBA8, 0, kGlFormat, 0, 0},
         AHardwareBuffer_Desc{49, 23, 1, GL_SRGB8_ALPHA8, 0, kGlFormat | kUseSrgb, 0, 0},
-        // TODO: enable for Android Q.
-        // AHardwareBuffer_Desc{63, 78, 1, GL_RGB565, 0, kGlFormat, 0, 0},
+        AHardwareBuffer_Desc{63, 78, 1, GL_RGB565, 0, kGlFormat, 0, 0},
         AHardwareBuffer_Desc{42, 41, 1, GL_RGBA16F, 0, kGlFormat, 0, 0},
         AHardwareBuffer_Desc{37, 63, 1, GL_RGB10_A2, 0, kGlFormat, 0, 0},
         AHardwareBuffer_Desc{33, 20, 1, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, 0, 0, 0, 0},
@@ -1675,8 +1683,7 @@
         AHardwareBuffer_Desc{64, 80, 6, GL_RGBA8, 0, kGlFormat, 0, 0},
         AHardwareBuffer_Desc{33, 28, 4, GL_SRGB8_ALPHA8, 0, kGlFormat | kUseSrgb, 0, 0},
         AHardwareBuffer_Desc{42, 41, 3, GL_RGBA16F, 0, kGlFormat, 0, 0},
-        // TODO: enable for Android Q.
-        // AHardwareBuffer_Desc{63, 78, 3, GL_RGB565, 0, kGlFormat, 0, 0},
+        AHardwareBuffer_Desc{63, 78, 3, GL_RGB565, 0, kGlFormat, 0, 0},
         AHardwareBuffer_Desc{37, 63, 4, GL_RGB10_A2, 0, kGlFormat, 0, 0},
         AHardwareBuffer_Desc{25, 77, 7, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, 0, 0, 0, 0},
         AHardwareBuffer_Desc{25, 77, 7, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, 0, kUseSrgb, 0, 0},
diff --git a/tests/tests/nativemedia/aaudio/jni/test_aaudio.cpp b/tests/tests/nativemedia/aaudio/jni/test_aaudio.cpp
index 141b867..cf8bcff 100644
--- a/tests/tests/nativemedia/aaudio/jni/test_aaudio.cpp
+++ b/tests/tests/nativemedia/aaudio/jni/test_aaudio.cpp
@@ -184,8 +184,7 @@
     // Allocate a buffer for the audio data.
     // TODO handle possibility of other data formats
     size_t dataSizeSamples = framesPerBurst() * actual().channelCount;
-    mData.reset(new int16_t[dataSizeSamples]);
-    memset(&mData[0], 0, dataSizeSamples);
+    mData.reset(new int16_t[dataSizeSamples]{});
 }
 
 TEST_P(AAudioOutputStreamTest, testWriting) {
diff --git a/tests/tests/nativemidi/Android.mk b/tests/tests/nativemidi/Android.mk
new file mode 100755
index 0000000..5da14e4
--- /dev/null
+++ b/tests/tests/nativemidi/Android.mk
@@ -0,0 +1,46 @@
+# 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)
+
+#
+# NativeMidiEchoTest
+#
+include $(CLEAR_VARS)
+
+# Don't include this package in any target.
+LOCAL_MODULE_TAGS := optional
+
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, java)
+
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner
+LOCAL_JNI_SHARED_LIBRARIES := libnativemidi_jni
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
+# Must match the package name in CtsTestCaseList.mk
+LOCAL_PACKAGE_NAME := CtsNativeMidiTestCases
+LOCAL_MULTILIB := both
+
+LOCAL_SDK_VERSION := current
+LOCAL_NDK_STL_VARIANT := c++_static
+
+include $(BUILD_CTS_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/nativemidi/AndroidManifest.xml b/tests/tests/nativemidi/AndroidManifest.xml
new file mode 100755
index 0000000..5c42c52
--- /dev/null
+++ b/tests/tests/nativemidi/AndroidManifest.xml
@@ -0,0 +1,51 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.nativemidi.cts">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+
+    <uses-feature android:name="android.software.midi" android:required="true"/>
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+
+        <service android:name="NativeMidiEchoTestService"
+                android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE">
+            <intent-filter>
+                <action android:name="android.media.midi.MidiDeviceService" />
+            </intent-filter>
+            <meta-data android:name="android.media.midi.MidiDeviceService"
+                android:resource="@xml/echo_device_info" />
+        </service>
+
+        <activity android:name="android.nativemidi.cts.NativeMidiEchoTest"
+                  android:label="NativeMidiEchoTest"/>
+    </application>
+
+    <!--  self-instrumenting test package. -->
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:label="CTS Native MIDI tests"
+        android:targetPackage="android.nativemidi.cts" >
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+</manifest>
+
diff --git a/tests/tests/nativemidi/AndroidTest.xml b/tests/tests/nativemidi/AndroidTest.xml
new file mode 100644
index 0000000..3a4774a
--- /dev/null
+++ b/tests/tests/nativemidi/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<configuration description="Config for CTS MIDI test cases">
+    <option name="config-descriptor:metadata" key="component" value="media" />
+    <option name="test-suite-tag" value="cts" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsMidiTestCases.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.nativemidi.cts" />
+        <option name="runtime-hint" value="8m" />
+    </test>
+</configuration>
diff --git a/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
new file mode 100644
index 0000000..1de9703
--- /dev/null
+++ b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
@@ -0,0 +1,544 @@
+/*
+ * 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.nativemidi.cts;
+
+import android.app.Activity;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.media.midi.MidiManager;
+import android.media.midi.MidiOutputPort;
+import android.media.midi.MidiDevice;
+import android.media.midi.MidiDevice.MidiConnection;
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiDeviceInfo.PortInfo;
+import android.media.midi.MidiDeviceStatus;
+import android.media.midi.MidiInputPort;
+import android.media.midi.MidiReceiver;
+import android.media.midi.MidiSender;
+
+import android.os.Bundle;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import android.test.AndroidTestCase;
+
+import android.util.Log;
+
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.Random;
+
+import org.junit.Assert;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+/*
+ * Test Class
+ */
+@RunWith(AndroidJUnit4.class)
+public class NativeMidiEchoTest {
+    private static final String TAG = "NativeMidiEchoTest";
+
+    public static final String TEST_MANUFACTURER = "AndroidCTS";
+    public static final String ECHO_PRODUCT = "NativeMidiEcho";
+
+    private static final long NANOS_PER_MSEC = 1000L * 1000L;
+
+    // This number seems excessively large and it is not clear if there is a linear
+    // relationship between the number of messages sent and the time required to send them
+    private static final int TIMEOUT_PER_MESSAGE_MS = 10;
+
+    // This timeout value is very generous.
+    private static final int TIMEOUT_OPEN_MSEC = 2000; // arbitrary
+
+    private Context mContext = InstrumentationRegistry.getContext();
+    private MidiManager mMidiManager;
+
+    private MidiDevice mEchoDevice;
+
+    private Random mRandom = new Random(1972941337);
+
+    // (Native code) attributes associated with a test/EchoServer instance.
+    private long mTestContext;
+
+    static {
+        System.loadLibrary("nativemidi_jni");
+    }
+
+    /*
+     * Helpers
+     */
+    private boolean hasMidiSupport() {
+        PackageManager pm = mContext.getPackageManager();
+        return pm.hasSystemFeature(PackageManager.FEATURE_MIDI);
+    }
+
+    private byte[] generateRandomMessage(int len) {
+        byte[] buffer = new byte[len];
+        for(int index = 0; index < len; index++) {
+            buffer[index] = (byte)(mRandom.nextInt() & 0xFF);
+        }
+        return buffer;
+    }
+
+    private void generateRandomBufers(byte[][] buffers, long timestamps[], int numMessages) {
+        int messageLen;
+        int maxMessageLen = 128;
+        for(int buffIndex = 0; buffIndex < numMessages; buffIndex++) {
+            messageLen = (int)(mRandom.nextFloat() * (maxMessageLen-1)) + 1;
+            buffers[buffIndex] = generateRandomMessage(messageLen);
+            timestamps[buffIndex] = mRandom.nextLong();
+        }
+    }
+
+    private void compareMessages(byte[] buffer, long timestamp, NativeMidiMessage nativeMsg) {
+        Assert.assertEquals("byte count of message", buffer.length, nativeMsg.len);
+        Assert.assertEquals("timestamp in message", timestamp, nativeMsg.timestamp);
+
+        for (int index = 0; index < buffer.length; index++) {
+            Assert.assertEquals("message byte[" + index + "]", buffer[index] & 0x0FF,
+                    nativeMsg.buffer[index] & 0x0FF);
+        }
+    }
+
+    /*
+     * Echo Server
+     */
+    // Listens for an asynchronous device open and notifies waiting foreground
+    // test.
+    class MyTestOpenCallback implements MidiManager.OnDeviceOpenedListener {
+        private volatile MidiDevice mDevice;
+
+        @Override
+        public synchronized void onDeviceOpened(MidiDevice device) {
+            mDevice = device;
+            notifyAll();
+        }
+
+        public synchronized MidiDevice waitForOpen(int msec)
+              throws InterruptedException {
+            long deadline = System.currentTimeMillis() + msec;
+            long timeRemaining = msec;
+            while (mDevice == null && timeRemaining > 0) {
+                wait(timeRemaining);
+                timeRemaining = deadline - System.currentTimeMillis();
+            }
+            return mDevice;
+        }
+    }
+
+     protected void setUpEchoServer() throws Exception {
+        Log.i(TAG, "++ setUpEchoServer()");
+        MidiDeviceInfo echoInfo = findEchoDevice();
+
+        // Open device.
+        MyTestOpenCallback callback = new MyTestOpenCallback();
+        mMidiManager.openDevice(echoInfo, callback, null);
+        mEchoDevice = callback.waitForOpen(TIMEOUT_OPEN_MSEC);
+        Assert.assertNotNull("could not open " + ECHO_PRODUCT, mEchoDevice);
+
+        // Query echo service directly to see if it is getting status updates.
+        NativeMidiEchoTestService echoService = NativeMidiEchoTestService.getInstance();
+
+        mTestContext = allocTestContext();
+        Assert.assertTrue("couldn't allocate test context.", mTestContext != 0);
+
+        // Open Device
+        int result = openNativeMidiDevice(mTestContext, mEchoDevice);
+        Assert.assertEquals("Bad open native MIDI device", 0, result);
+
+        // Open Input
+        result = startWritingMidi(mTestContext, 0/*mPortNumber*/);
+        Assert.assertEquals("Bad start writing (native) MIDI", 0, result);
+
+        // Open Output
+        result = startReadingMidi(mTestContext, 0/*mPortNumber*/);
+        Assert.assertEquals("Bad start Reading (native) MIDI", 0, result);
+    }
+
+    protected void tearDownEchoServer() throws IOException {
+        Log.i(TAG, "++ tearDownEchoServer()");
+        // Query echo service directly to see if it is getting status updates.
+        NativeMidiEchoTestService echoService = NativeMidiEchoTestService.getInstance();
+
+        int result;
+
+        // Stop inputs
+        result = stopReadingMidi(mTestContext);
+        Assert.assertEquals("Bad stop reading (native) MIDI", 0, result);
+
+        // Stop outputs
+        result = stopWritingMidi(mTestContext);
+        Assert.assertEquals("Bad stop writing (native) MIDI", 0, result);
+
+        // Close Device
+        result = closeNativeMidiDevice(mTestContext);
+        Assert.assertEquals("Bad close native MIDI device", 0, result);
+
+        freeTestContext(mTestContext);
+        mTestContext = 0;
+
+        mEchoDevice.close();
+    }
+
+    // Search through the available devices for the ECHO loop-back device.
+    protected MidiDeviceInfo findEchoDevice() {
+        MidiDeviceInfo[] infos = mMidiManager.getDevices();
+        MidiDeviceInfo echoInfo = null;
+        for (MidiDeviceInfo info : infos) {
+            Bundle properties = info.getProperties();
+            String manufacturer = (String) properties.get(
+                    MidiDeviceInfo.PROPERTY_MANUFACTURER);
+
+            if (TEST_MANUFACTURER.equals(manufacturer)) {
+                String product = (String) properties.get(
+                        MidiDeviceInfo.PROPERTY_PRODUCT);
+                if (ECHO_PRODUCT.equals(product)) {
+                    echoInfo = info;
+                    break;
+                }
+            }
+        }
+        Assert.assertNotNull("could not find " + ECHO_PRODUCT, echoInfo);
+        return echoInfo;
+    }
+
+    @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);
+
+        setUpEchoServer();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        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
+        }
+
+        Assert.assertNotNull("MidiManager not supported.", mMidiManager);
+
+        // There should be at least one device for the Echo server.
+        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_B_SendData() throws Exception {
+        Log.i(TAG, "++++ test_B_SendData() this:" + System.identityHashCode(this));
+
+        Assert.assertEquals("Didn't start with 0 sends", 0, getNumSends(mTestContext));
+        Assert.assertEquals("Didn't start with 0 bytes sent", 0, getNumBytesSent(mTestContext));
+
+        final byte[] buffer = {
+                (byte) 0x93, 0x47, 0x52
+        };
+        long timestamp = 0x0123765489ABFEDCL;
+        writeMidi(mTestContext, buffer, 0, buffer.length);
+
+        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
+        }
+
+        final byte[] buffer = {
+                (byte) 0x93, 0x47, 0x52
+        };
+        long timestamp = 0x0123765489ABFEDCL;
+
+        writeMidiWithTimestamp(mTestContext, buffer, 0, 0, timestamp); // should be a NOOP
+        writeMidiWithTimestamp(mTestContext, buffer, 0, buffer.length, timestamp);
+        writeMidiWithTimestamp(mTestContext, buffer, 0, 0, timestamp); // should be a NOOP
+
+        // Wait for message to pass quickly through echo service.
+        final int numMessages = 1;
+        final int timeoutMs = TIMEOUT_PER_MESSAGE_MS * numMessages;
+        Thread.sleep(timeoutMs);
+        Assert.assertEquals("number of messages.",
+                numMessages, getNumReceivedMessages(mTestContext));
+
+        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
+        }
+
+        int numMessages = 100;
+        byte[][] buffers = new byte[numMessages][];
+        long timestamps[] = new long[numMessages];
+        generateRandomBufers(buffers, timestamps, numMessages);
+
+        for(int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+            writeMidiWithTimestamp(mTestContext, buffers[msgIndex], 0, buffers[msgIndex].length,
+                    timestamps[msgIndex]);
+        }
+
+        // Wait for message to pass quickly through echo service.
+        final int timeoutMs = TIMEOUT_PER_MESSAGE_MS * numMessages;
+        Thread.sleep(timeoutMs);
+
+        // correct number of messages
+        Assert.assertEquals("number of messages.",
+                numMessages, getNumReceivedMessages(mTestContext));
+
+        // correct data & order?
+        for(int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+            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
+        }
+
+        int numMessages = 7;
+        byte[][] buffers = new byte[numMessages][];
+        long timestamps[] = new long[numMessages];
+        generateRandomBufers(buffers, timestamps, numMessages);
+
+        for(int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+            writeMidiWithTimestamp(mTestContext, buffers[msgIndex], 0, buffers[msgIndex].length,
+              timestamps[msgIndex]);
+        }
+
+        // Wait for message to pass through echo service.
+        final int timeoutMs = TIMEOUT_PER_MESSAGE_MS * numMessages;
+        Thread.sleep(timeoutMs);
+
+        int result = flushSentMessages(mTestContext);
+        Assert.assertEquals("flush messages failed", 0, result);
+
+        // correct number of messages
+        Assert.assertEquals("number of messages.",
+                numMessages, getNumReceivedMessages(mTestContext));
+
+        // correct data & order?
+        for(int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+            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
+        }
+
+        // Arbitrarily large message.
+        int hugeMessageLen = 1024 * 10;
+        byte[] buffer = generateRandomMessage(hugeMessageLen);
+        int result = writeMidi(mTestContext, buffer, 0, buffer.length);
+        Assert.assertEquals("Huge write failed.", hugeMessageLen, result);
+
+        int kindaHugeMessageLen = 1024 * 2;
+        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");
+    }
+
+    /**
+     * Check a large timeout for the echoed messages to come through. If they exceed this
+     * or don't come through at all, something is wrong.
+     */
+    @Test
+    public void test_G_NativeEchoTime() throws Exception {
+        Log.i(TAG, "++++ test_G_NativeEchoTime() this:" + System.identityHashCode(this));
+        if (!hasMidiSupport()) {
+            return; // nothing to test
+        }
+
+        final int numMessages = 10;
+        final long maxLatencyNanos = 15 * NANOS_PER_MSEC; // generally < 3 msec on N6
+        byte[] buffer = { (byte) 0x93, 0, 64 };
+
+        // Send multiple messages in a burst.
+        for (int index = 0; index < numMessages; index++) {
+            buffer[1] = (byte) (60 + index);
+            writeMidiWithTimestamp(mTestContext, buffer, 0, buffer.length, System.nanoTime());
+        }
+
+        // Wait for messages to pass quickly through echo service.
+        final int timeoutMs = TIMEOUT_PER_MESSAGE_MS * numMessages;
+        Thread.sleep(timeoutMs);
+        Assert.assertEquals("number of messages.",
+                numMessages, getNumReceivedMessages(mTestContext));
+
+        for (int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+            NativeMidiMessage message = getReceivedMessageAt(mTestContext, msgIndex);
+            Assert.assertEquals("message index", (byte) (60 + msgIndex), message.buffer[1]);
+            long elapsedNanos = message.timeReceived - message.timestamp;
+            // If this test fails then there may be a problem with the thread scheduler
+            // or there may be kernel activity that is blocking execution at the user level.
+            Assert.assertTrue("MIDI round trip latency index:" + msgIndex
+                    + " too large, " + elapsedNanos
+                    + " nanoseconds " +
+                    "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
+        }
+
+        int numMessages = 2;
+        byte[][] buffers = new byte[numMessages][];
+        long timestamps[] = new long[numMessages];
+        generateRandomBufers(buffers, timestamps, numMessages);
+
+        for(int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+            writeMidiWithTimestamp(mTestContext, buffers[msgIndex], 0, buffers[msgIndex].length,
+                    timestamps[msgIndex]);
+        }
+
+        // Wait for message to pass quickly through echo service.
+        final int timeoutMs = TIMEOUT_PER_MESSAGE_MS * numMessages;
+        Thread.sleep(timeoutMs);
+
+        int result = matchNativeMessages(mTestContext);
+        Assert.assertEquals("Native Compare Test Failed", result, 0);
+
+        Log.i(TAG, "++++ test_H_EchoNMessages_PureNative() - DONE");
+    }
+
+    /**
+     * Check a large timeout for the echoed messages to come through. If they exceed this
+     * or don't come through at all, something is wrong.
+     */
+    @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
+        }
+
+        final int numMessages = 10;
+        final long maxLatencyNanos = 15 * NANOS_PER_MSEC; // generally < 3 msec on N6
+        byte[] buffer = { (byte) 0x93, 0, 64 };
+
+        // Send multiple messages in a burst.
+        for (int index = 0; index < numMessages; index++) {
+            buffer[1] = (byte) (60 + index);
+            writeMidiWithTimestamp(mTestContext, buffer, 0, buffer.length, System.nanoTime());
+        }
+
+        // Wait for messages to pass through echo service.
+        final int timeoutMs = TIMEOUT_PER_MESSAGE_MS * numMessages;
+        Thread.sleep(timeoutMs);
+        Assert.assertEquals("number of messages.",
+                numMessages, getNumReceivedMessages(mTestContext));
+
+        int result = checkNativeLatency(mTestContext, maxLatencyNanos);
+        Assert.assertEquals("failed pure native latency test.", 0, result);
+
+        Log.i(TAG, "++++ test_I_NativeEchoTime_PureNative() - DONE");
+    }
+
+    // Native Routines
+    public static native void initN();
+
+    public static native long allocTestContext();
+    public static native void freeTestContext(long context);
+
+    public native int openNativeMidiDevice(long ctx, MidiDevice device);
+    public native int closeNativeMidiDevice(long ctx);
+
+    public native int startReadingMidi(long ctx, int portNumber);
+    public native int stopReadingMidi(long ctx);
+
+    public native int startWritingMidi(long ctx, int portNumber);
+    public native int stopWritingMidi(long ctx);
+
+    public native int writeMidi(long ctx, byte[] data, int offset, int length);
+    public native int writeMidiWithTimestamp(long ctx, byte[] data, int offset, int length,
+            long timestamp);
+    public native int flushSentMessages(long ctx);
+
+    // Status - Counters
+    public native int getNumSends(long ctx);
+    public native int getNumBytesSent(long ctx);
+    public native int getNumReceives(long ctx);
+    public native int getNumBytesReceived(long ctx);
+
+    // Status - Received Messages
+    public native int getNumReceivedMessages(long ctx);
+    public native NativeMidiMessage getReceivedMessageAt(long ctx, int index);
+
+    // Pure Native Checks
+    public native int matchNativeMessages(long ctx);
+    public native int checkNativeLatency(long ctx, long maxLatencyNanos);
+}
diff --git a/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTestService.java b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTestService.java
new file mode 100644
index 0000000..1984419
--- /dev/null
+++ b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTestService.java
@@ -0,0 +1,91 @@
+/*
+ * 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.nativemidi.cts;
+
+import android.content.Context;
+
+import android.media.midi.MidiDeviceService;
+import android.media.midi.MidiDeviceStatus;
+import android.media.midi.MidiManager;
+import android.media.midi.MidiReceiver;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Virtual MIDI Device that copies its input to its output.
+ * This is a Java Service that is used to test the Native MIDI API.
+ */
+
+public class NativeMidiEchoTestService extends MidiDeviceService {
+    private static final String TAG = "NativeMidiEchoTestService";
+
+    // Other apps will write to this port.
+    private MidiReceiver mInputReceiver = new MyReceiver();
+    // This app will copy the data to this port.
+    private MidiReceiver mOutputReceiver;
+    private static NativeMidiEchoTestService mInstance;
+
+    // These are public so we can easily read them from CTS test.
+    public int statusChangeCount;
+    public boolean inputOpened;
+    public int outputOpenCount;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mInstance = this;
+        MidiManager midiManager = (MidiManager)getSystemService(Context.MIDI_SERVICE);
+        if (midiManager == null) {
+            Log.e(TAG, "No MIDI Manager!");
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+    }
+
+    // For CTS testing, so I can read test fields.
+    public static NativeMidiEchoTestService getInstance() {
+        return mInstance;
+    }
+
+    @Override
+    public MidiReceiver[] onGetInputPortReceivers() {
+        return new MidiReceiver[] { mInputReceiver };
+    }
+
+    class MyReceiver extends MidiReceiver {
+        @Override
+        public void onSend(byte[] data, int offset, int count, long timestamp)
+                throws IOException {
+            if (mOutputReceiver == null) {
+                mOutputReceiver = getOutputPortReceivers()[0];
+            }
+            // Copy input to output.
+            mOutputReceiver.send(data, offset, count, timestamp);
+        }
+    }
+
+    @Override
+    public void onDeviceStatusChanged(MidiDeviceStatus status) {
+        statusChangeCount++;
+        inputOpened = status.isInputPortOpen(0);
+        outputOpenCount = status.getOutputPortOpenCount(0);
+    }
+}
diff --git a/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiMessage.java b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiMessage.java
new file mode 100644
index 0000000..d8186fe
--- /dev/null
+++ b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiMessage.java
@@ -0,0 +1,46 @@
+/*
+ * 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.nativemidi.cts;
+
+public class NativeMidiMessage {
+    public static final int AMIDI_PACKET_SIZE = 1024;
+    public static final int AMIDI_PACKET_OVERHEAD = 9;
+    public static final int AMIDI_BUFFER_SIZE = AMIDI_PACKET_SIZE - AMIDI_PACKET_OVERHEAD;
+
+    public int opcode;
+    public byte[] buffer = new byte[AMIDI_BUFFER_SIZE];
+    public int len;
+    public long timestamp;
+    public long timeReceived;
+
+    public NativeMidiMessage() {}
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("{opcode:" + opcode + " [len:" + len + "|");
+        for(int index = 0; index < len; index++) {
+            sb.append("0x" + Integer.toHexString(buffer[index] & 0x00FF));
+            if (index < len - 1) {
+                sb.append(" ");
+            }
+        }
+        sb.append("] timestamp:" + Long.toHexString(timestamp));
+
+        return sb.toString();
+    }
+
+}
\ No newline at end of file
diff --git a/tests/tests/nativemidi/jni/Android.mk b/tests/tests/nativemidi/jni/Android.mk
new file mode 100644
index 0000000..bb62dec
--- /dev/null
+++ b/tests/tests/nativemidi/jni/Android.mk
@@ -0,0 +1,33 @@
+# 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 := tests
+
+LOCAL_MODULE := libnativemidi_jni
+LOCAL_MULTILIB := both
+
+LOCAL_SRC_FILES := native-lib.cpp
+
+LOCAL_CFLAGS += -Wall -Wextra -Werror -O0
+
+LOCAL_SDK_VERSION := current
+LOCAL_NDK_STL_VARIANT := c++_static
+
+LOCAL_SHARED_LIBRARIES := libamidi liblog
+LOCAL_WHOLE_STATIC_LIBRARIES := libnativetesthelper_jni
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/nativemidi/jni/native-lib.cpp b/tests/tests/nativemidi/jni/native-lib.cpp
new file mode 100644
index 0000000..e68859c
--- /dev/null
+++ b/tests/tests/nativemidi/jni/native-lib.cpp
@@ -0,0 +1,534 @@
+/*
+ * 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.
+ */
+
+#include <atomic>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string>
+#include <thread>
+#include <time.h>
+#include <vector>
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NativeMidiManager-JNI"
+#include <android/log.h>
+
+#include <jni.h>
+
+#include <amidi/midi.h>
+
+extern "C" {
+
+/*
+ * Structures for storing data flowing through the echo server.
+ */
+#define SIZE_DATABUFFER 256
+/*
+ * Received Messages
+ */
+typedef struct {
+    std::unique_ptr<uint8_t[]> dataBuff;
+    size_t numDataBytes;
+    int32_t opCode;
+    int64_t timestamp;
+    int64_t timeReceived;
+} ReceivedMessageRecord;
+
+/*
+ * Sent Messages
+ */
+typedef struct {
+    std::unique_ptr<uint8_t[]> dataBuff;
+    size_t numDataBytes;
+    int64_t timestamp;
+    long timeSent;
+} SentMessageRecord;
+
+/*
+ * Context
+ * Holds the state of a given test and native MIDI I/O setup for that test.
+ * NOTE: There is one of these per test (and therefore unique to each test thread).
+ */
+class TestContext {
+private:
+    // counters
+    std::atomic<int> mNumSends;
+    std::atomic<int> mNumBytesSent;
+    std::atomic<int> mNumReceives;
+    std::atomic<int> mNumBytesReceived;
+
+    std::vector<ReceivedMessageRecord> mReceivedMsgs;
+    std::vector<SentMessageRecord> mSentMsgs;
+
+    // Java NativeMidiMessage class stuff, for passing messages back out to the Java client.
+    jclass mClsNativeMidiMessage;
+    jmethodID mMidNativeMidiMessage_ctor;
+    jfieldID mFid_opcode;
+    jfieldID mFid_buffer;
+    jfieldID mFid_len;
+    jfieldID mFid_timestamp;
+    jfieldID mFid_timeReceived;
+
+    std::mutex lock;
+
+public:
+    // read Thread
+    std::unique_ptr<std::thread> mReadThread;
+    std::atomic<bool> mReading;
+
+    AMidiDevice* nativeDevice;
+    std::atomic<AMidiOutputPort*> midiOutputPort;
+    std::atomic<AMidiInputPort*> midiInputPort;
+
+    TestContext() :
+        mNumSends(0),
+        mNumBytesSent(0),
+        mNumReceives(0),
+        mNumBytesReceived(0),
+        mClsNativeMidiMessage(0),
+        mMidNativeMidiMessage_ctor(0),
+        mFid_opcode(0),
+        mFid_buffer(0),
+        mFid_len(0),
+        mFid_timestamp(0),
+        mFid_timeReceived(0),
+        mReading(false),
+        nativeDevice(nullptr),
+        midiOutputPort(nullptr),
+        midiInputPort(nullptr)
+    {}
+
+    bool initN(JNIEnv* env);
+
+    int getNumSends() { return mNumSends; }
+    void incNumSends() { mNumSends++; }
+
+    int getNumBytesSent() { return mNumBytesSent; }
+    void incNumBytesSent(int numBytes) { mNumBytesSent += numBytes; }
+
+    int getNumReceives() { return mNumReceives; }
+    void incNumReceives() { mNumReceives++; }
+
+    int getNumBytesReceived() { return mNumBytesReceived; }
+    void incNumBytesReceived(int numBytes) { mNumBytesReceived += numBytes; }
+
+    void addSent(SentMessageRecord&& msg) { mSentMsgs.push_back(std::move(msg)); }
+    size_t getNumSentMsgs() { return mSentMsgs.size(); }
+
+    void addReceived(ReceivedMessageRecord&& msg) { mReceivedMsgs.push_back(std::move(msg)); }
+    size_t getNumReceivedMsgs() { return mReceivedMsgs.size(); }
+
+    jobject transferReceiveMsgAt(JNIEnv* env, int index);
+
+    static const int COMPARE_SUCCESS = 0;
+    static const int COMPARE_COUNTMISSMATCH = 1;
+    static const int COMPARE_DATALENMISMATCH = 2;
+    static const int COMPARE_DATAMISMATCH = 3;
+    static const int COMPARE_TIMESTAMPMISMATCH = 4;
+    int compareInsAndOuts();
+
+    static const int CHECKLATENCY_SUCCESS = 0;
+    static const int CHECKLATENCY_COUNTMISSMATCH = 1;
+    static const int CHECKLATENCY_LATENCYEXCEEDED = 2;
+    int checkInOutLatency(long maxLatencyNanos);
+};
+
+//
+// Helpers
+//
+static long System_nanoTime() {
+    // this code is the implementation of System.nanoTime()
+    // from system/code/ojluni/src/main/native/System.
+    struct timespec now;
+    clock_gettime(CLOCK_MONOTONIC, &now);
+    return now.tv_sec * 1000000000LL + now.tv_nsec;
+}
+
+bool TestContext::initN(JNIEnv* env) {
+    static const char* clsSigNativeMidiMessage = "android/nativemidi/cts/NativeMidiMessage";
+
+    jclass cls = env->FindClass(clsSigNativeMidiMessage);
+    if (cls == NULL) {
+        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
+                "JNI Error - couldn't find NativeMidiMessage class");
+        return false; // we are doomed, so bail.
+    }
+    mClsNativeMidiMessage = (jclass)env->NewGlobalRef(cls);
+    if (mClsNativeMidiMessage == NULL) {
+        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
+                "JNI Error - couldn't allocate NativeMidiMessage");
+        return false; // we are doomed, so bail.
+    }
+
+    mMidNativeMidiMessage_ctor = env->GetMethodID(mClsNativeMidiMessage, "<init>", "()V");
+    mFid_opcode = env->GetFieldID(mClsNativeMidiMessage, "opcode", "I");
+    mFid_buffer = env->GetFieldID(mClsNativeMidiMessage, "buffer", "[B");
+    mFid_len = env->GetFieldID( mClsNativeMidiMessage, "len", "I");
+    mFid_timestamp = env->GetFieldID(mClsNativeMidiMessage, "timestamp", "J");
+    mFid_timeReceived = env->GetFieldID(mClsNativeMidiMessage, "timeReceived", "J");
+    if (mMidNativeMidiMessage_ctor == NULL ||
+        mFid_opcode == NULL ||
+        mFid_buffer == NULL ||
+        mFid_len == NULL ||
+        mFid_timestamp == NULL ||
+        mFid_timeReceived == NULL) {
+        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
+                "JNI Error - couldn't load Field IDs");
+        return false; // we are doomed, so bail.
+    }
+
+    return true;
+}
+
+jobject TestContext::transferReceiveMsgAt(JNIEnv* env, int index) {
+    jobject msg = NULL;
+
+    if (index < (int)mReceivedMsgs.size()) {
+        ReceivedMessageRecord receiveRec = std::move(mReceivedMsgs.at(index));
+        msg = env->NewObject(mClsNativeMidiMessage, mMidNativeMidiMessage_ctor);
+
+        env->SetIntField(msg, mFid_opcode, receiveRec.opCode);
+        env->SetIntField(msg, mFid_len, receiveRec.numDataBytes);
+        jobject buffer_array = env->GetObjectField(msg, mFid_buffer);
+        env->SetByteArrayRegion(reinterpret_cast<jbyteArray>(buffer_array), 0,
+                receiveRec.numDataBytes, (jbyte*)receiveRec.dataBuff.get());
+        env->SetLongField(msg, mFid_timestamp, receiveRec.timestamp);
+        env->SetLongField(msg, mFid_timeReceived, receiveRec.timeReceived);
+    }
+
+    return msg;
+}
+
+int TestContext::compareInsAndOuts() {
+    // Number of messages sent/received
+    if (mReceivedMsgs.size() != mSentMsgs.size()) {
+        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "---- COMPARE_COUNTMISSMATCH r:%zu s:%zu",
+                mReceivedMsgs.size(), mSentMsgs.size());
+       return COMPARE_COUNTMISSMATCH;
+    }
+
+    // we know that both vectors have the same number of messages from the test above.
+    size_t numMessages = mSentMsgs.size();
+    for (size_t msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+        // Data Length?
+        if (mReceivedMsgs[msgIndex].numDataBytes != mSentMsgs[msgIndex].numDataBytes) {
+            __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
+                    "---- COMPARE_DATALENMISMATCH r:%zu s:%zu",
+                    mReceivedMsgs[msgIndex].numDataBytes, mSentMsgs[msgIndex].numDataBytes);
+            return COMPARE_DATALENMISMATCH;
+        }
+
+        // Timestamps
+        if (mReceivedMsgs[msgIndex].timestamp != mSentMsgs[msgIndex].timestamp) {
+            __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "---- COMPARE_TIMESTAMPMISMATCH");
+            return COMPARE_TIMESTAMPMISMATCH;
+        }
+
+        // we know that the data in both messages have the same number of bytes from the test above.
+        int dataLen = mReceivedMsgs[msgIndex].numDataBytes;
+        for (int dataIndex = 0; dataIndex < dataLen; dataIndex++) {
+            // Data Values?
+            if (mReceivedMsgs[msgIndex].dataBuff[dataIndex] !=
+                    mSentMsgs[msgIndex].dataBuff[dataIndex]) {
+                __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
+                        "---- COMPARE_DATAMISMATCH r:%d s:%d",
+                        (int)mReceivedMsgs[msgIndex].dataBuff[dataIndex],
+                        (int)mSentMsgs[msgIndex].dataBuff[dataIndex]);
+                return COMPARE_DATAMISMATCH;
+            }
+        }
+    }
+
+    return COMPARE_SUCCESS;
+}
+
+int TestContext::checkInOutLatency(long maxLatencyNanos) {
+    if (mReceivedMsgs.size() != mSentMsgs.size()) {
+        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "  ---- CHECKLATENCY_COUNTMISSMATCH");
+        return CHECKLATENCY_COUNTMISSMATCH;
+    }
+
+    // we know that both vectors have the same number of messages
+    // from the test above.
+    int numMessages = mSentMsgs.size();
+    for (int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+        long timeDelta =  mSentMsgs[msgIndex].timeSent - mReceivedMsgs[msgIndex].timestamp;
+        if (timeDelta > maxLatencyNanos) {
+            __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
+                    "  ---- CHECKLATENCY_LATENCYEXCEEDED %ld", timeDelta);
+            return CHECKLATENCY_LATENCYEXCEEDED;
+        }
+    }
+
+    return CHECKLATENCY_SUCCESS;
+}
+
+JNIEXPORT jlong JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_allocTestContext(
+        JNIEnv* env, jclass) {
+    TestContext* context = new TestContext;
+    if (!context->initN(env)) {
+        delete context;
+        context = NULL;
+    }
+
+    return (jlong)context;
+}
+
+JNIEXPORT void JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_freeTestContext(
+        JNIEnv*, jclass, jlong context) {
+    delete (TestContext*)context;
+}
+
+/*
+ * Receiving API
+ */
+//static void DumpDataMessage(ReceivedMessageRecord* msg) {
+//    char midiDumpBuffer[SIZE_DATABUFFER * 4]; // more than enough
+//    memset(midiDumpBuffer, 0, sizeof(midiDumpBuffer));
+//    int pos = snprintf(midiDumpBuffer, sizeof(midiDumpBuffer),
+//            "%" PRIx64 " ", msg->timestamp);
+//    for (uint8_t *b = msg->buffer, *e = b + msg->numDataBytes; b < e; ++b) {
+//        pos += snprintf(midiDumpBuffer + pos, sizeof(midiDumpBuffer) - pos,
+//                "%02x ", *b);
+//    }
+//    __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "---- DUMP %s", midiDumpBuffer);
+//}
+
+void readThreadRoutine(TestContext* context) {
+    int32_t opCode;
+    uint8_t inDataBuffer[SIZE_DATABUFFER];
+    size_t numDataBytes;
+    int64_t timestamp;
+
+    while (context->mReading) {
+        AMidiOutputPort* outputPort = context->midiOutputPort.load();
+        if (outputPort != nullptr) {
+            ssize_t numMessages =
+                AMidiOutputPort_receive(outputPort, &opCode,
+                    inDataBuffer, sizeof(inDataBuffer), &numDataBytes, &timestamp);
+
+            if (numMessages > 0) {
+                context->incNumReceives();
+                context->incNumBytesReceived(numDataBytes);
+                ReceivedMessageRecord receiveRec;
+                receiveRec.timeReceived = System_nanoTime();
+                receiveRec.numDataBytes = numDataBytes;
+                receiveRec.dataBuff.reset(new uint8_t[receiveRec.numDataBytes]);
+                memcpy(receiveRec.dataBuff.get(), inDataBuffer, receiveRec.numDataBytes);
+                receiveRec.opCode = opCode;
+                receiveRec.timestamp = timestamp;
+                context->addReceived(std::move(receiveRec));
+            }
+        }
+    }
+}
+
+static media_status_t commonDeviceOpen(JNIEnv *env, jobject midiDeviceObj, AMidiDevice** device) {
+    media_status_t status = AMidiDevice_fromJava(env, midiDeviceObj, device);
+    if (status == AMEDIA_OK) {
+        // __android_log_print(ANDROID_LOG_INFO, LOG_TAG,
+        //      "---- Obtained device token for obj %p: dev %p", midiDeviceObj, device);
+    } else {
+        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
+                "---- Could not obtain device token for obj %p: status:%d", midiDeviceObj, status);
+    }
+
+    return status;
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_openNativeMidiDevice(
+        JNIEnv* env, jobject, jlong ctx, jobject deviceObj) {
+    TestContext* context = (TestContext*)ctx;
+    media_status_t status = commonDeviceOpen(env, deviceObj, &context->nativeDevice);
+    return status;
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_closeNativeMidiDevice(
+        JNIEnv*, jobject, jlong ctx) {
+    TestContext* context = (TestContext*)ctx;
+    media_status_t status = AMidiDevice_release(context->nativeDevice);
+    return status;
+}
+
+/*
+ * Sending API
+ */
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_startWritingMidi(
+        JNIEnv*, jobject, jlong ctx, jint portNumber) {
+
+    TestContext* context = (TestContext*)ctx;
+
+    AMidiInputPort* inputPort;
+    media_status_t status = AMidiInputPort_open(context->nativeDevice, portNumber, &inputPort);
+    if (status == AMEDIA_OK) {
+        // __android_log_print(ANDROID_LOG_INFO, LOG_TAG,
+        //      "---- Opened INPUT port %d: token %p", portNumber, inputPort);
+        context->midiInputPort.store(inputPort);
+    } else {
+        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "---- Could not open INPUT port %p:%d %d",
+                context->nativeDevice, portNumber, status);
+        return status;
+    }
+
+    return AMEDIA_OK;
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_stopWritingMidi(
+        JNIEnv*, jobject, jlong ctx) {
+
+    TestContext* context = (TestContext*)ctx;
+
+    AMidiInputPort* inputPort = context->midiInputPort.exchange(nullptr);
+    if (inputPort == nullptr) {
+        return -1;
+    }
+
+    AMidiInputPort_close(inputPort);
+
+    return 0;
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_writeMidiWithTimestamp(
+        JNIEnv* env, jobject,
+        jlong ctx, jbyteArray data, jint offset, jint numBytes, jlong timestamp) {
+
+    TestContext* context = (TestContext*)ctx;
+    context->incNumSends();
+    context->incNumBytesSent(numBytes);
+
+    jbyte* bufferPtr = env->GetByteArrayElements(data, NULL);
+    if (bufferPtr == NULL) {
+        return -1;
+    }
+
+    int numWritten =  AMidiInputPort_sendWithTimestamp(
+            context->midiInputPort, (uint8_t*)bufferPtr + offset, numBytes, timestamp);
+    if (numWritten > 0) {
+        // Don't save a send record if we didn't send!
+        SentMessageRecord sendRec;
+        sendRec.numDataBytes = numBytes;
+        sendRec.dataBuff.reset(new uint8_t[sendRec.numDataBytes]);
+        memcpy(sendRec.dataBuff.get(), (uint8_t*)bufferPtr + offset, numBytes);
+        sendRec.timestamp = timestamp;
+        sendRec.timeSent = System_nanoTime();
+        context->addSent(std::move(sendRec));
+    }
+
+    env->ReleaseByteArrayElements(data, bufferPtr, JNI_ABORT);
+
+    return numWritten;
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_writeMidi(
+        JNIEnv* env, jobject j_object, jlong ctx, jbyteArray data, jint offset, jint numBytes) {
+    return Java_android_nativemidi_cts_NativeMidiEchoTest_writeMidiWithTimestamp(
+            env, j_object, ctx, data, offset, numBytes, 0L);
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_flushSentMessages(
+        JNIEnv*, jobject, jlong ctx) {
+    TestContext* context = (TestContext*)ctx;
+    return AMidiInputPort_sendFlush(context->midiInputPort);
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getNumSends(
+        JNIEnv*, jobject, jlong ctx) {
+    return ((TestContext*)ctx)->getNumSends();
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getNumBytesSent(
+        JNIEnv*, jobject, jlong ctx) {
+    return ((TestContext*)ctx)->getNumBytesSent();
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getNumReceives(
+        JNIEnv*, jobject, jlong ctx) {
+    return ((TestContext*)ctx)->getNumReceives();
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getNumBytesReceived(
+        JNIEnv*, jobject, jlong ctx) {
+    return ((TestContext*)ctx)->getNumBytesReceived();
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_startReadingMidi(
+        JNIEnv*, jobject, jlong ctx, jint portNumber) {
+
+    // __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "++++ startReadingMidi()");
+    TestContext* context = (TestContext*)ctx;
+
+    AMidiOutputPort* outputPort;
+    media_status_t status = AMidiOutputPort_open(context->nativeDevice, portNumber, &outputPort);
+    if (status == AMEDIA_OK) {
+        context->midiOutputPort.store(outputPort);
+    } else {
+        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
+                "---- Could not open OUTPUT port %p : %d %d",
+                context->nativeDevice, portNumber, status);
+        return status;
+    }
+
+    // Start read thread
+    context->mReading = true;
+    context->mReadThread.reset(new std::thread(readThreadRoutine, context));
+    std::this_thread::yield(); // let the read thread startup.
+
+    return status;
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_stopReadingMidi(
+        JNIEnv*, jobject, jlong ctx) {
+
+    // __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "++++ stopReadingMidi()");
+    TestContext* context = (TestContext*)ctx;
+    context->mReading = false;
+
+    context->mReadThread->join();
+
+    AMidiOutputPort* outputPort = context->midiOutputPort.exchange(nullptr);
+    if (outputPort == nullptr) {
+        return -1;
+    }
+
+    AMidiOutputPort_close(outputPort);
+
+    return 0;
+}
+
+/*
+ * Messages
+ */
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getNumReceivedMessages(
+        JNIEnv*, jobject, jlong ctx) {
+    return ((TestContext*)ctx)->getNumReceivedMsgs();
+}
+
+JNIEXPORT jobject JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getReceivedMessageAt(
+        JNIEnv* env, jobject, jlong ctx, jint index) {
+    return ((TestContext*)ctx)->transferReceiveMsgAt(env, index);
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_matchNativeMessages(
+        JNIEnv*, jobject, jlong ctx) {
+    return ((TestContext*)ctx)->compareInsAndOuts();
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_checkNativeLatency(
+        JNIEnv*, jobject, jlong ctx, jlong maxLatencyNanos) {
+    return ((TestContext*)ctx)->checkInOutLatency(maxLatencyNanos);
+}
+
+} // extern "C"
diff --git a/tests/tests/nativemidi/res/xml/echo_device_info.xml b/tests/tests/nativemidi/res/xml/echo_device_info.xml
new file mode 100644
index 0000000..9f2ebee
--- /dev/null
+++ b/tests/tests/nativemidi/res/xml/echo_device_info.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<devices>
+    <device manufacturer="AndroidCTS" product="NativeMidiEcho" tags="echo,test">
+        <input-port name="input" />
+        <output-port name="output" />
+    </device>
+</devices>
diff --git a/tests/tests/net/src/android/net/wifi/aware/OWNERS b/tests/tests/net/src/android/net/wifi/aware/OWNERS
index 4afc47f..cf116f8 100644
--- a/tests/tests/net/src/android/net/wifi/aware/OWNERS
+++ b/tests/tests/net/src/android/net/wifi/aware/OWNERS
@@ -1,2 +1 @@
 etancohen@google.com
-satk@google.com
\ No newline at end of file
diff --git a/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
index af91fbf..ea31fdf 100644
--- a/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
@@ -21,7 +21,10 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
@@ -32,9 +35,11 @@
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.net.wifi.hotspot2.pps.Credential;
 import android.net.wifi.hotspot2.pps.HomeSp;
+import android.os.Process;
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.test.AndroidTestCase;
+import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.compatibility.common.util.WifiConfigCreator;
@@ -45,6 +50,7 @@
 import java.security.cert.X509Certificate;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -951,4 +957,119 @@
 
         stopLocalOnlyHotspot(callback, wifiEnabled);
     }
+
+    /**
+     * Verify that the {@link android.Manifest.permission#NETWORK_STACK} permission is never held by
+     * any package.
+     * <p>
+     * No apps should <em>ever</em> attempt to acquire this permission, since it would give those
+     * apps extremely broad access to connectivity functionality.
+     */
+    public void testNetworkStackPermission() {
+        final PackageManager pm = getContext().getPackageManager();
+
+        final List<PackageInfo> holding = pm.getPackagesHoldingPermissions(new String[] {
+                android.Manifest.permission.NETWORK_STACK
+        }, PackageManager.MATCH_UNINSTALLED_PACKAGES);
+        for (PackageInfo pi : holding) {
+            fail("The NETWORK_STACK permission must not be held by " + pi.packageName
+                    + " and must be revoked for security reasons");
+        }
+    }
+
+    /**
+     * Verify that the {@link android.Manifest.permission#NETWORK_SETTINGS} permission is
+     * never held by any package.
+     * <p>
+     * Only Settings, SysUi and shell apps should <em>ever</em> attempt to acquire this
+     * permission, since it would give those apps extremely broad access to connectivity
+     * functionality.  The permission is intended to be granted to only those apps with direct user
+     * access and no others.
+     */
+    public void testNetworkSettingsPermission() {
+        final PackageManager pm = getContext().getPackageManager();
+
+        final ArraySet<String> allowedPackages = new ArraySet();
+        final ArraySet<Integer> allowedUIDs = new ArraySet();
+        // explicitly add allowed UIDs
+        allowedUIDs.add(Process.SYSTEM_UID);
+        allowedUIDs.add(Process.SHELL_UID);
+        allowedUIDs.add(Process.PHONE_UID);
+
+        // only quick settings is allowed to bind to the BIND_QUICK_SETTINGS_TILE permission, using
+        // this fact to determined allowed package name for sysui
+        String validPkg = "";
+        final List<PackageInfo> sysuiPackage = pm.getPackagesHoldingPermissions(new String[] {
+                android.Manifest.permission.BIND_QUICK_SETTINGS_TILE
+        }, PackageManager.MATCH_UNINSTALLED_PACKAGES);
+
+        if (sysuiPackage.size() > 1) {
+            fail("The BIND_QUICK_SETTINGS_TILE permission must only be held by one package");
+        }
+
+        if (sysuiPackage.size() == 1) {
+            validPkg = sysuiPackage.get(0).packageName;
+            allowedPackages.add(validPkg);
+        }
+
+        // the captive portal flow also currently holds the NETWORK_SETTINGS permission
+        final Intent intent = new Intent(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
+        final ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DISABLED_COMPONENTS);
+        if (ri != null) {
+            allowedPackages.add(ri.activityInfo.packageName);
+        }
+
+        final List<PackageInfo> holding = pm.getPackagesHoldingPermissions(new String[] {
+                android.Manifest.permission.NETWORK_SETTINGS
+        }, PackageManager.MATCH_UNINSTALLED_PACKAGES);
+        for (PackageInfo pi : holding) {
+            String packageName = pi.packageName;
+            if (allowedPackages.contains(packageName)) {
+                // this is an explicitly allowed package
+            } else {
+                // now check if the packages are from allowed UIDs
+                boolean allowed = false;
+                try {
+                    if (allowedUIDs.contains(pm.getPackageUid(packageName, 0))) {
+                        allowed = true;
+                        Log.d(TAG, packageName + " is on an allowed UID");
+                    }
+                } catch (PackageManager.NameNotFoundException e) { }
+                if (!allowed) {
+                    fail("The NETWORK_SETTINGS permission must not be held by "
+                            + packageName + " and must be revoked for security reasons");
+                }
+            }
+        }
+    }
+
+    /**
+     * Verify that the {@link android.Manifest.permission#NETWORK_SETUP_WIZARD} permission is
+     * only held by the device setup wizard application.
+     * <p>
+     * Only the SetupWizard app should <em>ever</em> attempt to acquire this
+     * permission, since it would give those apps extremely broad access to connectivity
+     * functionality.  The permission is intended to be granted to only the device setup wizard.
+     */
+    public void testNetworkSetupWizardPermission() {
+        final PackageManager pm = getContext().getPackageManager();
+
+        final Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.addCategory(Intent.CATEGORY_SETUP_WIZARD);
+        final ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DISABLED_COMPONENTS);
+        String validPkg = "";
+        if (ri != null) {
+            validPkg = ri.activityInfo.packageName;
+        }
+
+        final List<PackageInfo> holding = pm.getPackagesHoldingPermissions(new String[] {
+                android.Manifest.permission.NETWORK_SETUP_WIZARD
+        }, PackageManager.MATCH_UNINSTALLED_PACKAGES);
+        for (PackageInfo pi : holding) {
+            if (!Objects.equals(pi.packageName, validPkg)) {
+                fail("The NETWORK_SETUP_WIZARD permission must not be held by " + pi.packageName
+                    + " and must be revoked for security reasons [" + validPkg +"]");
+            }
+        }
+    }
 }
diff --git a/tests/tests/net/src/android/net/wifi/rtt/OWNERS b/tests/tests/net/src/android/net/wifi/rtt/OWNERS
index 4afc47f..cf116f8 100644
--- a/tests/tests/net/src/android/net/wifi/rtt/OWNERS
+++ b/tests/tests/net/src/android/net/wifi/rtt/OWNERS
@@ -1,2 +1 @@
 etancohen@google.com
-satk@google.com
\ No newline at end of file
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/Android.mk b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/Android.mk
index 7a11df4..032bcde 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/Android.mk
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/Android.mk
@@ -33,4 +33,6 @@
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 26
+
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/notificationlegacy/Android.mk b/tests/tests/notificationlegacy/Android.mk
index c1fb3fa3..07b2f80 100644
--- a/tests/tests/notificationlegacy/Android.mk
+++ b/tests/tests/notificationlegacy/Android.mk
@@ -39,4 +39,6 @@
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
+LOCAL_MIN_SDK_VERSION := 24
+
 include $(BUILD_CTS_PACKAGE)
\ No newline at end of file
diff --git a/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java b/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java
index 7a172e5..6626d95 100644
--- a/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java
+++ b/tests/tests/notificationlegacy/src/android/app/notification/legacy/cts/ConditionProviderServiceTest.java
@@ -22,6 +22,7 @@
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNull;
+import static junit.framework.TestCase.fail;
 
 import android.app.ActivityManager;
 import android.app.AutomaticZenRule;
@@ -182,6 +183,37 @@
         assertEquals(INTERRUPTION_FILTER_ALARMS, mNm.getCurrentInterruptionFilter());
     }
 
+    @Test
+    public void testRequestRebindWhenLostAccess() throws Exception {
+        if (mActivityManager.isLowRamDevice()) {
+            return;
+        }
+
+        // make sure it gets bound
+        pollForConnection(LegacyConditionProviderService.class, true);
+
+        // request unbind
+        LegacyConditionProviderService.getInstance().requestUnbind();
+
+        // make sure it unbinds
+        pollForConnection(LegacyConditionProviderService.class, false);
+
+        // lose dnd access
+        toggleNotificationPolicyAccess(mContext.getPackageName(),
+                InstrumentationRegistry.getInstrumentation(), false);
+
+        // try to rebind
+        LegacyConditionProviderService.requestRebind(LegacyConditionProviderService.getId());
+
+        // make sure it isn't rebound
+        try {
+            pollForConnection(LegacyConditionProviderService.class, true);
+            fail("Service got rebound after permission lost");
+        } catch (Exception e) {
+            // pass
+        }
+    }
+
     private void addRule(ComponentName cn, int filter, boolean enabled) {
         String id = mNm.addAutomaticZenRule(new AutomaticZenRule("name",
                 cn, Uri.EMPTY, filter, enabled));
diff --git a/tests/tests/os/Android.mk b/tests/tests/os/Android.mk
index 8f12610..cc62757 100644
--- a/tests/tests/os/Android.mk
+++ b/tests/tests/os/Android.mk
@@ -47,9 +47,7 @@
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-# uncomment when b/13282254 is fixed
-#LOCAL_SDK_VERSION := current
-LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_SDK_VERSION := test_current
 LOCAL_JAVA_LIBRARIES += android.test.runner.stubs
 LOCAL_JAVA_LIBRARIES += android.test.base.stubs
 
diff --git a/tests/tests/os/AndroidTest.xml b/tests/tests/os/AndroidTest.xml
index 8f8d997..45cfc4b 100644
--- a/tests/tests/os/AndroidTest.xml
+++ b/tests/tests/os/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Configuration for OS Tests">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsOsTestCases.apk" />
diff --git a/tests/tests/os/src/android/os/cts/AbiTest.java b/tests/tests/os/src/android/os/cts/AbiTest.java
index 1765ae7..f6fb3d1 100644
--- a/tests/tests/os/src/android/os/cts/AbiTest.java
+++ b/tests/tests/os/src/android/os/cts/AbiTest.java
@@ -30,7 +30,8 @@
 
 public class AbiTest extends TestCase {
     public void testNo64() throws Exception {
-        ArraySet<String> abiDirs = new ArraySet(Arrays.asList(
+        ArraySet<String> abiDirs = new ArraySet<>();
+        abiDirs.addAll(Arrays.asList(
             "/sbin",
             "/system",
             "/vendor"));
diff --git a/tests/tests/os/src/android/os/cts/BuildTest.java b/tests/tests/os/src/android/os/cts/BuildTest.java
index 7336fc0..0ebc438 100644
--- a/tests/tests/os/src/android/os/cts/BuildTest.java
+++ b/tests/tests/os/src/android/os/cts/BuildTest.java
@@ -22,16 +22,16 @@
 import android.os.Build;
 import android.platform.test.annotations.RestrictedBuildTest;
 
-import dalvik.system.VMRuntime;
-
 import junit.framework.TestCase;
 
 import java.io.IOException;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Scanner;
+import java.util.Set;
 import java.util.regex.Pattern;
 
 public class BuildTest extends TestCase {
@@ -40,16 +40,42 @@
     private static final String RO_PRODUCT_CPU_ABILIST32 = "ro.product.cpu.abilist32";
     private static final String RO_PRODUCT_CPU_ABILIST64 = "ro.product.cpu.abilist64";
 
-    /** Tests that check the values of {@link Build#CPU_ABI} and {@link Build#CPU_ABI2}. */
+    /**
+     * Verify that the values of the various CPU ABI fields are consistent.
+     */
     public void testCpuAbi() throws Exception {
         runTestCpuAbiCommon();
-        if (VMRuntime.getRuntime().is64Bit()) {
+        if (android.os.Process.is64Bit()) {
             runTestCpuAbi64();
         } else {
             runTestCpuAbi32();
         }
     }
 
+    /**
+     * Verify that the CPU ABI fields on device match the permitted ABIs defined by CDD.
+     */
+    public void testCpuAbi_valuesMatchPermitted() throws Exception {
+        // The permitted ABIs are listed in https://developer.android.com/ndk/guides/abis.
+        Set<String> just32 = new HashSet<>(Arrays.asList("armeabi", "armeabi-v7a", "x86"));
+        Set<String> just64 = new HashSet<>(Arrays.asList("x86_64", "arm64-v8a"));
+        Set<String> all = new HashSet<>();
+        all.addAll(just32);
+        all.addAll(just64);
+        Set<String> allAndEmpty = new HashSet<>(all);
+        allAndEmpty.add("");
+
+        // The cpu abi fields on the device must match the permitted values.
+        assertValueIsAllowed(all, Build.CPU_ABI);
+        // CPU_ABI2 will be empty when the device does not support a secondary CPU architecture.
+        assertValueIsAllowed(allAndEmpty, Build.CPU_ABI2);
+
+        // The supported abi fields on the device must match the permitted values.
+        assertValuesAreAllowed(all, Build.SUPPORTED_ABIS);
+        assertValuesAreAllowed(just32, Build.SUPPORTED_32_BIT_ABIS);
+        assertValuesAreAllowed(just64, Build.SUPPORTED_64_BIT_ABIS);
+    }
+
     private void runTestCpuAbiCommon() throws Exception {
         // The build property must match Build.SUPPORTED_ABIS exactly.
         final String[] abiListProperty = getStringList(RO_PRODUCT_CPU_ABILIST);
@@ -63,13 +89,13 @@
         // Every supported 32 bit ABI must be present in Build.SUPPORTED_ABIS.
         for (String abi : Build.SUPPORTED_32_BIT_ABIS) {
             assertTrue(abiList.contains(abi));
-            assertFalse(VMRuntime.is64BitAbi(abi));
+            assertFalse(Build.is64BitAbi(abi));
         }
 
         // Every supported 64 bit ABI must be present in Build.SUPPORTED_ABIS.
         for (String abi : Build.SUPPORTED_64_BIT_ABIS) {
             assertTrue(abiList.contains(abi));
-            assertTrue(VMRuntime.is64BitAbi(abi));
+            assertTrue(Build.is64BitAbi(abi));
         }
 
         // Build.CPU_ABI and Build.CPU_ABI2 must be present in Build.SUPPORTED_ABIS.
@@ -170,6 +196,17 @@
         }
     }
 
+    private static void assertValueIsAllowed(Set<String> allowedValues, String actualValue) {
+        assertTrue("Expected one of " + allowedValues + ", but was: '" + actualValue + "'",
+                allowedValues.contains(actualValue));
+    }
+
+    private static void assertValuesAreAllowed(Set<String> allowedValues, String[] actualValues) {
+        for (String actualValue : actualValues) {
+            assertValueIsAllowed(allowedValues, actualValue);
+        }
+    }
+
     private static final Pattern BOARD_PATTERN =
         Pattern.compile("^([0-9A-Za-z._-]+)$");
     private static final Pattern BRAND_PATTERN =
diff --git a/tests/tests/os/src/android/os/cts/DebugTest.java b/tests/tests/os/src/android/os/cts/DebugTest.java
index 0f19078..a7079cc 100644
--- a/tests/tests/os/src/android/os/cts/DebugTest.java
+++ b/tests/tests/os/src/android/os/cts/DebugTest.java
@@ -21,14 +21,12 @@
 
 import com.android.compatibility.common.util.TestThread;
 
-import dalvik.system.VMDebug;
-
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-import java.util.Map;
 
 public class DebugTest extends AndroidTestCase {
     private static final Logger Log = Logger.getLogger(DebugTest.class.getName());
@@ -51,7 +49,7 @@
         final String traceName = getFileName();
 
         final int bufSize = 1024 * 1024 * 2;
-        final int debug_flag = VMDebug.TRACE_COUNT_ALLOCS;
+        final int debug_flag = Debug.TRACE_COUNT_ALLOCS;
 
         Debug.startMethodTracing();
         Thread.sleep(debugTime);
diff --git a/tests/tests/os/src/android/os/cts/LaunchpadActivity.java b/tests/tests/os/src/android/os/cts/LaunchpadActivity.java
index 3919ece..d12d191 100644
--- a/tests/tests/os/src/android/os/cts/LaunchpadActivity.java
+++ b/tests/tests/os/src/android/os/cts/LaunchpadActivity.java
@@ -22,10 +22,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.Message;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -505,7 +503,6 @@
     @SuppressWarnings("deprecation")
     private Intent makeBroadcastIntent(String action) {
         final Intent intent = new Intent(action, null);
-        intent.putExtra("caller", mCallTarget);
         return intent;
     }
 
@@ -552,26 +549,6 @@
         }
     };
 
-    static final int GOT_RECEIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
-    static final int ERROR_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 1;
-
-    private final Binder mCallTarget = new Binder() {
-        @Override
-        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
-            data.setDataPosition(0);
-            data.enforceInterface(LaunchpadActivity.LAUNCH);
-            if (code == GOT_RECEIVE_TRANSACTION) {
-                final String name = data.readString();
-                gotReceive(name, null);
-                return true;
-            } else if (code == ERROR_TRANSACTION) {
-                finishBad(data.readString());
-                return true;
-            }
-            return false;
-        }
-    };
-
     private final void gotReceive(String name, Intent intent) {
         synchronized (this) {
 
diff --git a/tests/tests/os/src/android/os/cts/LocaleListTest.java b/tests/tests/os/src/android/os/cts/LocaleListTest.java
index bc30e5c..89780b2 100644
--- a/tests/tests/os/src/android/os/cts/LocaleListTest.java
+++ b/tests/tests/os/src/android/os/cts/LocaleListTest.java
@@ -20,6 +20,8 @@
 import android.os.Parcel;
 import android.test.AndroidTestCase;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Locale;
 
 public class LocaleListTest extends AndroidTestCase {
@@ -247,7 +249,7 @@
             assertEquals("ae,en,ja", LocaleList.getAdjustedDefault().toLanguageTags());
         } finally {
             // restore the original values
-            LocaleList.setDefault(originalLocaleList, originalLocaleList.indexOf(originalLocale));
+            resetDefaultLocaleList(originalLocale, originalLocaleList);
         }
     }
 
@@ -281,7 +283,7 @@
             assertEquals(locales, LocaleList.getAdjustedDefault());
         } finally {
             // restore the original values
-            LocaleList.setDefault(originalLocaleList, originalLocaleList.indexOf(originalLocale));
+            resetDefaultLocaleList(originalLocale, originalLocaleList);
         }
     }
 
@@ -297,7 +299,7 @@
             assertEquals(locales, LocaleList.getAdjustedDefault());
         } finally {
             // restore the original values
-            LocaleList.setDefault(originalLocaleList, originalLocaleList.indexOf(originalLocale));
+            resetDefaultLocaleList(originalLocale, originalLocaleList);
         }
     }
 
@@ -319,6 +321,26 @@
         LocaleList.getEmptyLocaleList().describeContents();
     }
 
+    private static void resetDefaultLocaleList(Locale topLocale, LocaleList localeList) {
+        final List<Locale> newLocales = new ArrayList<>();
+
+        if (topLocale != null) {
+            newLocales.add(topLocale);
+        }
+
+        if (localeList != null) {
+            for (int index = 0; index < localeList.size(); index++) {
+                final Locale locale = localeList.get(index);
+                if (topLocale != null && !topLocale.equals(locale)) {
+                    newLocales.add(locale);
+                }
+            }
+        }
+
+        final LocaleList result = new LocaleList(newLocales.toArray(new Locale[newLocales.size()]));
+        LocaleList.setDefault(result);
+    }
+
     private static LocaleList cloneViaParcel(final LocaleList original) {
         Parcel parcel = null;
         try {
diff --git a/tests/tests/os/src/android/os/cts/MessengerTest.java b/tests/tests/os/src/android/os/cts/MessengerTest.java
index 7612c14..9f21d9b 100644
--- a/tests/tests/os/src/android/os/cts/MessengerTest.java
+++ b/tests/tests/os/src/android/os/cts/MessengerTest.java
@@ -21,21 +21,17 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
-import android.os.IInterface;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
 import android.os.Parcel;
 import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.ShellCallback;
 import android.test.AndroidTestCase;
 
-import java.io.FileDescriptor;
-
 public class MessengerTest extends AndroidTestCase {
 
     private Messenger mMessenger;
@@ -54,48 +50,7 @@
         }
     };
 
-    private final IBinder mIBinder = new IBinder() {
-
-        public String getInterfaceDescriptor() throws RemoteException {
-            return null;
-        }
-
-        public boolean isBinderAlive() {
-            return false;
-        }
-
-        public void linkToDeath(DeathRecipient recipient, int flags) throws RemoteException {
-        }
-
-        public boolean pingBinder() {
-            return false;
-        }
-
-        public IInterface queryLocalInterface(String descriptor) {
-            return null;
-        }
-
-        public boolean transact(int code, Parcel data, Parcel reply, int flags)
-                throws RemoteException {
-            return false;
-        }
-
-        public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
-            return false;
-        }
-
-        public void dump(FileDescriptor fd, String[] args) throws RemoteException {
-        }
-
-        public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
-        }
-
-        @Override
-        public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
-                String[] args, ShellCallback shellCallback, ResultReceiver resultReceiver) {
-        }
-
-    };
+    private final IBinder mIBinder = new Binder();
 
     // Create another messenger to send msg.
     private ServiceConnection mConnection = new ServiceConnection() {
diff --git a/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java b/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java
index 38651d7..a4f956b 100644
--- a/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java
+++ b/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java
@@ -132,21 +132,6 @@
         done.get(5, TimeUnit.SECONDS);
     }
 
-    @Test
-    public void testFromData() throws IOException {
-        assertNull(ParcelFileDescriptor.fromData(null, null));
-        byte[] data = new byte[] { 0 };
-        assertFileDescriptorContent(data, ParcelFileDescriptor.fromData(data, null));
-        data = new byte[] { 0, 1, 2, 3 };
-        assertFileDescriptorContent(data, ParcelFileDescriptor.fromData(data, null));
-
-        // Check that modifying the data does not modify the data in the FD
-        data = new byte[] { 0, 1, 2, 3 };
-        ParcelFileDescriptor pfd = ParcelFileDescriptor.fromData(data, null);
-        data[1] = 42;
-        assertFileDescriptorContent(new byte[] { 0, 1, 2, 3 }, pfd);
-    }
-
     private static void assertFileDescriptorContent(byte[] expected, ParcelFileDescriptor fd)
         throws IOException {
         assertInputStreamContent(expected, new ParcelFileDescriptor.AutoCloseInputStream(fd));
@@ -166,28 +151,6 @@
     }
 
     @Test
-    public void testFromDataSkip() throws IOException {
-        byte[] data = new byte[] { 40, 41, 42, 43, 44, 45, 46 };
-        ParcelFileDescriptor pfd = ParcelFileDescriptor.fromData(data, null);
-        assertNotNull(pfd);
-        FileDescriptor fd = pfd.getFileDescriptor();
-        assertNotNull(fd);
-        assertTrue(fd.valid());
-        FileInputStream is = new FileInputStream(fd);
-        try {
-            assertEquals(1, is.skip(1));
-            assertEquals(41, is.read());
-            assertEquals(42, is.read());
-            assertEquals(2, is.skip(2));
-            assertEquals(45, is.read());
-            assertEquals(46, is.read());
-            assertEquals(-1, is.read());
-        } finally {
-            is.close();
-        }
-    }
-
-    @Test
     public void testToString() {
         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
         assertNotNull(pfd.toString());
diff --git a/tests/tests/os/src/android/os/cts/PowerManagerTest.java b/tests/tests/os/src/android/os/cts/PowerManagerTest.java
index 50a3a4f..f3c0c77 100644
--- a/tests/tests/os/src/android/os/cts/PowerManagerTest.java
+++ b/tests/tests/os/src/android/os/cts/PowerManagerTest.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.os.PowerManager;
-import android.os.SystemClock;
 import android.os.PowerManager.WakeLock;
 import android.test.AndroidTestCase;
 
@@ -43,35 +42,10 @@
         assertFalse(wl.isHeld());
 
         try {
-            pm.goToSleep(SystemClock.uptimeMillis());
-            fail("goToSleep should throw SecurityException");
-        } catch (SecurityException e) {
-            // expected
-        }
-
-        try {
-            pm.wakeUp(SystemClock.uptimeMillis());
-            fail("wakeUp should throw SecurityException");
-        } catch (SecurityException e) {
-            // expected
-        }
-
-        try {
-            pm.nap(SystemClock.uptimeMillis());
-            fail("nap should throw SecurityException");
-        } catch (SecurityException e) {
-            // expected
-        }
-
-        try {
             pm.reboot("Testing");
             fail("reboot should throw SecurityException");
         } catch (SecurityException e) {
             // expected
         }
-
-        // This method requires DEVICE_POWER but does not throw a SecurityException
-        // for historical reasons.  So this call should be a no-op.
-        pm.userActivity(SystemClock.uptimeMillis(), false);
     }
 }
diff --git a/tests/tests/os/src/android/os/cts/SecurityPatchTest.java b/tests/tests/os/src/android/os/cts/SecurityPatchTest.java
index 7afeaa9..b0742b6 100644
--- a/tests/tests/os/src/android/os/cts/SecurityPatchTest.java
+++ b/tests/tests/os/src/android/os/cts/SecurityPatchTest.java
@@ -29,14 +29,26 @@
 public class SecurityPatchTest extends InstrumentationTestCase {
 
     private static final String TAG = SecurityPatchTest.class.getSimpleName();
+    private static final String MISSING_BOOT_SECURITY_PATCH_LEVEL =
+            "ro.boot.security_patch should be defined in the device specific BoardConfig.mk on the BOARD_KERNEL_COMMANDLINE";
     private static final String SECURITY_PATCH_ERROR =
-            "ro.build.version.security_patch should be in the format \"YYYY-MM-DD\". Found \"%s\"";
-
+            "security_patch should be in the format \"YYYY-MM-DD\". Found \"%s\"";
+    private static final String SECURITY_PATCH_DATE_ERROR =
+            "security_patch should be \"%d-%02d\" or later. Found \"%s\"";
+    private static final int SECURITY_PATCH_YEAR = 2016;
+    private static final int SECURITY_PATCH_MONTH = 12;
+  
     private boolean mSkipTests = false;
+    private String mVendorSecurityPatch;
+    private String mBuildSecurityPatch;
+    private String mBootSecurityPatch;
 
     @Override
     protected void setUp() {
         mSkipTests = (ApiLevelUtil.isBefore(Build.VERSION_CODES.M));
+        mVendorSecurityPatch = SystemProperties.get("ro.vendor.build.security_patch", "");
+        mBuildSecurityPatch = Build.VERSION.SECURITY_PATCH;
+        mBootSecurityPatch = SystemProperties.get("ro.boot.security_patch", "");
     }
 
     /** Security patch string must exist in M or higher **/
@@ -45,32 +57,105 @@
             Log.w(TAG, "Skipping M+ Test.");
             return;
         }
+        String error = String.format(SECURITY_PATCH_ERROR, mBuildSecurityPatch);
+        assertTrue(error, !mBuildSecurityPatch.isEmpty());
+    }
 
-        String buildSecurityPatch = Build.VERSION.SECURITY_PATCH;
-        String error = String.format(SECURITY_PATCH_ERROR, buildSecurityPatch);
-        assertTrue(error, !buildSecurityPatch.isEmpty());
+    /** Vendor security patch string must exist in P or higher **/
+    public void testVendorSecurityPatchFound() {
+        if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.O) {
+            Log.w(TAG, "Skipping P+ Test");
+            return;
+        }
+        assertTrue(!mVendorSecurityPatch.isEmpty());
+    }
+
+    /** Boot security patch string must exist in P or higher **/
+    public void testBootSecurityPatchFound() {
+        if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.O) {
+            Log.w(TAG, "Skipping P+ Test");
+            return;
+        }
+        assertTrue(MISSING_BOOT_SECURITY_PATCH_LEVEL, !mBootSecurityPatch.isEmpty());
+    }
+
+    public void testSecurityPatchesFormat() {
+        if (mSkipTests) {
+            Log.w(TAG, "Skipping M+ Test.");
+            return;
+        }
+        String error = String.format(SECURITY_PATCH_ERROR, mBuildSecurityPatch);
+        testSecurityPatchFormat(mBuildSecurityPatch, error);
+
+        if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.O) {
+            Log.w(TAG, "Skipping P+ Test");
+            return;
+        }
+        error = String.format(SECURITY_PATCH_ERROR, mVendorSecurityPatch);
+        testSecurityPatchFormat(mVendorSecurityPatch, error);
+
+        error = String.format(SECURITY_PATCH_ERROR, mBootSecurityPatch);
+        testSecurityPatchFormat(mBootSecurityPatch, error);
     }
 
     /** Security patch should be of the form YYYY-MM-DD in M or higher */
-    public void testSecurityPatchFormat() {
+    private void testSecurityPatchFormat(String patch, String error) {
+        assertEquals(error, 10, patch.length());
+        assertTrue(error, Character.isDigit(patch.charAt(0)));
+        assertTrue(error, Character.isDigit(patch.charAt(1)));
+        assertTrue(error, Character.isDigit(patch.charAt(2)));
+        assertTrue(error, Character.isDigit(patch.charAt(3)));
+        assertEquals(error, '-', patch.charAt(4));
+        assertTrue(error, Character.isDigit(patch.charAt(5)));
+        assertTrue(error, Character.isDigit(patch.charAt(6)));
+        assertEquals(error, '-', patch.charAt(7));
+        assertTrue(error, Character.isDigit(patch.charAt(8)));
+        assertTrue(error, Character.isDigit(patch.charAt(9)));
+    }
+
+    public void testSecurityPatchDates() {
         if (mSkipTests) {
             Log.w(TAG, "Skipping M+ Test.");
             return;
         }
 
-        String buildSecurityPatch = Build.VERSION.SECURITY_PATCH;
-        String error = String.format(SECURITY_PATCH_ERROR, buildSecurityPatch);
+        String error = String.format(SECURITY_PATCH_DATE_ERROR,
+                                     SECURITY_PATCH_YEAR,
+                                     SECURITY_PATCH_MONTH,
+                                     mBuildSecurityPatch);
+        testSecurityPatchDate(mBuildSecurityPatch, error);
 
-        assertEquals(error, 10, buildSecurityPatch.length());
-        assertTrue(error, Character.isDigit(buildSecurityPatch.charAt(0)));
-        assertTrue(error, Character.isDigit(buildSecurityPatch.charAt(1)));
-        assertTrue(error, Character.isDigit(buildSecurityPatch.charAt(2)));
-        assertTrue(error, Character.isDigit(buildSecurityPatch.charAt(3)));
-        assertEquals(error, '-', buildSecurityPatch.charAt(4));
-        assertTrue(error, Character.isDigit(buildSecurityPatch.charAt(5)));
-        assertTrue(error, Character.isDigit(buildSecurityPatch.charAt(6)));
-        assertEquals(error, '-', buildSecurityPatch.charAt(7));
-        assertTrue(error, Character.isDigit(buildSecurityPatch.charAt(8)));
-        assertTrue(error, Character.isDigit(buildSecurityPatch.charAt(9)));
+        if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.O) {
+            Log.w(TAG, "Skipping P+ Test");
+            return;
+        }
+        error = String.format(SECURITY_PATCH_DATE_ERROR,
+                                     SECURITY_PATCH_YEAR,
+                                     SECURITY_PATCH_MONTH,
+                                     mVendorSecurityPatch);
+        testSecurityPatchDate(mVendorSecurityPatch, error);
+
+        error = String.format(SECURITY_PATCH_DATE_ERROR,
+                                     SECURITY_PATCH_YEAR,
+                                     SECURITY_PATCH_MONTH,
+                                     mBootSecurityPatch);
+        testSecurityPatchDate(mBootSecurityPatch, error);
+    }
+
+    /** Security patch should no older than the month this test was updated in M or higher **/
+    private void testSecurityPatchDate(String patch, String error) {
+        int declaredYear = 0;
+        int declaredMonth = 0;
+
+        try {
+            declaredYear = Integer.parseInt(patch.substring(0,4));
+            declaredMonth = Integer.parseInt(patch.substring(5,7));
+        } catch (Exception e) {
+            assertTrue(error, false);
+        }
+
+        assertTrue(error, declaredYear >= SECURITY_PATCH_YEAR);
+        assertTrue(error, (declaredYear > SECURITY_PATCH_YEAR) ||
+                          (declaredMonth >= SECURITY_PATCH_MONTH));
     }
 }
diff --git a/tests/tests/os/src/android/os/cts/StrictModeTest.java b/tests/tests/os/src/android/os/cts/StrictModeTest.java
index a316e24..02c0d04 100644
--- a/tests/tests/os/src/android/os/cts/StrictModeTest.java
+++ b/tests/tests/os/src/android/os/cts/StrictModeTest.java
@@ -34,8 +34,16 @@
 import android.os.StrictMode;
 import android.os.StrictMode.ThreadPolicy.Builder;
 import android.os.StrictMode.ViolationInfo;
+import android.os.strictmode.CleartextNetworkViolation;
 import android.os.strictmode.CustomViolation;
+import android.os.strictmode.DiskReadViolation;
+import android.os.strictmode.DiskWriteViolation;
+import android.os.strictmode.ExplicitGcViolation;
 import android.os.strictmode.FileUriExposedViolation;
+import android.os.strictmode.InstanceCountViolation;
+import android.os.strictmode.LeakedClosableViolation;
+import android.os.strictmode.NetworkViolation;
+import android.os.strictmode.NonSdkApiUsedViolation;
 import android.os.strictmode.UntaggedSocketViolation;
 import android.os.strictmode.Violation;
 import android.support.test.InstrumentationRegistry;
@@ -46,6 +54,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -105,9 +114,9 @@
         final File test = File.createTempFile("foo", "bar");
         inspectViolation(
                 test::exists,
-                violation -> {
-                    assertThat(violation.getViolationDetails()).isNull();
-                    assertThat(violation.getStackTrace()).contains("DiskReadViolation");
+                info -> {
+                    assertThat(info.getViolationDetails()).isNull();
+                    assertThat(info.getStackTrace()).contains("DiskReadViolation");
                 });
     }
 
@@ -125,7 +134,8 @@
                     assertThat(info.getStackTrace())
                             .contains("Explicit termination method 'close' not called");
                     assertThat(info.getStackTrace()).contains("leakCloseable");
-                    assertPolicy(info, StrictMode.DETECT_VM_CLOSABLE_LEAKS);
+                    assertThat(info.getViolationClass())
+                            .isAssignableTo(LeakedClosableViolation.class);
                 });
     }
 
@@ -162,7 +172,8 @@
         references.add(new LimitedClass());
         inspectViolation(
                 StrictMode::conditionallyCheckInstanceCounts,
-                info -> assertPolicy(info, StrictMode.DETECT_VM_INSTANCE_LEAKS));
+                info -> assertThat(info.getViolationClass())
+                        .isAssignableTo(InstanceCountViolation.class));
     }
 
     private static final class LimitedClass {}
@@ -182,13 +193,8 @@
                 () ->
                         ((HttpURLConnection) new URL("http://example.com/").openConnection())
                                 .getResponseCode(),
-                info -> {
-                    assertThat(info.getViolationDetails())
-                            .contains("Detected cleartext network traffic from UID");
-                    assertThat(info.getViolationDetails())
-                            .startsWith(StrictMode.CLEARTEXT_DETECTED_MSG);
-                    assertPolicy(info, StrictMode.DETECT_VM_CLEARTEXT_NETWORK);
-                });
+                info -> assertThat(info.getViolationClass())
+                        .isAssignableTo(CleartextNetworkViolation.class));
     }
 
     /** Secure connection should be ignored */
@@ -221,8 +227,8 @@
                     intent.setDataAndType(badUri, "image/jpeg");
                     getContext().startActivity(intent);
                 },
-                violation -> {
-                    assertThat(violation.getStackTrace()).contains(badUri + " exposed beyond app");
+                info -> {
+                    assertThat(info.getStackTrace()).contains(badUri + " exposed beyond app");
                 });
 
         final Uri goodUri = Uri.parse("content://com.example/foobar");
@@ -251,8 +257,8 @@
                     intent.setDataAndType(uri, "image/jpeg");
                     getContext().startActivity(intent);
                 },
-                violation ->
-                        assertThat(violation.getStackTrace())
+                info ->
+                        assertThat(info.getStackTrace())
                                 .contains(uri + " exposed beyond app"));
 
         assertNoViolation(
@@ -279,9 +285,8 @@
                 () ->
                         ((HttpURLConnection) new URL("http://example.com/").openConnection())
                                 .getResponseCode(),
-                violation ->
-                        assertThat(violation.getStackTrace())
-                                .contains(UntaggedSocketViolation.MESSAGE));
+                info -> assertThat(info.getViolationClass())
+                        .isAssignableTo(UntaggedSocketViolation.class));
 
         assertNoViolation(
                 () -> {
@@ -321,9 +326,8 @@
                         socket.getOutputStream().close();
                     }
                 },
-                violation ->
-                        assertThat(violation.getStackTrace())
-                                .contains(UntaggedSocketViolation.MESSAGE));
+                info -> assertThat(info.getViolationClass())
+                        .isAssignableTo(UntaggedSocketViolation.class));
     }
 
     private static final int PERMISSION_USER_ONLY = 0600;
@@ -341,13 +345,13 @@
                 new StrictMode.ThreadPolicy.Builder().detectDiskReads().penaltyLog().build());
         inspectViolation(
                 test::exists,
-                violation -> {
-                    assertThat(violation.getViolationDetails()).isNull();
-                    assertThat(violation.getStackTrace()).contains("DiskReadViolation");
+                info -> {
+                    assertThat(info.getViolationDetails()).isNull();
+                    assertThat(info.getStackTrace()).contains("DiskReadViolation");
                 });
 
-        Consumer<ViolationInfo> assertDiskReadPolicy =
-                violation -> assertPolicy(violation, StrictMode.DETECT_DISK_READ);
+        Consumer<ViolationInfo> assertDiskReadPolicy = info -> assertThat(
+                info.getViolationClass()).isAssignableTo(DiskReadViolation.class);
         inspectViolation(test::exists, assertDiskReadPolicy);
         inspectViolation(test::length, assertDiskReadPolicy);
         inspectViolation(dir::list, assertDiskReadPolicy);
@@ -373,13 +377,13 @@
 
         inspectViolation(
                 file::createNewFile,
-                violation -> {
-                    assertThat(violation.getViolationDetails()).isNull();
-                    assertThat(violation.getStackTrace()).contains("DiskWriteViolation");
+                info -> {
+                    assertThat(info.getViolationDetails()).isNull();
+                    assertThat(info.getStackTrace()).contains("DiskWriteViolation");
                 });
 
-        Consumer<ViolationInfo> assertDiskWritePolicy =
-                violation -> assertPolicy(violation, StrictMode.DETECT_DISK_WRITE);
+        Consumer<ViolationInfo> assertDiskWritePolicy = info -> assertThat(
+                info.getViolationClass()).isAssignableTo(DiskWriteViolation.class);
 
         inspectViolation(() -> File.createTempFile("foo", "bar"), assertDiskWritePolicy);
         inspectViolation(() -> new FileOutputStream(file), assertDiskWritePolicy);
@@ -412,12 +416,14 @@
                         socket.getOutputStream().close();
                     }
                 },
-                violation -> assertPolicy(violation, StrictMode.DETECT_NETWORK));
+                info -> assertThat(info.getViolationClass())
+                        .isAssignableTo(NetworkViolation.class));
         inspectViolation(
                 () ->
                         ((HttpURLConnection) new URL("http://example.com/").openConnection())
                                 .getResponseCode(),
-                violation -> assertPolicy(violation, StrictMode.DETECT_NETWORK));
+                info -> assertThat(info.getViolationClass())
+                        .isAssignableTo(NetworkViolation.class));
     }
 
     @Test
@@ -427,7 +433,8 @@
 
         inspectViolation(
                 () -> { Runtime.getRuntime().gc(); },
-                violation -> assertPolicy(violation, StrictMode.DETECT_EXPLICIT_GC));
+                info -> assertThat(info.getViolationClass())
+                        .isAssignableTo(ExplicitGcViolation.class));
     }
 
     @Test
@@ -441,18 +448,19 @@
                     try {
                         inspectViolation(
                                 () -> service.performDiskWrite(),
-                                (violation) -> {
-                                    assertPolicy(violation, StrictMode.DETECT_DISK_WRITE);
-                                    assertThat(violation.getViolationDetails())
+                                (info) -> {
+                                    assertThat(info.getViolationClass())
+                                            .isAssignableTo(DiskWriteViolation.class);
+                                    assertThat(info.getViolationDetails())
                                             .isNull(); // Disk write has no message.
-                                    assertThat(violation.getStackTrace())
+                                    assertThat(info.getStackTrace())
                                             .contains("DiskWriteViolation");
-                                    assertThat(violation.getStackTrace())
+                                    assertThat(info.getStackTrace())
                                             .contains(
                                                     "at android.os.StrictMode$AndroidBlockGuardPolicy.onWriteToDisk");
-                                    assertThat(violation.getStackTrace())
+                                    assertThat(info.getStackTrace())
                                             .contains("# via Binder call with stack:");
-                                    assertThat(violation.getStackTrace())
+                                    assertThat(info.getStackTrace())
                                             .contains(
                                                     "at android.os.cts.ISecondary$Stub$Proxy.performDiskWrite");
                                 });
@@ -479,11 +487,12 @@
                   }
                 }
             },
-            violation -> {
-                assertThat(violation).isNotNull();
-                assertPolicy(violation, StrictMode.DETECT_VM_NON_SDK_API_USAGE);
-                assertThat(violation.getViolationDetails()).contains(methodName);
-                assertThat(violation.getStackTrace()).contains("checkNonSdkApiUsageViolation");
+            info -> {
+                assertThat(info).isNotNull();
+                assertThat(info.getViolationClass())
+                        .isAssignableTo(NonSdkApiUsedViolation.class);
+                assertThat(info.getViolationDetails()).contains(methodName);
+                assertThat(info.getStackTrace()).contains("checkNonSdkApiUsageViolation");
             }
         );
     }
@@ -580,16 +589,12 @@
     }
 
     private static void assertViolation(String expected, ThrowingRunnable r) throws Exception {
-        inspectViolation(r, violation -> assertThat(violation.getStackTrace()).contains(expected));
+        inspectViolation(r, info -> assertThat(info.getStackTrace()).contains(expected));
     }
 
     private static void assertNoViolation(ThrowingRunnable r) throws Exception {
         inspectViolation(
-                r, violation -> assertWithMessage("Unexpected violation").that(violation).isNull());
-    }
-
-    private void assertPolicy(ViolationInfo info, int policy) {
-        assertWithMessage("Policy bit incorrect").that(info.getViolationBit()).isEqualTo(policy);
+                r, info -> assertWithMessage("Unexpected violation").that(info).isNull());
     }
 
     private static void inspectViolation(
diff --git a/tests/tests/os/src/android/os/cts/VibrationEffectTest.java b/tests/tests/os/src/android/os/cts/VibrationEffectTest.java
index 50237f0..babb0f0 100644
--- a/tests/tests/os/src/android/os/cts/VibrationEffectTest.java
+++ b/tests/tests/os/src/android/os/cts/VibrationEffectTest.java
@@ -16,25 +16,21 @@
 
 package android.os.cts;
 
-import android.content.Context;
-import android.media.AudioAttributes;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.fail;
+
 import android.os.Parcel;
 import android.os.VibrationEffect;
-import android.os.Vibrator;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.util.Log;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.util.Arrays;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.fail;
-
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class VibrationEffectTest {
@@ -51,13 +47,22 @@
             VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, -1);
     private static final VibrationEffect TEST_WAVEFORM_NO_AMPLITUDES =
             VibrationEffect.createWaveform(TEST_TIMINGS, -1);
+    private static final VibrationEffect TEST_PREBAKED =
+            VibrationEffect.get(VibrationEffect.EFFECT_CLICK, true);
 
 
     @Test
     public void testCreateOneShot() {
-        VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE);
-        VibrationEffect.createOneShot(1, 1);
-        VibrationEffect.createOneShot(1000, 255);
+        VibrationEffect e = VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE);
+        assertEquals(100, e.getDuration());
+        assertEquals(VibrationEffect.DEFAULT_AMPLITUDE,
+                ((VibrationEffect.OneShot)e).getAmplitude());
+        e = VibrationEffect.createOneShot(1, 1);
+        assertEquals(1, e.getDuration());
+        assertEquals(1, ((VibrationEffect.OneShot)e).getAmplitude());
+        e = VibrationEffect.createOneShot(1000, 255);
+        assertEquals(1000, e.getDuration());
+        assertEquals(255, ((VibrationEffect.OneShot)e).getAmplitude());
     }
 
     @Test
@@ -76,6 +81,11 @@
         } catch (IllegalArgumentException expected) { }
 
         try {
+            VibrationEffect.createOneShot(TEST_TIMING, 0);
+            fail("Invalid amplitude, should throw IllegalArgumentException");
+        } catch (IllegalArgumentException expected) { }
+
+        try {
             VibrationEffect.createOneShot(TEST_TIMING, 256);
             fail("Invalid amplitude, should throw IllegalArgumentException");
         } catch (IllegalArgumentException expected) { }
@@ -113,10 +123,42 @@
     }
 
     @Test
+    public void testCreatePrebaked() {
+        int[] ids = { VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_DOUBLE_CLICK,
+                VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_THUD,
+                VibrationEffect.EFFECT_POP, VibrationEffect.EFFECT_HEAVY_CLICK };
+        boolean[] fallbacks = { false, true };
+        for (int id : ids) {
+            for (boolean fallback : fallbacks) {
+                VibrationEffect.Prebaked effect = (VibrationEffect.Prebaked)
+                        VibrationEffect.get(id, fallback);
+                assertEquals(id, effect.getId());
+                assertEquals(fallback, effect.shouldFallback());
+                assertEquals(-1, effect.getDuration());
+            }
+        }
+    }
+
+    @Test
     public void testCreateWaveform() {
-        VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, -1);
-        VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, 0);
-        VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, TEST_AMPLITUDES.length - 1);
+        VibrationEffect.Waveform effect = (VibrationEffect.Waveform)
+                VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, -1);
+        assertArrayEquals(TEST_TIMINGS, effect.getTimings());
+        assertArrayEquals(TEST_AMPLITUDES, effect.getAmplitudes());
+        assertEquals(-1, effect.getRepeatIndex());
+        assertEquals(400, effect.getDuration());
+        effect = (VibrationEffect.Waveform)
+            VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, 0);
+        assertArrayEquals(TEST_TIMINGS, effect.getTimings());
+        assertArrayEquals(TEST_AMPLITUDES, effect.getAmplitudes());
+        assertEquals(0, effect.getRepeatIndex());
+        assertEquals(Long.MAX_VALUE, effect.getDuration());
+        effect = (VibrationEffect.Waveform)VibrationEffect.createWaveform(TEST_TIMINGS,
+                TEST_AMPLITUDES, TEST_AMPLITUDES.length - 1);
+        assertArrayEquals(TEST_TIMINGS, effect.getTimings());
+        assertArrayEquals(TEST_AMPLITUDES, effect.getAmplitudes());
+        assertEquals(TEST_AMPLITUDES.length - 1, effect.getRepeatIndex());
+        assertEquals(Long.MAX_VALUE, effect.getDuration());
     }
 
     @Test
@@ -278,17 +320,76 @@
     }
 
     @Test
-    public void testParceling() {
+    public void testParcelingOneShot() {
         Parcel p = Parcel.obtain();
         TEST_ONE_SHOT.writeToParcel(p, 0);
         p.setDataPosition(0);
         VibrationEffect parceledEffect = VibrationEffect.CREATOR.createFromParcel(p);
         assertEquals(TEST_ONE_SHOT, parceledEffect);
+    }
 
-        p.setDataPosition(0);
+    @Test
+    public void testParcelingWaveForm() {
+        Parcel p = Parcel.obtain();
         TEST_WAVEFORM.writeToParcel(p, 0);
         p.setDataPosition(0);
-        parceledEffect = VibrationEffect.CREATOR.createFromParcel(p);
+        VibrationEffect parceledEffect = VibrationEffect.CREATOR.createFromParcel(p);
         assertEquals(TEST_WAVEFORM, parceledEffect);
     }
+
+    @Test
+    public void testParcelingPrebaked() {
+        Parcel p = Parcel.obtain();
+        TEST_PREBAKED.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        VibrationEffect parceledEffect = VibrationEffect.CREATOR.createFromParcel(p);
+        assertEquals(TEST_PREBAKED, parceledEffect);
+    }
+
+    @Test
+    public void testDescribeContents() {
+        TEST_ONE_SHOT.describeContents();
+        TEST_WAVEFORM.describeContents();
+        TEST_WAVEFORM_NO_AMPLITUDES.describeContents();
+        TEST_PREBAKED.describeContents();
+    }
+
+    @Test
+    public void testSetStrength() {
+        VibrationEffect.Prebaked effect = (VibrationEffect.Prebaked)VibrationEffect.get(
+                VibrationEffect.EFFECT_CLICK, true);
+        int[] strengths = {
+                VibrationEffect.EFFECT_STRENGTH_LIGHT,
+                VibrationEffect.EFFECT_STRENGTH_MEDIUM,
+                VibrationEffect.EFFECT_STRENGTH_STRONG
+        };
+        for (int strength : strengths) {
+            effect.setEffectStrength(strength);
+            assertEquals(strength, effect.getEffectStrength());
+        }
+    }
+
+    @Test
+    public void testSetStrengthInvalid() {
+        VibrationEffect.Prebaked effect = (VibrationEffect.Prebaked)VibrationEffect.get(
+                VibrationEffect.EFFECT_CLICK, true);
+        try {
+            effect.setEffectStrength(239017);
+            fail("Illegal strength, should throw IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {}
+    }
+
+    @Test
+    public void testPrebakedEquals() {
+        VibrationEffect otherEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK, true);
+        assertEquals(TEST_PREBAKED, otherEffect);
+        assertEquals(TEST_PREBAKED.hashCode(), otherEffect.hashCode());
+    }
+
+    @Test
+    public void testToString() {
+        TEST_ONE_SHOT.toString();
+        TEST_WAVEFORM.toString();
+        TEST_PREBAKED.toString();
+    }
 }
diff --git a/tests/tests/os/src/android/os/cts/WorkSourceTest.java b/tests/tests/os/src/android/os/cts/WorkSourceTest.java
index e7df7be..9b6fa80 100644
--- a/tests/tests/os/src/android/os/cts/WorkSourceTest.java
+++ b/tests/tests/os/src/android/os/cts/WorkSourceTest.java
@@ -15,45 +15,17 @@
  */
 package android.os.cts;
 
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import java.util.Arrays;
-
 import android.os.WorkSource;
 import android.test.AndroidTestCase;
 
+import java.util.Arrays;
+
 public class WorkSourceTest extends AndroidTestCase {
-    private Constructor<WorkSource> mConstructWS;
-    private Object[] mConstructWSArgs = new Object[1];
-    private Method mAddUid;
-    private Object[] mAddUidArgs = new Object[1];
-    private Method mAddUidName;
-    private Object[] mAddUidNameArgs = new Object[2];
-    private Method mAddReturningNewbs;
-    private Object[] mAddReturningNewbsArgs = new Object[1];
-    private Method mSetReturningDiffs;
-    private Object[] mSetReturningDiffsArgs = new Object[1];
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mConstructWS = WorkSource.class.getConstructor(new Class[] { int.class });
-        mAddUid = WorkSource.class.getMethod("add", new Class[] { int.class });
-        mAddUidName = WorkSource.class.getMethod("add", new Class[] { int.class, String.class });
-        mAddReturningNewbs = WorkSource.class.getMethod("addReturningNewbs", new Class[] { WorkSource.class });
-        mSetReturningDiffs = WorkSource.class.getMethod("setReturningDiffs", new Class[] { WorkSource.class });
+    private WorkSource wsNew(int uid) {
+        return new WorkSource(uid);
     }
 
-    private WorkSource wsNew(int uid) throws IllegalArgumentException,
-            InstantiationException, IllegalAccessException, InvocationTargetException {
-        mConstructWSArgs[0] = uid;
-        return mConstructWS.newInstance(mConstructWSArgs);
-    }
-
-    private WorkSource wsNew(int[] uids) throws IllegalArgumentException,
-            InstantiationException, IllegalAccessException, InvocationTargetException {
+    private WorkSource wsNew(int[] uids) {
         WorkSource ws = new WorkSource();
         for (int i=0; i<uids.length; i++) {
             wsAdd(ws, uids[i]);
@@ -62,8 +34,7 @@
         return ws;
     }
 
-    private WorkSource wsNew(int[] uids, String[] names) throws IllegalArgumentException,
-            InstantiationException, IllegalAccessException, InvocationTargetException {
+    private WorkSource wsNew(int[] uids, String[] names) {
         WorkSource ws = new WorkSource();
         for (int i=0; i<uids.length; i++) {
             wsAdd(ws, uids[i], names[i]);
@@ -72,29 +43,20 @@
         return ws;
     }
 
-    private boolean wsAdd(WorkSource ws, int uid) throws IllegalArgumentException,
-            InstantiationException, IllegalAccessException, InvocationTargetException {
-        mAddUidArgs[0] = uid;
-        return (Boolean)mAddUid.invoke(ws, mAddUidArgs);
+    private boolean wsAdd(WorkSource ws, int uid) {
+        return ws.add(uid);
     }
 
-    private boolean wsAdd(WorkSource ws, int uid, String name) throws IllegalArgumentException,
-            InstantiationException, IllegalAccessException, InvocationTargetException {
-        mAddUidNameArgs[0] = uid;
-        mAddUidNameArgs[1] = name;
-        return (Boolean)mAddUidName.invoke(ws, mAddUidNameArgs);
+    private boolean wsAdd(WorkSource ws, int uid, String name) {
+        return ws.add(uid, name);
     }
 
-    private WorkSource wsAddReturningNewbs(WorkSource ws, WorkSource other) throws IllegalArgumentException,
-            InstantiationException, IllegalAccessException, InvocationTargetException {
-        mAddReturningNewbsArgs[0] = other;
-        return (WorkSource)mAddReturningNewbs.invoke(ws, mAddReturningNewbsArgs);
+    private WorkSource wsAddReturningNewbs(WorkSource ws, WorkSource other) {
+        return ws.addReturningNewbs(other);
     }
 
-    private WorkSource[] wsSetReturningDiffs(WorkSource ws, WorkSource other) throws IllegalArgumentException,
-            InstantiationException, IllegalAccessException, InvocationTargetException {
-        mSetReturningDiffsArgs[0] = other;
-        return (WorkSource[])mSetReturningDiffs.invoke(ws, mSetReturningDiffsArgs);
+    private WorkSource[] wsSetReturningDiffs(WorkSource ws, WorkSource other) {
+        return ws.setReturningDiffs(other);
     }
 
     private void printArrays(StringBuilder sb, int[] uids, String[] names) {
diff --git a/tests/tests/os/src/android/os/health/cts/SystemHealthManagerTest.java b/tests/tests/os/src/android/os/health/cts/SystemHealthManagerTest.java
index 1ce3086..4abd939 100644
--- a/tests/tests/os/src/android/os/health/cts/SystemHealthManagerTest.java
+++ b/tests/tests/os/src/android/os/health/cts/SystemHealthManagerTest.java
@@ -17,12 +17,11 @@
 package android.os.health.cts;
 
 import android.content.Context;
-import android.os.Parcel;
 import android.os.Process;
-import android.os.health.SystemHealthManager;
 import android.os.health.HealthStats;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.os.health.SystemHealthManager;
 import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
 
 import junit.framework.Assert;
 
@@ -36,7 +35,7 @@
     @SmallTest
     public void testTakeMyUidSnapshot() throws Exception {
         final Context context = getInstrumentation().getTargetContext();
-        final SystemHealthManager healthy = SystemHealthManager.from(context);
+        final SystemHealthManager healthy = context.getSystemService(SystemHealthManager.class);
 
         Assert.assertNotNull(healthy.takeMyUidSnapshot());
     }
@@ -47,7 +46,7 @@
     @SmallTest
     public void testTakeUidSnapshotWithMe() throws Exception {
         final Context context = getInstrumentation().getTargetContext();
-        final SystemHealthManager healthy = SystemHealthManager.from(context);
+        final SystemHealthManager healthy = context.getSystemService(SystemHealthManager.class);
 
         Assert.assertNotNull(healthy.takeUidSnapshot(Process.myUid()));
     }
@@ -58,7 +57,7 @@
     @SmallTest
     public void testTakeMyUidSnapshotWithSystem() throws Exception {
         final Context context = getInstrumentation().getTargetContext();
-        final SystemHealthManager healthy = SystemHealthManager.from(context);
+        final SystemHealthManager healthy = context.getSystemService(SystemHealthManager.class);
 
         boolean threw = false;
         try {
@@ -76,7 +75,7 @@
     @SmallTest
     public void testTakeUidSnapshotsWithEmptyArray() throws Exception {
         final Context context = getInstrumentation().getTargetContext();
-        final SystemHealthManager healthy = SystemHealthManager.from(context);
+        final SystemHealthManager healthy = context.getSystemService(SystemHealthManager.class);
 
         final HealthStats[] result = healthy.takeUidSnapshots(new int[0]);
         Assert.assertEquals(0, result.length);
@@ -88,7 +87,7 @@
     @SmallTest
     public void testTakeUidSnapshotsWithMe() throws Exception {
         final Context context = getInstrumentation().getTargetContext();
-        final SystemHealthManager healthy = SystemHealthManager.from(context);
+        final SystemHealthManager healthy = context.getSystemService(SystemHealthManager.class);
 
         final HealthStats[] result = healthy.takeUidSnapshots(new int[] {
                     Process.myUid(),
@@ -105,7 +104,7 @@
     @SmallTest
     public void testTakeMyUidSnapshotsWithSystem() throws Exception {
         final Context context = getInstrumentation().getTargetContext();
-        final SystemHealthManager healthy = SystemHealthManager.from(context);
+        final SystemHealthManager healthy = context.getSystemService(SystemHealthManager.class);
 
         boolean threw = false;
         try {
diff --git a/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java b/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
index d7f0853..4fa9ddc 100644
--- a/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
+++ b/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
@@ -16,17 +16,16 @@
 
 package android.os.storage.cts;
 
-import android.os.cts.R;
-
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.Looper;
-import android.os.ProxyFileDescriptorCallback;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
+import android.os.ProxyFileDescriptorCallback;
+import android.os.cts.R;
 import android.os.storage.OnObbStateChangeListener;
 import android.os.storage.StorageManager;
 import android.os.storage.StorageVolume;
@@ -39,24 +38,22 @@
 
 import com.android.compatibility.common.util.FileUtils;
 
-import libcore.io.Streams;
+import junit.framework.AssertionFailedError;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.InterruptedIOException;
 import java.io.SyncFailedException;
-import java.io.IOException;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.SynchronousQueue;
-import junit.framework.AssertionFailedError;
 
 public class StorageManagerTest extends AndroidTestCase {
 
@@ -236,7 +233,7 @@
             }
         }
         assertNotNull("No primary volume on  " + volumes, primary);
-        assertStorageVolumesEquals(primary, mStorageManager.getPrimaryVolume());
+        assertStorageVolumesEquals(primary, mStorageManager.getPrimaryStorageVolume());
     }
 
     public void testGetStorageVolume() throws Exception {
@@ -706,7 +703,7 @@
     }
 
     private static void assertFileContains(File file, String contents) throws IOException {
-        byte[] actual = Streams.readFully(new FileInputStream(file));
+        byte[] actual = readFully(new FileInputStream(file));
         byte[] expected = contents.getBytes("UTF-8");
         assertEquals("unexpected size", expected.length, actual.length);
         for (int i = 0; i < expected.length; i++) {
@@ -860,4 +857,24 @@
             assertFalse("OBB should not be mounted", mStorageManager.isObbMounted(file.getPath()));
         }
     }
+
+    public static byte[] readFully(InputStream in) throws IOException {
+        // Shamelessly borrowed from libcore.io.Streams
+        try {
+            return readFullyNoClose(in);
+        } finally {
+            in.close();
+        }
+    }
+
+    public static byte[] readFullyNoClose(InputStream in) throws IOException {
+        // Shamelessly borrowed from libcore.io.Streams
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+        byte[] buffer = new byte[1024];
+        int count;
+        while ((count = in.read(buffer)) != -1) {
+            bytes.write(buffer, 0, count);
+        }
+        return bytes.toByteArray();
+    }
 }
diff --git a/tests/tests/packageinstaller/emptytestapp/Android.mk b/tests/tests/packageinstaller/emptytestapp/Android.mk
index 7574d1f..4763e01 100644
--- a/tests/tests/packageinstaller/emptytestapp/Android.mk
+++ b/tests/tests/packageinstaller/emptytestapp/Android.mk
@@ -23,6 +23,7 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 23
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := arcts cts vts general-tests
diff --git a/tests/tests/packageinstaller/externalsources/Android.bp b/tests/tests/packageinstaller/externalsources/Android.bp
new file mode 100644
index 0000000..3b0598b
--- /dev/null
+++ b/tests/tests/packageinstaller/externalsources/Android.bp
@@ -0,0 +1,27 @@
+// 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: "CtsExternalSourcesTestCases-lib",
+    sdk_version: "test_current",
+
+    srcs: ["src/**/*.kt"],
+
+    static_libs: [
+        "junit",
+        "android-support-test",
+        "platform-test-annotations",
+        "compatibility-device-util",
+    ]
+}
\ No newline at end of file
diff --git a/tests/tests/packageinstaller/externalsources/Android.mk b/tests/tests/packageinstaller/externalsources/Android.mk
index b7346b5..d8c062d 100755
--- a/tests/tests/packageinstaller/externalsources/Android.mk
+++ b/tests/tests/packageinstaller/externalsources/Android.mk
@@ -22,12 +22,7 @@
 
 LOCAL_PACKAGE_NAME := CtsExternalSourcesTestCases
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES := ub-uiautomator \
-                               android-support-test \
-                               androidx.legacy_legacy-support-v4 \
-                               compatibility-device-util
+LOCAL_STATIC_JAVA_LIBRARIES := CtsExternalSourcesTestCases-lib
 
 LOCAL_SDK_VERSION := current
 
diff --git a/tests/tests/packageinstaller/externalsources/AndroidTest.xml b/tests/tests/packageinstaller/externalsources/AndroidTest.xml
index a39baab..eea8e10 100644
--- a/tests/tests/packageinstaller/externalsources/AndroidTest.xml
+++ b/tests/tests/packageinstaller/externalsources/AndroidTest.xml
@@ -17,6 +17,7 @@
 <configuration description="Config for CTS External Sources 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="instant_app" />
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesInstantAppsTest.java b/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesInstantAppsTest.java
deleted file mode 100644
index 7aa6a57..0000000
--- a/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesInstantAppsTest.java
+++ /dev/null
@@ -1,82 +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.externalsources.cts;
-
-import static org.junit.Assert.assertFalse;
-
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.platform.test.annotations.AppModeInstant;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import com.android.compatibility.common.util.AppOpsUtils;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.IOException;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@AppModeInstant
-public class ExternalSourcesInstantAppsTest {
-
-    private Context mContext;
-    private PackageManager mPm;
-    private String mPackageName;
-
-    @Before
-    public void setUp() throws Exception {
-        mContext = InstrumentationRegistry.getTargetContext();
-        mPm = mContext.getPackageManager();
-        mPackageName = mContext.getPackageName();
-    }
-
-    private void setAppOpsMode(int mode) throws IOException {
-        AppOpsUtils.setOpMode(mPackageName, "REQUEST_INSTALL_PACKAGES", mode);
-    }
-
-    @Test
-    public void blockedSourceTest() throws Exception {
-        setAppOpsMode(AppOpsManager.MODE_ERRORED);
-        final boolean isTrusted = mPm.canRequestPackageInstalls();
-        assertFalse("Instant app " + mPackageName + " allowed to install packages", isTrusted);
-    }
-
-    @Test
-    public void allowedSourceTest() throws Exception {
-        setAppOpsMode(AppOpsManager.MODE_ALLOWED);
-        final boolean isTrusted = mPm.canRequestPackageInstalls();
-        assertFalse("Instant app " + mPackageName + " allowed to install packages", isTrusted);
-    }
-
-    @Test
-    public void defaultSourceTest() throws Exception {
-        setAppOpsMode(AppOpsManager.MODE_DEFAULT);
-        final boolean isTrusted = mPm.canRequestPackageInstalls();
-        assertFalse("Instant app " + mPackageName + " allowed to install packages", isTrusted);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        setAppOpsMode(AppOpsManager.MODE_DEFAULT);
-    }
-}
diff --git a/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesInstantAppsTest.kt b/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesInstantAppsTest.kt
new file mode 100644
index 0000000..67bed56
--- /dev/null
+++ b/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesInstantAppsTest.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.externalsources.cts
+
+import android.app.AppOpsManager.*
+import android.platform.test.annotations.AppModeInstant
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SmallTest
+import android.support.test.runner.AndroidJUnit4
+import com.android.compatibility.common.util.AppOpsUtils
+import org.junit.After
+import org.junit.Assert.assertFalse
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@AppModeInstant
+class ExternalSourcesInstantAppsTest {
+    private val pm = InstrumentationRegistry.getTargetContext().packageManager
+    private val packageName = InstrumentationRegistry.getTargetContext().packageName
+
+    private fun setAppOpsMode(mode: Int) {
+        AppOpsUtils.setOpMode(packageName, "REQUEST_INSTALL_PACKAGES", mode)
+    }
+
+    @Test
+    fun blockedSourceTest() {
+        setAppOpsMode(MODE_ERRORED)
+        assertFalse("Instant app $packageName allowed to install packages",
+                pm.canRequestPackageInstalls())
+    }
+
+    @Test
+    fun allowedSourceTest() {
+        setAppOpsMode(MODE_ALLOWED)
+        assertFalse("Instant app $packageName allowed to install packages",
+                pm.canRequestPackageInstalls())
+    }
+
+    @Test
+    fun defaultSourceTest() {
+        setAppOpsMode(MODE_DEFAULT)
+        assertFalse("Instant app $packageName allowed to install packages",
+                pm.canRequestPackageInstalls())
+    }
+
+    @After
+    fun resetAppOpsMode() {
+        setAppOpsMode(MODE_DEFAULT)
+    }
+}
diff --git a/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesTest.java b/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesTest.java
deleted file mode 100644
index a29264b..0000000
--- a/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesTest.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.packageinstaller.externalsources.cts;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.platform.test.annotations.AppModeFull;
-import android.provider.Settings;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.uiautomator.UiDevice;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.IOException;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@AppModeFull
-public class ExternalSourcesTest {
-
-    private Context mContext;
-    private PackageManager mPm;
-    private String mPackageName;
-    private UiDevice mUiDevice;
-
-    @Before
-    public void setUp() throws Exception {
-        mContext = InstrumentationRegistry.getTargetContext();
-        mPm = mContext.getPackageManager();
-        mPackageName = mContext.getPackageName();
-        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-    }
-
-    private void setAppOpsMode(String mode) throws IOException {
-        final StringBuilder commandBuilder = new StringBuilder("appops set");
-        commandBuilder.append(" " + mPackageName);
-        commandBuilder.append(" REQUEST_INSTALL_PACKAGES");
-        commandBuilder.append(" " + mode);
-        mUiDevice.executeShellCommand(commandBuilder.toString());
-    }
-
-    @Test
-    public void blockedSourceTest() throws Exception {
-        setAppOpsMode("deny");
-        final boolean isTrusted = mPm.canRequestPackageInstalls();
-        Assert.assertFalse("Package " + mPackageName
-                + " allowed to install packages after setting app op to errored", isTrusted);
-    }
-
-    @Test
-    public void allowedSourceTest() throws Exception {
-        setAppOpsMode("allow");
-        final boolean isTrusted = mPm.canRequestPackageInstalls();
-        Assert.assertTrue("Package " + mPackageName
-                + " blocked from installing packages after setting app op to allowed", isTrusted);
-    }
-
-    @Test
-    public void defaultSourceTest() throws Exception {
-        setAppOpsMode("default");
-        final boolean isTrusted = mPm.canRequestPackageInstalls();
-        Assert.assertFalse("Package " + mPackageName
-                + " with default app ops state allowed to install packages", isTrusted);
-    }
-
-    @Test
-    public void testManageUnknownSourcesExists() {
-        Intent manageUnknownSources = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
-        ResolveInfo info = mPm.resolveActivity(manageUnknownSources, 0);
-        Assert.assertNotNull("No activity found for " + manageUnknownSources.getAction(), info);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        setAppOpsMode("default");
-    }
-}
diff --git a/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesTest.kt b/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesTest.kt
new file mode 100644
index 0000000..5edfa38
--- /dev/null
+++ b/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesTest.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.packageinstaller.externalsources.cts
+
+import android.app.AppOpsManager.*
+import android.content.Intent
+import android.platform.test.annotations.AppModeFull
+import android.provider.Settings
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SmallTest
+import android.support.test.runner.AndroidJUnit4
+import com.android.compatibility.common.util.AppOpsUtils
+import org.junit.After
+import org.junit.Assert.*
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@AppModeFull
+class ExternalSourcesTest {
+    private val pm = InstrumentationRegistry.getTargetContext().packageManager
+    private val packageName = InstrumentationRegistry.getTargetContext().packageName
+
+    private fun setAppOpsMode(mode: Int) {
+        AppOpsUtils.setOpMode(packageName, "REQUEST_INSTALL_PACKAGES", mode)
+    }
+
+    @Test
+    fun blockedSourceTest() {
+        setAppOpsMode(MODE_ERRORED)
+        assertFalse("Package $packageName allowed to install packages after setting app op to "
+                + "errored", pm.canRequestPackageInstalls())
+    }
+
+    @Test
+    fun allowedSourceTest() {
+        setAppOpsMode(MODE_ALLOWED)
+        assertTrue("Package $packageName blocked from installing packages after setting app op to "
+                + "allowed", pm.canRequestPackageInstalls())
+    }
+
+    @Test
+    fun defaultSourceTest() {
+        setAppOpsMode(MODE_DEFAULT)
+        assertFalse("Package $packageName with default app ops state allowed to install packages",
+                pm.canRequestPackageInstalls())
+    }
+
+    @Test
+    fun testManageUnknownSourcesExists() {
+        val manageUnknownSources = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES)
+        assertNotNull("No activity found for ${manageUnknownSources.action}",
+                pm.resolveActivity(manageUnknownSources, 0))
+    }
+
+    @After
+    fun resetAppOpsMode() {
+        setAppOpsMode(MODE_DEFAULT)
+    }
+}
diff --git a/tests/tests/permission/Android.mk b/tests/tests/permission/Android.mk
index 2075137..0172716 100644
--- a/tests/tests/permission/Android.mk
+++ b/tests/tests/permission/Android.mk
@@ -27,8 +27,6 @@
 # Include both the 32 and 64 bit versions
 LOCAL_MULTILIB := both
 
-LOCAL_JAVA_LIBRARIES := telephony-common
-
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
     guava \
@@ -41,9 +39,8 @@
 
 LOCAL_PACKAGE_NAME := CtsPermissionTestCases
 
-# uncomment when b/13249777 is fixed
-#LOCAL_SDK_VERSION := current
-LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_SDK_VERSION := test_current
+
 LOCAL_JAVA_LIBRARIES += android.test.runner.stubs
 LOCAL_JAVA_LIBRARIES += android.test.base.stubs
 
diff --git a/tests/tests/permission/AndroidTest.xml b/tests/tests/permission/AndroidTest.xml
index c516a60..8a7a6b9 100644
--- a/tests/tests/permission/AndroidTest.xml
+++ b/tests/tests/permission/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS Permission 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="instant_app" />
 
     <!-- Install main test suite apk -->
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
@@ -33,16 +34,21 @@
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="push" value="CtsAppThatRequestsPermissionAandB.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsPermissionAandB.apk" />
         <option name="push" value="CtsAppThatRequestsPermissionAandC.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsPermissionAandC.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" />
     </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.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" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.permission.cts" />
         <option name="runtime-hint" value="13m" />
-        <option name="hidden-api-checks" value="false" />
     </test>
 </configuration>
diff --git a/tests/tests/permission/jni/Android.mk b/tests/tests/permission/jni/Android.mk
index bfa8b67..ef1b903 100644
--- a/tests/tests/permission/jni/Android.mk
+++ b/tests/tests/permission/jni/Android.mk
@@ -17,6 +17,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := libctspermission_jni
+LOCAL_SDK_VERSION := current
 
 # Don't include this package in any configuration by default.
 LOCAL_MODULE_TAGS := optional
@@ -28,7 +29,7 @@
 LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) 
 
 LOCAL_SHARED_LIBRARIES := libnativehelper_compat_libc++ liblog
-LOCAL_CXX_STL := libc++_static
+LOCAL_NDK_STL_VARIANT := c++_static
 
 LOCAL_CFLAGS := -Wno-unused-parameter -Wall -Werror
 
diff --git a/tests/tests/permission/src/android/permission/cts/AppOpsTest.java b/tests/tests/permission/src/android/permission/cts/AppOpsTest.java
index 547af7e..1c8e772 100644
--- a/tests/tests/permission/src/android/permission/cts/AppOpsTest.java
+++ b/tests/tests/permission/src/android/permission/cts/AppOpsTest.java
@@ -241,7 +241,7 @@
             assertNotNull("Each app op must have an operation string defined", opStr);
             opStrs.add(opStr);
         }
-        assertEquals("Not all op strings are unique", AppOpsManager._NUM_OP, opStrs.size());
+        assertEquals("Not all op strings are unique", AppOpsManager.getNumOps(), opStrs.size());
     }
 
     @SmallTest
diff --git a/tests/tests/permission/src/android/permission/cts/BackgroundPermissionsTest.java b/tests/tests/permission/src/android/permission/cts/BackgroundPermissionsTest.java
new file mode 100644
index 0000000..8e1484b
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/BackgroundPermissionsTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.permission.cts;
+
+import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.AppOpsManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
+import android.platform.test.annotations.AppModeFull;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+import android.util.ArrayMap;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class BackgroundPermissionsTest {
+    private static final String LOG_TAG = BackgroundPermissionsTest.class.getSimpleName();
+
+    @Test
+    @AppModeFull(reason = "Instant apps cannot read properties of other packages")
+    public void backgroundPermissionsNeedToBeInSameGroupAsForegroundPermissions() throws Exception {
+        PackageInfo pkg = InstrumentationRegistry.getContext().getPackageManager().getPackageInfo(
+                "android", PackageManager.GET_PERMISSIONS);
+        ArrayMap<String, String> potentialBackgroundPermissionsToGroup = new ArrayMap<>();
+
+        int numPermissions = pkg.permissions.length;
+        for (int i = 0; i < numPermissions; i++) {
+            PermissionInfo permission = pkg.permissions[i];
+
+            // background permissions must be dangerous
+            if ((permission.getProtection() & PROTECTION_DANGEROUS) != 0) {
+                potentialBackgroundPermissionsToGroup.put(permission.name, permission.group);
+            }
+        }
+
+        for (int i = 0; i < numPermissions; i++) {
+            PermissionInfo permission = pkg.permissions[i];
+            String backgroundPermissionName = permission.backgroundPermission;
+
+            if (backgroundPermissionName != null) {
+                Log.i(LOG_TAG, permission.name + "->" + backgroundPermissionName);
+
+                // foreground permissions must be dangerous
+                assertNotEquals(0, permission.getProtection() & PROTECTION_DANGEROUS);
+
+                // foreground permissions must be in a group
+                assertNotNull(permission.group);
+
+                // All foreground permissions need an app op
+                assertNotNull(AppOpsManager.permissionToOp(permission.name));
+
+                // the background permission must exist
+                assertTrue(potentialBackgroundPermissionsToGroup
+                        .containsKey(backgroundPermissionName));
+
+                // the foreground and background permission must be in the same group
+                assertEquals(permission.group,
+                        potentialBackgroundPermissionsToGroup.get(backgroundPermissionName));
+            }
+        }
+    }
+}
diff --git a/tests/tests/permission/src/android/permission/cts/DevicePowerPermissionTest.java b/tests/tests/permission/src/android/permission/cts/DevicePowerPermissionTest.java
deleted file mode 100644
index 006fb6d..0000000
--- a/tests/tests/permission/src/android/permission/cts/DevicePowerPermissionTest.java
+++ /dev/null
@@ -1,50 +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 android.permission.cts;
-
-import android.content.Context;
-import android.os.PowerManager;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-
-/**
- * Verify that various PowerManagement functionality requires Permission.
- */
-public class DevicePowerPermissionTest extends AndroidTestCase {
-    PowerManager mPowerManager;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-    }
-
-    /**
-     * Verify that going to sleep requires Permission.
-     * <p>Requires Permission:
-     *   {@link android.Manifest.permission#DEVICE_POWER}.
-     */
-    @LargeTest
-    public void testGoToSleep() {
-        try {
-            mPowerManager.goToSleep(0);
-            fail("Was able to call PowerManager.goToSleep without DEVICE_POWER Permission.");
-        } catch (SecurityException e) {
-            // expected
-        }
-    }
-}
diff --git a/tests/tests/permission/src/android/permission/cts/PermissionGroupChange.java b/tests/tests/permission/src/android/permission/cts/PermissionGroupChange.java
index e2e8b93..4dbaab8 100644
--- a/tests/tests/permission/src/android/permission/cts/PermissionGroupChange.java
+++ b/tests/tests/permission/src/android/permission/cts/PermissionGroupChange.java
@@ -97,7 +97,7 @@
 
     protected void clickAllowButton() throws Exception {
         mUiDevice.findObject(new UiSelector().resourceId(
-                "com.android.packageinstaller:id/permission_allow_button")).click();
+                "com.android.permissioncontroller:id/permission_allow_button")).click();
     }
 
     private void grantPermissionViaUi() throws Throwable {
diff --git a/tests/tests/permission/src/android/permission/cts/ProviderPermissionTest.java b/tests/tests/permission/src/android/permission/cts/ProviderPermissionTest.java
index ec0de49..83c8287 100644
--- a/tests/tests/permission/src/android/permission/cts/ProviderPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/ProviderPermissionTest.java
@@ -23,12 +23,17 @@
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AppModeInstant;
 import android.provider.CallLog;
 import android.provider.Contacts;
+import android.provider.ContactsContract;
 import android.provider.Settings;
+import android.provider.Telephony;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 
@@ -37,18 +42,36 @@
  */
 @MediumTest
 public class ProviderPermissionTest extends AndroidTestCase {
+
+    private static final String TAG = ProviderPermissionTest.class.getSimpleName();
+
+    private static final List<Uri> CONTACT_URIS = new ArrayList<Uri>() {{
+        add(Contacts.People.CONTENT_URI); // Deprecated.
+        add(ContactsContract.Contacts.CONTENT_FILTER_URI);
+        add(ContactsContract.Contacts.CONTENT_GROUP_URI);
+        add(ContactsContract.Contacts.CONTENT_LOOKUP_URI);
+        add(ContactsContract.CommonDataKinds.Email.CONTENT_URI);
+        add(ContactsContract.CommonDataKinds.Email.CONTENT_FILTER_URI);
+        add(ContactsContract.Directory.CONTENT_URI);
+        add(ContactsContract.Directory.ENTERPRISE_CONTENT_URI);
+        add(ContactsContract.Profile.CONTENT_URI);
+    }};
+
     /**
-     * Verify that read and write to contact requires permissions.
+     * Verify that reading contacts requires permissions.
      * <p>Tests Permission:
      *   {@link android.Manifest.permission#READ_CONTACTS}
      */
     public void testReadContacts() {
-        assertReadingContentUriRequiresPermission(Contacts.People.CONTENT_URI,
-                android.Manifest.permission.READ_CONTACTS);
+        for (Uri uri : CONTACT_URIS) {
+            Log.d(TAG, "Checking contacts URI " + uri);
+            assertReadingContentUriRequiresPermission(uri,
+                    android.Manifest.permission.READ_CONTACTS);
+        }
     }
 
     /**
-     * Verify that write to contact requires permissions.
+     * Verify that writing contacts requires permissions.
      * <p>Tests Permission:
      *   {@link android.Manifest.permission#WRITE_CONTACTS}
      */
@@ -57,22 +80,6 @@
                 android.Manifest.permission.WRITE_CONTACTS);
     }
 
-    public void assertWritingContentUriRequiresPermission(Uri uri, String permission,
-            boolean allowIAE) {
-        try {
-            getContext().getContentResolver().insert(uri, new ContentValues());
-            fail("expected SecurityException requiring " + permission);
-        } catch (IllegalArgumentException e) {
-            if (!allowIAE) {
-                fail("expected SecurityException requiring " + permission + " got " + e);
-            }
-        } catch (SecurityException expected) {
-            assertNotNull("security exception's error message.", expected.getMessage());
-            assertTrue("error message should contain " + permission + ".",
-                    expected.getMessage().contains(permission));
-        }
-    }
-
     /**
      * Verify that reading call logs requires permissions.
      * <p>Tests Permission:
@@ -89,10 +96,66 @@
      * <p>Tests Permission:
      *   {@link android.Manifest.permission#WRITE_CALL_LOG}
      */
+    @AppModeFull
     public void testWriteCallLog() {
         assertWritingContentUriRequiresPermission(CallLog.CONTENT_URI,
-                android.Manifest.permission.WRITE_CALL_LOG,
-                getContext().getPackageManager().isInstantApp());
+                android.Manifest.permission.WRITE_CALL_LOG);
+    }
+
+    /**
+     * Verify that reading from call-log (a content provider that is not accessible to instant apps)
+     * returns null
+     */
+    @AppModeInstant
+    public void testReadCallLogInstant() {
+        assertNull(getContext().getContentResolver().query(CallLog.CONTENT_URI, null, null, null,
+                null));
+    }
+
+    /**
+     * Verify that writing to call-log (a content provider that is not accessible to instant apps)
+     * yields an IAE.
+     */
+    @AppModeInstant
+    public void testWriteCallLogInstant() {
+        try {
+            getContext().getContentResolver().insert(CallLog.CONTENT_URI, new ContentValues());
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    /**
+     * Verify that reading already received SMS messages requires permissions.
+     * <p>Tests Permission:
+     *   {@link android.Manifest.permission#READ_SMS}
+     *
+     * <p>Note: The WRITE_SMS permission has been removed.
+     */
+    @AppModeFull
+    public void testReadSms() {
+        if (!mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_TELEPHONY)) {
+            return;
+        }
+
+        assertReadingContentUriRequiresPermission(Telephony.Sms.CONTENT_URI,
+                android.Manifest.permission.READ_SMS);
+    }
+
+    /**
+     * Verify that reading from 'sms' (a content provider that is not accessible to instant apps)
+     * returns null
+     */
+    @AppModeInstant
+    public void testReadSmsInstant() {
+        if (!mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_TELEPHONY)) {
+            return;
+        }
+
+        assertNull(getContext().getContentResolver().query(Telephony.Sms.CONTENT_URI, null, null,
+                null, null));
     }
 
     /**
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..ca4730a
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/RemovePermissionTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.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.SecurityTest;
+import android.support.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.Before;
+import org.junit.Test;
+
+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 Context mContext;
+    private Instrumentation mInstrumentation;
+
+    @Before
+    public void setContextAndInstrumentation() {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    }
+
+    @Before
+    public void wakeUpScreen() {
+        SystemUtil.runShellCommand("input keyevent KEYCODE_WAKEUP");
+    }
+
+    private boolean permissionGranted(String permName) throws PackageManager.NameNotFoundException {
+        PackageInfo appInfo = mContext.getPackageManager().getPackageInfo(USER_PKG_NAME,
+                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) {
+        String installResult = SystemUtil.runShellCommand(
+                "pm install -r data/local/tmp/cts/permissions/" + apk + ".apk");
+        assertEquals("Success", installResult.trim());
+    }
+
+    private void uninstallApp(String pkg) {
+        String uninstallResult = SystemUtil.runShellCommand(
+                "pm uninstall " + pkg);
+        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(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(TEST_PERMISSION));
+    }
+}
diff --git a/tests/tests/permission/src/android/permission/cts/SmsManagerPermissionTest.java b/tests/tests/permission/src/android/permission/cts/SmsManagerPermissionTest.java
new file mode 100644
index 0000000..ab099ee
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/SmsManagerPermissionTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.permission.cts;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.SmsManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeTrue;
+
+/**
+ * Test that sending SMS and MMS messages requires permissions.
+ */
+@RunWith(AndroidJUnit4.class)
+public class SmsManagerPermissionTest {
+
+    private static final String SOURCE_ADDRESS = "+15550000000";
+    private static final String DESTINATION_ADDRESS = "+15550000001";
+
+    private boolean mHasTelephony;
+    private SmsManager mSmsManager;
+    private Context mContext;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getContext();
+        mHasTelephony = mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_TELEPHONY);
+        assumeTrue(mHasTelephony); // Don't run these tests if FEATURE_TELEPHONY is not available.
+
+        mSmsManager = SmsManager.getDefault();
+        assertNotNull(mSmsManager);
+    }
+
+    @Test(expected = SecurityException.class)
+    public void testSendTextMessage() {
+        mSmsManager.sendTextMessage(
+                DESTINATION_ADDRESS, SOURCE_ADDRESS, "Message text", null, null);
+    }
+
+    @Test(expected = SecurityException.class)
+    public void testSendTextMessageWithoutPersisting() {
+        mSmsManager.sendTextMessageWithoutPersisting(
+                DESTINATION_ADDRESS, SOURCE_ADDRESS, "Message text", null, null);
+    }
+
+    @Test(expected = SecurityException.class)
+    public void testSendMultipartTextMessage() {
+        ArrayList<String> messageParts = new ArrayList<>();
+        messageParts.add("Message text");
+        mSmsManager.sendMultipartTextMessage(
+                DESTINATION_ADDRESS, SOURCE_ADDRESS, messageParts, null, null);
+    }
+
+    @Test(expected = SecurityException.class)
+    public void testSendDataMessage() {
+        mSmsManager.sendDataMessage(
+                DESTINATION_ADDRESS, SOURCE_ADDRESS, (short) 1, new byte[]{0, 0, 0}, null, null);
+    }
+
+    @Test(expected = SecurityException.class)
+    public void testSendMultimediaMessage() {
+        // Ideally we would provide an Uri to an existing resource, to make sure the
+        // SecurityException is not due to the invalid Uri.
+        Uri uri = Uri.parse("android.resource://android.permission.cts/some-image.png");
+        mSmsManager.sendMultimediaMessage(mContext, uri, "", null, null);
+    }
+}
diff --git a/tests/tests/permission/src/android/permission/cts/SuspendAppsPermissionTest.java b/tests/tests/permission/src/android/permission/cts/SuspendAppsPermissionTest.java
deleted file mode 100644
index 2346d4a..0000000
--- a/tests/tests/permission/src/android/permission/cts/SuspendAppsPermissionTest.java
+++ /dev/null
@@ -1,84 +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.permission.cts;
-
-import static android.Manifest.permission.SEND_SHOW_SUSPENDED_APP_DETAILS;
-import static android.Manifest.permission.SUSPEND_APPS;
-import static android.content.Intent.ACTION_SHOW_SUSPENDED_APP_DETAILS;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.List;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class SuspendAppsPermissionTest {
-
-    private PackageManager mPackageManager;
-
-    @Before
-    public void setUp() {
-        mPackageManager = InstrumentationRegistry.getTargetContext().getPackageManager();
-    }
-
-    @Test
-    public void testNumberOfAppsWithPermission() {
-        final List<PackageInfo> packagesWithPerm = mPackageManager.getPackagesHoldingPermissions(
-                new String[]{SUSPEND_APPS}, 0);
-        assertTrue("At most one app can hold the permission " + SUSPEND_APPS + ", but found more: "
-                + packagesWithPerm, packagesWithPerm.size() <= 1);
-    }
-
-    @Test
-    public void testShowSuspendedAppDetailsDeclared() {
-        final List<PackageInfo> packagesWithPerm = mPackageManager.getPackagesHoldingPermissions(
-                new String[]{SUSPEND_APPS}, 0);
-        final Intent showDetailsIntent = new Intent(ACTION_SHOW_SUSPENDED_APP_DETAILS);
-        boolean success = true;
-        StringBuilder errorString = new StringBuilder();
-        for (PackageInfo packageInfo : packagesWithPerm) {
-            showDetailsIntent.setPackage(packageInfo.packageName);
-            final ResolveInfo resolveInfo = mPackageManager.resolveActivity(showDetailsIntent, 0);
-            if (resolveInfo == null || resolveInfo.activityInfo == null) {
-                errorString.append("No activity found for " + ACTION_SHOW_SUSPENDED_APP_DETAILS
-                        + " inside package " + packageInfo.packageName);
-                success = false;
-            }
-            else if (!SEND_SHOW_SUSPENDED_APP_DETAILS.equals(resolveInfo.activityInfo.permission)) {
-                errorString.append("Activity handling " + ACTION_SHOW_SUSPENDED_APP_DETAILS
-                        + " not protected with permission " + SEND_SHOW_SUSPENDED_APP_DETAILS);
-                success = false;
-            }
-        }
-        if (!success) {
-            fail(errorString.toString());
-        }
-    }
-}
diff --git a/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java b/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java
index d117e19..dcc9cdb 100644
--- a/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java
@@ -275,48 +275,6 @@
         }
     }
 
-    /**
-     * Verify that TelephonyManager.setAllowedCarriers requires Permission.
-     * <p>
-     * Requires Permission:
-     * {@link android.Manifest.permission#MODIFY_PHONE_STATE}.
-     */
-    @Test
-    public void testSetAllowedCarriers() {
-        if (!mHasTelephony
-                || !getContext().getPackageManager().hasSystemFeature(
-                        PackageManager.FEATURE_TELEPHONY_CARRIERLOCK)) {
-            return;
-        }
-        try {
-            mTelephonyManager.setAllowedCarriers(0, Collections.emptyList());
-            fail("Able to set allowed carriers");
-        } catch (SecurityException e) {
-            // expected
-        }
-    }
-
-    /**
-     * Verify that TelephonyManager.getAllowedCarriers requires Permission.
-     * <p>
-     * Requires Permission:
-     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}.
-     */
-    @Test
-    public void testGetAllowedCarriers() {
-        if (!mHasTelephony
-                || !getContext().getPackageManager().hasSystemFeature(
-                        PackageManager.FEATURE_TELEPHONY_CARRIERLOCK)) {
-            return;
-        }
-        try {
-            mTelephonyManager.getAllowedCarriers(0);
-            fail("Able to get allowed carriers");
-        } catch (SecurityException e) {
-            // expected
-        }
-    }
-
     private static Context getContext() {
         return InstrumentationRegistry.getContext();
     }
diff --git a/tests/tests/permission/testapps/Android.mk b/tests/tests/permission/testapps/Android.mk
new file mode 100644
index 0000000..9aaa6ac
--- /dev/null
+++ b/tests/tests/permission/testapps/Android.mk
@@ -0,0 +1,17 @@
+# 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 $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libs/deviceutillegacy/Android.mk b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/Android.mk
similarity index 61%
copy from libs/deviceutillegacy/Android.mk
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/Android.mk
index 8cfa6d1..0302643 100644
--- a/libs/deviceutillegacy/Android.mk
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2014 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.
@@ -11,26 +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_STATIC_JAVA_LIBRARIES := \
-    compatibility-device-util \
-    junit
-
-LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src)
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE := ctsdeviceutillegacy
-
+LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
 
-include $(BUILD_STATIC_JAVA_LIBRARY)
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
+
+LOCAL_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..20fd73a
--- /dev/null
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.
+ -->
+
+<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/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/res/values/strings.xml b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/res/values/strings.xml
new file mode 100644
index 0000000..062b41c
--- /dev/null
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="test_permission">Test Permission</string>
+</resources>
diff --git a/libs/deviceutillegacy/Android.mk b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/Android.mk
similarity index 61%
copy from libs/deviceutillegacy/Android.mk
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/Android.mk
index 8cfa6d1..70a096e 100644
--- a/libs/deviceutillegacy/Android.mk
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2014 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.
@@ -11,26 +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_STATIC_JAVA_LIBRARIES := \
-    compatibility-device-util \
-    junit
-
-LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src)
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE := ctsdeviceutillegacy
-
+LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
 
-include $(BUILD_STATIC_JAVA_LIBRARY)
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey2
+LOCAL_PACKAGE_NAME := CtsAdversarialPermissionUserApp
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/AndroidManifest.xml b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/AndroidManifest.xml
new file mode 100644
index 0000000..84ba0ed
--- /dev/null
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.permission.cts.revokepermissionwhenremoved.userapp">
+
+    <uses-permission android:name="android.permission.cts.revokepermissionwhenremoved.TestPermission" />
+
+    <application>
+    </application>
+</manifest>
+
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/Android.mk b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/Android.mk
new file mode 100644
index 0000000..9aaa6ac
--- /dev/null
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/Android.mk
@@ -0,0 +1,17 @@
+# 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 $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libs/deviceutillegacy/Android.mk b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/Android.mk
similarity index 61%
copy from libs/deviceutillegacy/Android.mk
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/Android.mk
index 8cfa6d1..512d6ac 100644
--- a/libs/deviceutillegacy/Android.mk
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2014 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.
@@ -11,26 +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_STATIC_JAVA_LIBRARIES := \
-    compatibility-device-util \
-    junit
-
-LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src)
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE := ctsdeviceutillegacy
-
+LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
 
-include $(BUILD_STATIC_JAVA_LIBRARY)
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests cts_instant
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+
+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..72f836d
--- /dev/null
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.
+ -->
+
+<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/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/res/values/strings.xml b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/res/values/strings.xml
new file mode 100644
index 0000000..062b41c
--- /dev/null
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<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/AndroidTest.xml b/tests/tests/permission2/AndroidTest.xml
index d4fb85f..91318f8 100644
--- a/tests/tests/permission2/AndroidTest.xml
+++ b/tests/tests/permission2/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS Permission 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="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsPermission2TestCases.apk" />
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index b20bd67..cb39898 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -262,6 +262,7 @@
 
     <protected-broadcast android:name="android.intent.action.HEADSET_PLUG" />
     <protected-broadcast android:name="android.media.action.HDMI_AUDIO_PLUG" />
+    <protected-broadcast android:name="android.media.action.MICROPHONE_MUTE_CHANGED" />
 
     <protected-broadcast android:name="android.media.AUDIO_BECOMING_NOISY" />
     <protected-broadcast android:name="android.media.RINGER_MODE_CHANGED" />
@@ -487,6 +488,7 @@
     <protected-broadcast android:name="android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED" />
     <protected-broadcast android:name="android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED" />
     <protected-broadcast android:name="android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION" />
+    <protected-broadcast android:name="android.telephony.action.SUBSCRIPTION_PLANS_CHANGED" />
 
     <protected-broadcast android:name="com.android.bluetooth.btservice.action.ALARM_WAKEUP" />
     <protected-broadcast android:name="com.android.server.action.NETWORK_STATS_POLL" />
@@ -597,6 +599,12 @@
     <protected-broadcast android:name="android.app.action.AFFILIATED_PROFILE_TRANSFER_OWNERSHIP_COMPLETE" />
     <protected-broadcast android:name="android.app.action.DATA_SHARING_RESTRICTION_CHANGED" />
     <protected-broadcast android:name="android.app.action.STATSD_STARTED" />
+    <protected-broadcast android:name="com.android.server.biometrics.fingerprint.ACTION_LOCKOUT_RESET" />
+    <protected-broadcast android:name="com.android.server.biometrics.face.ACTION_LOCKOUT_RESET" />
+
+    <!-- For IdleController -->
+    <protected-broadcast android:name="android.intent.action.DOCK_IDLE" />
+    <protected-broadcast android:name="android.intent.action.DOCK_ACTIVE" />
 
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
@@ -725,7 +733,7 @@
         android:description="@string/permdesc_receiveMms"
         android:protectionLevel="dangerous" />
 
-    <!-- Allows an application to read previously received cell broadcast
+    <!-- @TestApi Allows an application to read previously received cell broadcast
          messages and to register a content observer to get notifications when
          a cell broadcast has been received and added to the database. For
          emergency alerts, the database is updated immediately after the
@@ -816,6 +824,9 @@
         android:label="@string/permgrouplab_location"
         android:description="@string/permgroupdesc_location"
         android:request="@string/permgrouprequest_location"
+        android:requestDetail="@string/permgrouprequestdetail_location"
+        android:backgroundRequest="@string/permgroupbackgroundrequest_location"
+        android:backgroundRequestDetail="@string/permgroupbackgroundrequestdetail_location"
         android:priority="400" />
 
     <!-- Allows an app to access precise location.
@@ -826,6 +837,7 @@
         android:permissionGroup="android.permission-group.LOCATION"
         android:label="@string/permlab_accessFineLocation"
         android:description="@string/permdesc_accessFineLocation"
+        android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
         android:protectionLevel="dangerous|instant" />
 
     <!-- Allows an app to access approximate location.
@@ -836,6 +848,20 @@
         android:permissionGroup="android.permission-group.LOCATION"
         android:label="@string/permlab_accessCoarseLocation"
         android:description="@string/permdesc_accessCoarseLocation"
+        android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
+        android:protectionLevel="dangerous|instant" />
+
+    <!-- Allows an app to access location in the background.  If you
+         are requesting this, you should also request {@link #ACCESS_FINE_LOCATION}.
+         Requesting this by itself is not sufficient to give you
+         location access.
+         <p>Protection level: dangerous
+         @hide
+    -->
+    <permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
+        android:permissionGroup="android.permission-group.LOCATION"
+        android:label="@string/permlab_accessBackgroundLocation"
+        android:description="@string/permdesc_accessBackgroundLocation"
         android:protectionLevel="dangerous|instant" />
 
     <!-- ====================================================================== -->
@@ -845,21 +871,12 @@
 
     <!-- Used for permissions that are associated telephony features. -->
     <permission-group android:name="android.permission-group.CALL_LOG"
-        android:icon="@drawable/perm_group_phone_calls"
+        android:icon="@drawable/perm_group_call_log"
         android:label="@string/permgrouplab_calllog"
         android:description="@string/permgroupdesc_calllog"
         android:request="@string/permgrouprequest_calllog"
         android:priority="450" />
 
-
-    <!-- Used for permissions that are associated telephony features. -->
-    <permission-group android:name="android.permission-group.CALL_LOG"
-                      android:icon="@drawable/perm_group_phone_calls"
-                      android:label="@string/permgrouplab_calllog"
-                      android:description="@string/permgroupdesc_calllog"
-                      android:request="@string/permgrouprequest_calllog"
-                      android:priority="450" />
-
     <!-- Allows an application to access the IMS call service: making and
          modifying a call
         <p>Protection level: signature|privileged
@@ -1323,7 +1340,7 @@
          This should only be used by HDMI-CEC service.
     -->
     <permission android:name="android.permission.HDMI_CEC"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature|privileged|vendorPrivileged" />
 
     <!-- @SystemApi Allows an application to use location features in hardware,
          such as the geofencing api.
@@ -1472,13 +1489,14 @@
         android:protectionLevel="signature|privileged" />
 
     <!-- @hide Allows internal management of Wi-Fi connectivity state when on
-         permission review mode.
+         wireless consent mode.
          <p>Not for use by third-party applications. -->
-    <permission android:name="android.permission.MANAGE_WIFI_WHEN_PERMISSION_REVIEW_REQUIRED"
+    <permission android:name="android.permission.MANAGE_WIFI_WHEN_WIRELESS_CONSENT_REQUIRED"
         android:protectionLevel="signature" />
 
-    <!-- @hide Allows an app to bypass Private DNS.
-         <p>Not for use by third-party applications. -->
+    <!-- #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. -->
     <permission android:name="android.permission.NETWORK_BYPASS_PRIVATE_DNS"
         android:protectionLevel="signature" />
 
@@ -1502,14 +1520,6 @@
     <permission android:name="android.permission.SUSPEND_APPS"
         android:protectionLevel="signature|privileged" />
 
-    <!-- @SystemApi Must be required by activities that handle the intent action
-         {@link Intent#ACTION_SEND_SHOW_SUSPENDED_APP_DETAILS}. This is for use by apps that
-         hold {@link Manifest.permission#SUSPEND_APPS} to interact with the system.
-         <p>Not for use by third-party applications.
-         @hide -->
-    <permission android:name="android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS"
-                android:protectionLevel="signature" />
-
     <!-- Allows applications to discover and pair bluetooth devices.
          <p>Protection level: normal
     -->
@@ -1580,9 +1590,9 @@
     <permission android:name="android.permission.NFC_HANDOVER_STATUS"
         android:protectionLevel="signature|privileged" />
 
-    <!-- @hide Allows internal management of Bluetooth state when on permission review mode.
+    <!-- @hide Allows internal management of Bluetooth state when on wireless consnet mode.
          <p>Not for use by third-party applications. -->
-    <permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_PERMISSION_REVIEW_REQUIRED"
+    <permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED"
         android:protectionLevel="signature" />
 
     <!-- ================================== -->
@@ -1711,7 +1721,7 @@
          @hide This should only be used by OEM's TvInputService's.
     -->
     <permission android:name="android.permission.TV_INPUT_HARDWARE"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature|privileged|vendorPrivileged" />
 
     <!-- @SystemApi Allows to capture a frame of TV input hardware such as
          built-in tuners and HDMI-in's.
@@ -2070,6 +2080,15 @@
     <permission android:name="android.permission.START_ANY_ACTIVITY"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi Must be required by activities that handle the intent action
+         {@link Intent#ACTION_SEND_SHOW_SUSPENDED_APP_DETAILS}. This is for use by apps that
+         hold {@link Manifest.permission#SUSPEND_APPS} to interact with the system.
+         <p>Not for use by third-party applications.
+         @hide -->
+    <permission android:name="android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS"
+                android:protectionLevel="signature" />
+    <uses-permission android:name="android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS" />
+
     <!-- @deprecated The {@link android.app.ActivityManager#restartPackage}
         API is no longer supported. -->
     <permission android:name="android.permission.RESTART_PACKAGES"
@@ -2240,7 +2259,8 @@
         android:description="@string/permdesc_install_shortcut"
         android:protectionLevel="normal"/>
 
-    <!--This permission is no longer supported.
+    <!-- <p class="caution"><strong>Don't use this permission in your app.</strong><br>This
+         permission is no longer supported.
     -->
     <permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"
         android:label="@string/permlab_uninstall_shortcut"
@@ -3026,6 +3046,15 @@
     <permission android:name="android.permission.INSTALL_PACKAGE_UPDATES"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Allows an application to install existing system packages. This is a limited
+         version of {@link android.Manifest.permission#INSTALL_PACKAGES}.
+         <p>Not for use by third-party applications.
+         TODO(b/80204953): remove this permission once we have a long-term solution.
+         @hide
+    -->
+    <permission android:name="com.android.permission.INSTALL_EXISTING_PACKAGES"
+        android:protectionLevel="signature|privileged" />
+
     <!-- @SystemApi Allows an application to clear user data.
          <p>Not for use by third-party applications
          @hide
@@ -3354,7 +3383,7 @@
     <permission android:name="android.permission.OBSERVE_APP_USAGE"
         android:protectionLevel="signature|privileged" />
 
-    <!-- @hide @SystemApi Allows an application to change the app idle state of an app.
+    <!-- @hide @TestApi @SystemApi Allows an application to change the app idle state of an app.
          <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.CHANGE_APP_IDLE_STATE"
         android:protectionLevel="signature|privileged" />
@@ -3470,6 +3499,10 @@
     <permission android:name="android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE"
                 android:protectionLevel="signature" />
 
+    <!-- @hide Internal permission to allows an application to access card content provider. -->
+    <permission android:name="android.permission.WRITE_SETTINGS_HOMEPAGE_DATA"
+                android:protectionLevel="signature|privileged" />
+
     <!-- @SystemApi Allows applications to set a live wallpaper.
          @hide XXX Change to signature once the picker is moved to its
          own apk as Ghod Intended. -->
@@ -3580,7 +3613,7 @@
     <permission android:name="android.permission.UPDATE_LOCK"
         android:protectionLevel="signature|privileged" />
 
-    <!-- @SystemApi Allows an application to read the current set of notifications, including
+    <!-- @SystemApi @TestApi Allows an application to read the current set of notifications, including
          any metadata and intents attached.
          @hide -->
     <permission android:name="android.permission.ACCESS_NOTIFICATIONS"
@@ -3618,6 +3651,14 @@
     <permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT"
         android:protectionLevel="signature" />
 
+    <!-- Allows managing (adding, removing) facial templates. Reserved for the system. @hide -->
+    <permission android:name="android.permission.MANAGE_FACE"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows an app to reset face authentication attempt counter. Reserved for the system. @hide -->
+    <permission android:name="android.permission.RESET_FACE_LOCKOUT"
+        android:protectionLevel="signature" />
+
     <!-- Allows an application to control keyguard.  Only allowed for system processes.
         @hide -->
     <permission android:name="android.permission.CONTROL_KEYGUARD"
@@ -3978,6 +4019,11 @@
     <permission android:name="android.permission.DISABLE_HIDDEN_API_CHECKS"
                 android:protectionLevel="signature" />
 
+    <!-- Allows an application to read emergency info name.
+         @hide <p>Not for use by third-party applications. -->
+    <permission android:name="com.android.emergency.permission.READ_EMERGENCY_INFO_NAME"
+                android:protectionLevel="signature" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
@@ -4161,6 +4207,12 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="com.android.internal.app.SuspendedAppActivity"
+                  android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert"
+                  android:excludeFromRecents="true"
+                  android:process=":ui">
+        </activity>
+
         <activity android:name="com.android.internal.app.UnlaunchableAppActivity"
                 android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert"
                 android:excludeFromRecents="true"
@@ -4283,26 +4335,6 @@
             </intent-filter>
         </receiver>
 
-        <receiver android:name="com.android.server.stats.StatsCompanionService$AnomalyAlarmReceiver"
-                  android:permission="android.permission.STATSCOMPANION"
-                  android:exported="false">
-        </receiver>
-
-        <receiver android:name="com.android.server.stats.StatsCompanionService$PullingAlarmReceiver"
-                  android:permission="android.permission.STATSCOMPANION"
-                  android:exported="false">
-        </receiver>
-
-        <receiver android:name="com.android.server.am.BatteryStatsService$UsbConnectionReceiver"
-                  android:exported="false">
-            <intent-filter>
-                <action android:name="android.intent.action.BOOT_COMPLETED" />
-            </intent-filter>
-            <intent-filter>
-                <action android:name="android.hardware.usb.action.USB_STATE" />
-            </intent-filter>
-        </receiver>
-
         <service android:name="android.hardware.location.GeofenceHardwareService"
             android:permission="android.permission.LOCATION_HARDWARE"
             android:exported="false" />
diff --git a/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java b/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java
index 585f9c6..b9366b1 100644
--- a/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java
@@ -16,6 +16,7 @@
 
 package android.permission2.cts;
 
+import com.android.compatibility.common.util.PropertyUtil;
 import com.android.compatibility.common.util.SystemUtil;
 
 import android.content.pm.PackageInfo;
@@ -52,6 +53,9 @@
     private static final String PLATFORM_PACKAGE_NAME = "android";
 
     public void testPrivappPermissionsEnforcement() throws Exception {
+        assertEquals("ro.control_privapp_permissions is not set to enforce",
+                "enforce", PropertyUtil.getProperty("ro.control_privapp_permissions"));
+
         Set<String> platformPrivPermissions = new HashSet<>();
         PackageManager pm = getContext().getPackageManager();
         PackageInfo platformPackage = pm.getPackageInfo(PLATFORM_PACKAGE_NAME,
diff --git a/tests/tests/preference/AndroidTest.xml b/tests/tests/preference/AndroidTest.xml
index 584bbc8..4287b76 100644
--- a/tests/tests/preference/AndroidTest.xml
+++ b/tests/tests/preference/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS Preference test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="uitoolkit" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/preference2/AndroidTest.xml b/tests/tests/preference2/AndroidTest.xml
index b13eba5..a3fb234 100644
--- a/tests/tests/preference2/AndroidTest.xml
+++ b/tests/tests/preference2/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS Preference test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="uitoolkit" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsPreference2TestCases.apk" />
diff --git a/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowLandscapeTest.java b/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowLandscapeTest.java
index e0f387b..8e042d7 100644
--- a/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowLandscapeTest.java
+++ b/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowLandscapeTest.java
@@ -101,14 +101,6 @@
     }
 
     /**
-     * Landscape setup of {@link #startWithFragmentAndInitTitleMultiWindowInner}.
-     */
-    @Test
-    public void startWithFragmentAndInitTitleMultiWindowLandscapeTest() {
-        startWithFragmentAndInitTitleMultiWindowInner();
-    }
-
-    /**
      * Landscape setup of {@link #startWithFragmentNoHeadersInner}.
      */
     @Test
@@ -125,14 +117,6 @@
     }
 
     /**
-     * Landscape setup of {@link #startWithFragmentNoHeadersMultiWindowTest}.
-     */
-    @Test
-    public void startWithFragmentNoHeadersMultiWindowLandscapeTest() {
-        startWithFragmentNoHeadersMultiWindowTest();
-    }
-
-    /**
      * Landscape setup of {@link #listDialogTest}.
      */
     @Test
@@ -156,38 +140,6 @@
         recreateInnerFragmentTest();
     }
 
-    /**
-     * Landscape setup of {@link #multiWindowInOutTest}.
-     */
-    @Test
-    public void multiWindowInOutLandscapeTest() {
-        multiWindowInOutTest();
-    }
-
-    /**
-     * Landscape setup of {@link #multiWindowInnerFragmentInOutTest}.
-     */
-    @Test
-    public void multiWindowInnerFragmentInOutLandscapeTest() {
-        multiWindowInnerFragmentInOutTest();
-    }
-
-    /**
-     * Landscape setup of {@link #multiWindowInitialHeaderOnBackTest}.
-     */
-    @Test
-    public void multiWindowInitialHeaderOnBackLandscapeTest() {
-        multiWindowInitialHeaderOnBackTest();
-    }
-
-    /**
-     * Landscape setup of {@link #multiWindowHistoryPreserveTest}.
-     */
-    @Test
-    public void multiWindowHistoryPreserveLandscapeTest() {
-        multiWindowHistoryPreserveTest();
-    }
-
     @Override
     protected PreferenceWithHeaders launchActivity(Intent intent) {
         if (intent != null) {
@@ -200,9 +152,7 @@
     @Override
     protected void runOnUiThread(final Runnable runnable) {
         try {
-            mActivityRule.runOnUiThread(() -> {
-                runnable.run();
-            });
+            mActivityRule.runOnUiThread(runnable);
         } catch (Throwable ex) {
             throw new RuntimeException("Failure on the UI thread", ex);
         }
diff --git a/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowPortraitTest.java b/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowPortraitTest.java
index 6b484bd..48e7ef7 100644
--- a/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowPortraitTest.java
+++ b/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowPortraitTest.java
@@ -108,14 +108,6 @@
     }
 
     /**
-     * Portrait setup of {@link #startWithFragmentAndInitTitleMultiWindowInner}.
-     */
-    @Test
-    public void startWithFragmentAndInitTitleMultiWindowPortraitTest() {
-        startWithFragmentAndInitTitleMultiWindowInner();
-    }
-
-    /**
      * Portrait setup of {@link #startWithFragmentNoHeadersInner}.
      */
     @Test
@@ -132,14 +124,6 @@
     }
 
     /**
-     * Portrait setup of {@link #startWithFragmentNoHeadersMultiWindowTest}.
-     */
-    @Test
-    public void startWithFragmentNoHeadersMultiWindowPortraitTest() {
-        startWithFragmentNoHeadersMultiWindowTest();
-    }
-
-    /**
      * Portrait setup of {@link #listDialogTest}.
      */
     @Test
@@ -163,38 +147,6 @@
         recreateInnerFragmentTest();
     }
 
-    /**
-     * Portrait setup of {@link #multiWindowInOutTest}.
-     */
-    @Test
-    public void multiWindowInOutPortraitTest() {
-        multiWindowInOutTest();
-    }
-
-    /**
-     * Portrait setup of {@link #multiWindowInnerFragmentInOutTest}.
-     */
-    @Test
-    public void multiWindowInnerFragmentInOutPortraitTest() {
-        multiWindowInnerFragmentInOutTest();
-    }
-
-    /**
-     * Portrait setup of {@link #multiWindowInitialHeaderOnBackTest}.
-     */
-    @Test
-    public void multiWindowInitialHeaderOnBackPortraitTest() {
-        multiWindowInitialHeaderOnBackTest();
-    }
-
-    /**
-     * Portrait setup of {@link #multiWindowHistoryPreserveTest}.
-     */
-    @Test
-    public void multiWindowHistoryPreservePortraitTest() {
-        multiWindowHistoryPreserveTest();
-    }
-
     @Override
     protected PreferenceWithHeaders launchActivity(Intent intent) {
         if (intent != null) {
@@ -207,9 +159,7 @@
     @Override
     protected void runOnUiThread(final Runnable runnable) {
         try {
-            mActivityRule.runOnUiThread(() -> {
-                runnable.run();
-            });
+            mActivityRule.runOnUiThread(runnable);
         } catch (Throwable ex) {
             throw new RuntimeException("Failure on the UI thread", ex);
         }
diff --git a/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowTest.java b/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowTest.java
index 253f8f6..28071ac 100644
--- a/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowTest.java
+++ b/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowTest.java
@@ -27,7 +27,6 @@
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.os.SystemClock;
-import android.preference2.cts.R;
 import android.util.Log;
 
 import com.android.compatibility.common.util.BitmapUtils;
@@ -325,36 +324,6 @@
     }
 
     /**
-     * For: Large screen (multi-pane).
-     * Scenario: Tests that initial title is displayed or hidden properly when transitioning in and
-     * out of the multi-window mode.
-     */
-    void startWithFragmentAndInitTitleMultiWindowInner() {
-        launchActivityWithExtras(PreferenceWithHeaders.PrefsTwoFragment.class,
-                false /* noHeaders */, INITIAL_TITLE_RES_ID);
-        if (!shouldRunLargeDeviceTest()) {
-            return;
-        }
-
-        assertInitialStateForFragment();
-        String testTitle = mActivity.getResources().getString(INITIAL_TITLE_RES_ID);
-
-        // Title should not be shown (we are in multi-pane).
-        assertFalse(mTestUtils.isTextShown(testTitle));
-
-        mTestUtils.enterMultiWindow(mActivity);
-        mTestUtils.getMultiWindowFocus(mActivity);
-
-        // Title should be shown (we are in single-pane).
-        assertTextShown(testTitle);
-
-        mTestUtils.leaveMultiWindow(mActivity);
-
-        // Title should not be shown (we are back in multi-pane).
-        assertTextHidden(testTitle);
-    }
-
-    /**
      * For: Any screen (single or multi-pane).
      * Scenario: Tests that EXTRA_NO_HEADERS intent arg that prevents showing headers in multi-pane
      * is applied correctly.
@@ -390,34 +359,6 @@
 
     /**
      * For: Any screen (single or multi-pane).
-     * Scenario: Tests that EXTRA_NO_HEADERS intent arg that prevents showing headers survives
-     * correctly multi-window changes. Tested via screenshots.
-     */
-    void startWithFragmentNoHeadersMultiWindowTest() {
-        launchActivityWithExtras(PreferenceWithHeaders.PrefsTwoFragment.class,
-                true /* noHeaders */, -1 /* initialTitle */);
-
-        assertInitialStateForFragment();
-
-        // Workaround for some focus bug in the framework
-        mTestUtils.tapOnViewWithText(PREFS2_PANEL_TITLE);
-
-        // Take screenshot
-        Bitmap before = mTestUtils.takeScreenshot();
-
-        // Enter and leave multi-window.
-        mTestUtils.enterMultiWindow(mActivity);
-        mTestUtils.leaveMultiWindow(mActivity);
-
-        assertInitialStateForFragment();
-
-        // Compare screenshots
-        Bitmap after = mTestUtils.takeScreenshot();
-        assertScreenshotsAreEqual(before, after);
-    }
-
-    /**
-     * For: Any screen (single or multi-pane).
      * Scenario: Tests that list preference opens correctly and that back press correctly closes it.
      */
     void listDialogTest()  {
@@ -515,163 +456,6 @@
         assertScreenshotsAreEqual(before, after);
     }
 
-    /**
-     * For: Any screen (single or multi-pane).
-     * Scenario: Tests that the PreferenceActivity properly restores its state after going to
-     * multi-window and back. Test done via screenshots.
-     */
-    void multiWindowInOutTest() {
-        launchActivity();
-
-        assertInitialState();
-        // Tap on Prefs2 header.
-        tapOnPrefs2Header();
-
-        assertPanelPrefs2Shown();
-
-        // Take screenshot
-        Bitmap before = mTestUtils.takeScreenshot();
-
-        // Enter and leave multi-window.
-        mTestUtils.enterMultiWindow(mActivity);
-        mTestUtils.leaveMultiWindow(mActivity);
-
-        assertPanelPrefs2Shown();
-
-        // Compare screenshots
-        Bitmap after = mTestUtils.takeScreenshot();
-        assertScreenshotsAreEqual(before, after);
-    }
-
-    /**
-     * For: Any screen (single or multi-pane).
-     * Scenario: Tests that the PreferenceActivity properly restores its state after going to
-     * multi-window and back while an inner fragment is shown. Test done via screenshots.
-     */
-    void multiWindowInnerFragmentInOutTest() {
-        launchActivity();
-
-        assertInitialState();
-        if (!mIsMultiPane) {
-            tapOnPrefs1Header();
-        }
-
-        // Go to preferences inner fragment.
-        mTestUtils.tapOnViewWithText(INNER_FRAGMENT_PREF_BUTTON);
-
-        // We don't need to check that correct panel is displayed that is already covered by
-        // smallScreenGoToFragmentInner and largeScreenGoToFragmentInner
-
-        // Take screenshot
-        Bitmap before = mTestUtils.takeScreenshot();
-
-        // Enter and leave multi-window.
-        mTestUtils.enterMultiWindow(mActivity);
-        mTestUtils.leaveMultiWindow(mActivity);
-
-        // Compare screenshots
-        Bitmap after = mTestUtils.takeScreenshot();
-        assertScreenshotsAreEqual(before, after);
-    }
-
-    /**
-     * For: Large screen (single or multi-pane).
-     * Scenario: Goes to single-pane by entering multi-window and tests that back press ends up with
-     * a list of headers and nothing else. Then leaves multi-window back to single-pane and tests if
-     * the proper default header was opened (screenshot test).
-     */
-    void multiWindowInitialHeaderOnBackTest() {
-        launchActivity();
-        if (!shouldRunLargeDeviceTest()) {
-            return;
-        }
-
-        assertInitialState();
-
-        Bitmap before = mTestUtils.takeScreenshot();
-
-        // Enter multi-window.
-        mTestUtils.enterMultiWindow(mActivity);
-
-        // Get window focus (otherwise back press would close multi-window instead of firing to the
-        // Activity.
-        mTestUtils.getMultiWindowFocus(mActivity);
-
-        pressBack();
-
-        // Only headers should be shown (also checks that we have correct focus).
-        assertHeadersShown();
-        assertPanelPrefs1Hidden();
-        assertPanelPrefs2Hidden();
-
-        // Leave multi-window
-        mTestUtils.leaveMultiWindow(mActivity);
-
-        // Headers and Prefs1 should be shown.
-        assertHeadersShown();
-        assertPanelPrefs1Shown();
-        assertPanelPrefs2Hidden();
-
-        // Compare screenshots
-        Bitmap after = mTestUtils.takeScreenshot();
-        assertScreenshotsAreEqual(before, after);
-    }
-
-    /**
-     * For: Large screen (multi-pane).
-     * Scenario: Tests that history is preserved correctly while transitioning to multi-window.
-     * Navigates to Prefs2 pane and then goes to single-pane mode via multi-window. Test that back
-     * press navigates to the headers list. Then tests that restoring multi-pane by leaving
-     * multi-window opens the same screen with which was the activity started before (screenshot
-     * test).
-     */
-    void multiWindowHistoryPreserveTest() {
-        launchActivity();
-        if (!shouldRunLargeDeviceTest()) {
-            return;
-        }
-
-        assertInitialState();
-        Bitmap before = mTestUtils.takeScreenshot();
-
-        tapOnPrefs2Header();
-
-        // Enter multi-window.
-        mTestUtils.enterMultiWindow(mActivity);
-        mTestUtils.getMultiWindowFocus(mActivity);
-
-        // Only Prefs2 should be shown (also checks that we have correct focus).
-        assertHeadersHidden();
-        assertPanelPrefs1Hidden();
-        assertPanelPrefs2Shown();
-
-        pressBack();
-
-        // Only headers should be shown.
-        assertHeadersShown();
-        assertPanelPrefs1Hidden();
-        assertPanelPrefs2Hidden();
-
-        tapOnPrefs1Header();
-
-        // Only Prefs1 should be shown.
-        assertHeadersHidden();
-        assertPanelPrefs1Shown();
-        assertPanelPrefs2Hidden();
-
-        // Leave multi-window
-        mTestUtils.leaveMultiWindow(mActivity);
-
-        // Headers and Prefs1 should be shown.
-        assertHeadersShown();
-        assertPanelPrefs1Shown();
-        assertPanelPrefs2Hidden();
-
-        // Compare screenshots
-        Bitmap after = mTestUtils.takeScreenshot();
-        assertScreenshotsAreEqual(before, after);
-    }
-
     private void assertScreenshotsAreEqual(Bitmap before, Bitmap after) {
         assertTrue("Screenshots do not match!", BitmapUtils.compareBitmaps(before, after));
     }
@@ -690,7 +474,6 @@
             assertPanelPrefs1Hidden();
             assertPanelPrefs2Hidden();
         }
-
         assertHeadersAreLoaded();
     }
 
@@ -708,8 +491,6 @@
             assertPanelPrefs1Hidden();
             assertPanelPrefs2Shown();
         }
-
-
     }
 
     public boolean shouldRunLargeDeviceTest() {
@@ -739,12 +520,10 @@
     }
 
     private void assertHeadersAreLoaded() {
-        runOnUiThread(() -> {
-            assertEquals(EXPECTED_HEADERS_COUNT,
-                    mActivity.loadedHeaders == null
-                            ? 0
-                            : mActivity.loadedHeaders.size());
-        });
+        runOnUiThread(() -> assertEquals(EXPECTED_HEADERS_COUNT,
+                mActivity.loadedHeaders == null
+                        ? 0
+                        : mActivity.loadedHeaders.size()));
     }
 
     private void assertHeadersShown() {
@@ -822,9 +601,7 @@
     private void launchActivity() {
         mActivity = launchActivity(null);
         mTestUtils.device.waitForIdle();
-        runOnUiThread(() -> {
-            mIsMultiPane = mActivity.isMultiPane();
-        });
+        runOnUiThread(() -> mIsMultiPane = mActivity.isMultiPane());
     }
 
     private void launchActivityWithExtras(Class extraFragment, boolean noHeaders,
@@ -843,9 +620,7 @@
 
         mActivity = launchActivity(intent);
         mTestUtils.device.waitForIdle();
-        runOnUiThread(() -> {
-            mIsMultiPane = mActivity.isMultiPane();
-        });
+        runOnUiThread(() -> mIsMultiPane = mActivity.isMultiPane());
     }
 
     protected abstract PreferenceWithHeaders launchActivity(Intent intent);
diff --git a/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityLegacyFlowTest.java b/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityLegacyFlowTest.java
index b3d8b8d..46863cb 100644
--- a/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityLegacyFlowTest.java
+++ b/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityLegacyFlowTest.java
@@ -84,44 +84,6 @@
         assertScreenshotsAreEqual(before, after);
     }
 
-    /**
-     * Scenario: Tests that the activity still shows the preference screen even after multi-window
-     * is entered.
-     */
-    @Test
-    public void legacyActivityMultiWindowTest() {
-        waitForIdle();
-
-        mTestUtils.enterMultiWindow(mActivity);
-        mTestUtils.getMultiWindowFocus(mActivity);
-
-        // Prefs list should be shown.
-        assertTextShown(LEGACY_SCREEN_TEXT);
-
-        mTestUtils.leaveMultiWindow(mActivity);
-
-        // Prefs list should be shown.
-        assertTextShown(LEGACY_SCREEN_TEXT);
-    }
-
-    /**
-     * Scenario: Tests that the activity correctly restores its state after multi-window changes
-     * in legacy mode.
-     */
-    @Test
-    public void legacyActivityMultiWindowToggleTest() {
-        waitForIdle();
-
-        Bitmap before = mTestUtils.takeScreenshot();
-
-        mTestUtils.enterMultiWindow(mActivity);
-        mTestUtils.leaveMultiWindow(mActivity);
-
-        // Compare screenshots
-        Bitmap after = mTestUtils.takeScreenshot();
-        assertScreenshotsAreEqual(before, after);
-    }
-
     private void recreate() {
         runOnUiThread(() -> mActivity.recreate());
         SystemClock.sleep(1000);
@@ -130,9 +92,7 @@
 
     private void runOnUiThread(final Runnable runnable) {
         try {
-            mActivityRule.runOnUiThread(() -> {
-                runnable.run();
-            });
+            mActivityRule.runOnUiThread(() -> runnable.run());
         } catch (Throwable ex) {
             throw new RuntimeException("Failure on the UI thread", ex);
         }
diff --git a/tests/tests/preference2/src/android/preference2/cts/TestUtils.java b/tests/tests/preference2/src/android/preference2/cts/TestUtils.java
index e00e658..568b6f9 100644
--- a/tests/tests/preference2/src/android/preference2/cts/TestUtils.java
+++ b/tests/tests/preference2/src/android/preference2/cts/TestUtils.java
@@ -16,26 +16,17 @@
 
 package android.preference2.cts;
 
-import android.app.Activity;
 import android.app.Instrumentation;
 import android.app.UiAutomation;
 import android.app.UiModeManager;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
-import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiObjectNotFoundException;
-import android.support.test.uiautomator.UiScrollable;
-import android.support.test.uiautomator.UiSelector;
-
-import com.android.compatibility.common.util.SystemUtil;
-
-import java.io.IOException;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
 
 /**
  * Collection of helper utils for testing preferences.
@@ -43,19 +34,24 @@
 public class TestUtils {
 
     final UiDevice device;
+
+    private final Context mContext;
     private final Instrumentation mInstrumentation;
+    private final String mPackageName;
     private final UiAutomation mAutomation;
     private int mStatusBarHeight = -1;
     private int mNavigationBarHeight = -1;
 
     TestUtils() {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mContext = mInstrumentation.getTargetContext();
+        mPackageName = mContext.getPackageName();
         device = UiDevice.getInstance(mInstrumentation);
         mAutomation = mInstrumentation.getUiAutomation();
     }
 
     Bitmap takeScreenshot() {
-        // Only take screenshot once the screen is stable enough.
+        // Only take a screenshot once the screen is stable enough.
         device.waitForIdle();
 
         Bitmap bt = mAutomation.takeScreenshot();
@@ -66,8 +62,8 @@
         // Crop-out the navigation bar to avoid flakiness with button animations.
         int navigationBarHeight = getNavigationBarHeight();
 
-        // Crop the right side for scrollbar which might or might not be visible. On wearable
-        // devices the scroll bar is a curve and occupies 20% of the right side.
+        // Crop-out the right side for the scrollbar which may or may not be visible.
+        // On wearable devices the scroll bar is a curve and occupies 20% of the right side.
         int xToCut = isOnWatchUiMode() ? bt.getWidth() / 5 : bt.getWidth() / 20;
 
         bt = Bitmap.createBitmap(
@@ -77,142 +73,45 @@
         return bt;
     }
 
-    void tapOnViewWithText(String searchText) {
-        if (searchText == null) {
-            return;
+    void tapOnViewWithText(String text) {
+        UiObject2 object = getTextObject(text);
+        if (object == null) {
+            throw new AssertionError("View with text '" + text + "' was not found!");
         }
-
-        try {
-            // If the current UI has shown text, just click on it.
-            UiObject text = new UiObject(new UiSelector().text(searchText));
-            if (text.exists() || text.waitForExists(1000)) {
-                text.click();
-                return;
-            }
-
-            // Otherwise, if it is scrollable, scroll to where the text is and tap.
-            UiScrollable textScroll = new UiScrollable(new UiSelector().scrollable(true));
-
-            textScroll.scrollIntoView(new UiSelector().text(searchText));
-            text = new UiObject(new UiSelector().text(searchText));
-            text.click();
-        } catch (UiObjectNotFoundException e) {
-            throw new AssertionError("View with text '" + searchText + "' was not found!", e);
-        }
+        object.click();
     }
 
-    boolean isTextShown(String searchText) {
-        if (searchText == null) {
-            return false;
-        }
-
-        UiObject text = new UiObject(new UiSelector().text(searchText));
-        if (text.exists() || text.waitForExists(1000)) {
-            return true;
-        }
-
-        UiScrollable textScroll = new UiScrollable(new UiSelector().scrollable(true));
-        try {
-            return textScroll.scrollIntoView(new UiSelector().text(searchText));
-        } catch (UiObjectNotFoundException e) {
-            return false;
-        }
+    boolean isTextShown(String text) {
+        return getTextObject(text) != null;
     }
 
     boolean isTextHidden(String text) {
-        UiObject obj = device.findObject(new UiSelector().textMatches(text));
-        if (!obj.exists()) {
-            return true;
-        }
-        return obj.waitUntilGone(1000);
+        return getTextObject(text) == null;
     }
 
     boolean isTextFocused(String text) {
-        UiObject obj = device.findObject(new UiSelector().textMatches(text));
-        try {
-            return obj.isFocused();
-        } catch (UiObjectNotFoundException e) {
-            return false;
-        }
+        UiObject2 object = getTextObject(text);
+        return object != null && object.isFocused();
     }
 
     boolean isOnWatchUiMode() {
-        Context context = mInstrumentation.getTargetContext();
-        UiModeManager uiModeManager = context.getSystemService(UiModeManager.class);
+        UiModeManager uiModeManager = mContext.getSystemService(UiModeManager.class);
         return uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_WATCH;
     }
 
-    void getMultiWindowFocus(Context context) {
-        // Get window focus (otherwise back press would close multi-window instead of firing to the
-        // Activity and also the automator would fail to find objects on the screen.
-        // We want to click slightly below status bar in the 1/3 of width of the screen.
-        int x = device.getDisplayWidth() / 3;
-        int resourceId =
-                context.getResources().getIdentifier("status_bar_height", "dimen", "android");
-        int statusBarHeight =
-                (resourceId > 0) ? context.getResources().getDimensionPixelSize(resourceId) : 0;
-        device.click(x, 2 * statusBarHeight);
-    }
-
-    // Multi-window helpers taken from ActivityManagerDockedStackTests.java
-
-    void enterMultiWindow(Activity activity) {
-        try {
-            int id = getActivityTaskId(activity);
-            runShellCommand("am stack move-task " + id + " 3 true");
-        } catch (IOException e) {
-            throw new RuntimeException("Failed to get activity task id!", e);
-        }
-        SystemClock.sleep(5000);
-    }
-
-    void leaveMultiWindow(Activity activity) {
-        try {
-            int id = getActivityTaskId(activity);
-            runShellCommand("am stack move-task " + id + " 1 true");
-        } catch (IOException e) {
-            throw new RuntimeException("Failed to get activity task id!", e);
-        }
-        SystemClock.sleep(5000);
-    }
-
-    private int getActivityTaskId(Activity activity) throws IOException {
-        // Taken from ActivityManagerTestBase.java
-        final String output = runShellCommand("am stack list");
-        final Pattern activityPattern =
-                Pattern.compile("(.*) " + getWindowName(activity) + " (.*)");
-        for (String line : output.split("\\n")) {
-            Matcher matcher = activityPattern.matcher(line);
-            if (matcher.matches()) {
-                for (String word : line.split("\\s+")) {
-                    if (word.startsWith("taskId")) {
-                        final String withColon = word.split("=")[1];
-                        return Integer.parseInt(withColon.substring(0, withColon.length() - 1));
-                    }
-                }
-            }
-        }
-        return -1;
-    }
-
-    private String getWindowName(Activity activity) {
-        String componentName = activity.getPackageName();
-        String baseWindowName = componentName + "/" + componentName + ".";
-        return baseWindowName + activity.getClass().getSimpleName();
-    }
-
     private int getStatusBarHeight() {
         // Cache the result to keep it fast.
         if (mStatusBarHeight >= 0) {
             return mStatusBarHeight;
         }
 
-        mStatusBarHeight = 0;
         int resourceId = mInstrumentation.getTargetContext().getResources()
                 .getIdentifier("status_bar_height", "dimen", "android");
         if (resourceId > 0) {
             mStatusBarHeight = mInstrumentation.getTargetContext().getResources()
                     .getDimensionPixelSize(resourceId);
+        } else {
+            mStatusBarHeight = 0;
         }
         return mStatusBarHeight;
     }
@@ -223,21 +122,19 @@
             return mNavigationBarHeight;
         }
 
-        mNavigationBarHeight = 0;
         int resourceId = mInstrumentation.getTargetContext().getResources()
                 .getIdentifier("navigation_bar_height", "dimen", "android");
         if (resourceId > 0) {
             mNavigationBarHeight = mInstrumentation.getTargetContext().getResources()
                     .getDimensionPixelSize(resourceId);
+        } else {
+            mNavigationBarHeight = 0;
         }
         return mNavigationBarHeight;
     }
 
-    private String runShellCommand(String cmd) {
-        try {
-            return SystemUtil.runShellCommand(mInstrumentation, cmd);
-        } catch (IOException e) {
-            throw new RuntimeException("Failed to run command: " + cmd, e);
-        }
+    private UiObject2 getTextObject(String text) {
+        // Wait for up to 1 second to find the object. Returns null if the object cannot be found.
+        return device.wait(Until.findObject(By.text(text).pkg(mPackageName)), 1000);
     }
 }
diff --git a/tests/tests/print/AndroidTest.xml b/tests/tests/print/AndroidTest.xml
index ffcaf65..8572398 100644
--- a/tests/tests/print/AndroidTest.xml
+++ b/tests/tests/print/AndroidTest.xml
@@ -17,12 +17,24 @@
     <option name="test-suite-tag" value="cts" />
     <option name="not-shardable" value="true" />
     <option name="config-descriptor:metadata" key="component" value="print" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
 
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="cmd print set-bind-instant-service-allowed true" />
         <option name="teardown-command" value="cmd print set-bind-instant-service-allowed false" />
     </target_preparer>
 
+    <!-- Create place to store apks -->
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="mkdir -p /data/local/tmp/cts/print" />
+        <option name="teardown-command" value="rm -rf /data/local/tmp/cts"/>
+    </target_preparer>
+
+    <!-- Load external print service APK onto device -->
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="push" value="CtsExternalPrintService.apk->/data/local/tmp/cts/print/CtsExternalPrintService.apk" />
+    </target_preparer>
+
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsPrintTestCases.apk" />
diff --git a/tests/tests/uirendering/Android.mk b/tests/tests/print/ExternalPrintService/Android.mk
similarity index 64%
rename from tests/tests/uirendering/Android.mk
rename to tests/tests/print/ExternalPrintService/Android.mk
index ec98a0e..4cee3b7 100644
--- a/tests/tests/uirendering/Android.mk
+++ b/tests/tests/print/ExternalPrintService/Android.mk
@@ -1,4 +1,5 @@
-# Copyright (C) 2014 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.
@@ -11,33 +12,23 @@
 # WITHOUT 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
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-LOCAL_JAVA_LIBRARIES := android.test.runner.stubs
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    compatibility-device-util \
-    ctsdeviceutillegacy \
-    ctstestrunner \
-    mockito-target-minus-junit4 \
-    android-support-test
-
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_JAVA_LIBRARIES := junit
 
-LOCAL_PACKAGE_NAME := CtsUiRenderingTestCases
-
-# Enforce public / test api only
-LOCAL_SDK_VERSION := test_current
+LOCAL_PACKAGE_NAME := CtsExternalPrintService
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/print/ExternalPrintService/AndroidManifest.xml b/tests/tests/print/ExternalPrintService/AndroidManifest.xml
new file mode 100644
index 0000000..ff480c1
--- /dev/null
+++ b/tests/tests/print/ExternalPrintService/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<!--
+  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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.print.cts.externalservice" >
+
+    <application android:allowBackup="false" >
+        <service
+            android:name=".ExternalService"
+            android:permission="android.permission.BIND_PRINT_SERVICE">
+          <intent-filter>
+              <action android:name="android.printservice.PrintService" />
+          </intent-filter>
+
+          <meta-data
+              android:name="android.printservice"
+              android:resource="@xml/printservice">
+          </meta-data>
+        </service>
+    </application>
+</manifest>
diff --git a/tests/tests/print/ExternalPrintService/res/xml/printservice.xml b/tests/tests/print/ExternalPrintService/res/xml/printservice.xml
new file mode 100644
index 0000000..a470cc7
--- /dev/null
+++ b/tests/tests/print/ExternalPrintService/res/xml/printservice.xml
@@ -0,0 +1,17 @@
+<!--
+  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.
+  -->
+
+<print-service />
diff --git a/tests/tests/print/ExternalPrintService/src/android/print/cts/externalservice/ExternalService.java b/tests/tests/print/ExternalPrintService/src/android/print/cts/externalservice/ExternalService.java
new file mode 100644
index 0000000..d50f15e
--- /dev/null
+++ b/tests/tests/print/ExternalPrintService/src/android/print/cts/externalservice/ExternalService.java
@@ -0,0 +1,81 @@
+/*
+ * 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.print.cts.externalservice;
+
+import static android.print.PrinterInfo.STATUS_IDLE;
+
+import static org.junit.Assert.fail;
+
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import android.printservice.PrintJob;
+import android.printservice.PrintService;
+import android.printservice.PrinterDiscoverySession;
+
+import java.util.Collections;
+import java.util.List;
+
+public class ExternalService extends PrintService {
+    private static final String PRINTER_NAME = "ExternalServicePrinter";
+
+    @Override
+    protected PrinterDiscoverySession onCreatePrinterDiscoverySession() {
+        return new PrinterDiscoverySession() {
+            @Override
+            public void onStartPrinterDiscovery(List<PrinterId> priorityList) {
+                addPrinters(Collections.singletonList(
+                        (new PrinterInfo.Builder(generatePrinterId(PRINTER_NAME), PRINTER_NAME,
+                                STATUS_IDLE)).build()));
+            }
+
+            @Override
+            public void onStopPrinterDiscovery() {
+                // empty
+            }
+
+            @Override
+            public void onValidatePrinters(List<PrinterId> printerIds) {
+                // empty
+            }
+
+            @Override
+            public void onStartPrinterStateTracking(PrinterId printerId) {
+                // empty
+            }
+
+            @Override
+            public void onStopPrinterStateTracking(PrinterId printerId) {
+                // empty
+            }
+
+            @Override
+            public void onDestroy() {
+                // empty
+            }
+        };
+    }
+
+    @Override
+    protected void onRequestCancelPrintJob(PrintJob printJob) {
+        fail("This service does not support printing");
+    }
+
+    @Override
+    protected void onPrintJobQueued(PrintJob printJob) {
+        fail("This service does not support printing");
+    }
+}
diff --git a/tests/tests/print/printTestUtilLib/Android.mk b/tests/tests/print/printTestUtilLib/Android.mk
index 358861b..4ed8ab6 100644
--- a/tests/tests/print/printTestUtilLib/Android.mk
+++ b/tests/tests/print/printTestUtilLib/Android.mk
@@ -22,7 +22,12 @@
 
 LOCAL_MODULE := print-test-util-lib
 
-LOCAL_STATIC_JAVA_LIBRARIES := mockito-target-minus-junit4 ctstestrunner ub-uiautomator compatibility-device-util android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target-minus-junit4 \
+                               ctstestrunner \
+                               ub-uiautomator \
+                               compatibility-device-util \
+                               android-support-test \
+                               platformprotosnano
 
 LOCAL_SDK_VERSION := test_current
 
diff --git a/tests/tests/print/printTestUtilLib/src/android/print/test/BasePrintTest.java b/tests/tests/print/printTestUtilLib/src/android/print/test/BasePrintTest.java
index 5c40dfc..048768f 100755
--- a/tests/tests/print/printTestUtilLib/src/android/print/test/BasePrintTest.java
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/BasePrintTest.java
@@ -64,8 +64,10 @@
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObject2;
 import android.support.test.uiautomator.UiObjectNotFoundException;
 import android.support.test.uiautomator.UiSelector;
+import android.support.test.uiautomator.Until;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -99,6 +101,7 @@
 import java.lang.annotation.Target;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -189,7 +192,7 @@
         sEnabledImes = null;
     }
 
-    protected static Instrumentation getInstrumentation() {
+    public static Instrumentation getInstrumentation() {
         return InstrumentationRegistry.getInstrumentation();
     }
 
@@ -205,7 +208,8 @@
         Log.d(LOG_TAG, "disableImes()");
         disableImes();
         Log.d(LOG_TAG, "disablePrintServices()");
-        disablePrintServices(instrumentation.getTargetContext().getPackageName());
+        sDisabledPrintServicesBefore = disablePrintServices(instrumentation.getTargetContext()
+                .getPackageName());
 
         // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2
         // Dexmaker is used by mockito.
@@ -219,12 +223,14 @@
      * Disable all print services beside the ones we want to leave enabled.
      *
      * @param packageToLeaveEnabled The package of the services to leave enabled.
+     *
+     * @return Services that were enabled before this method was called
      */
-    private static void disablePrintServices(@NonNull String packageToLeaveEnabled)
+    protected static @NonNull String disablePrintServices(@Nullable String packageToLeaveEnabled)
             throws IOException {
         Instrumentation instrumentation = getInstrumentation();
 
-        sDisabledPrintServicesBefore = SystemUtil.runShellCommand(instrumentation,
+        String previousEnabledServices = SystemUtil.runShellCommand(instrumentation,
                 "settings get secure " + Settings.Secure.DISABLED_PRINT_SERVICES);
 
         Intent printServiceIntent = new Intent(android.printservice.PrintService.SERVICE_INTERFACE);
@@ -233,7 +239,8 @@
 
         StringBuilder builder = new StringBuilder();
         for (ResolveInfo service : installedServices) {
-            if (packageToLeaveEnabled.equals(service.serviceInfo.packageName)) {
+            if (packageToLeaveEnabled != null
+                    && packageToLeaveEnabled.equals(service.serviceInfo.packageName)) {
                 continue;
             }
             if (builder.length() > 0) {
@@ -245,15 +252,19 @@
 
         SystemUtil.runShellCommand(instrumentation, "settings put secure "
                 + Settings.Secure.DISABLED_PRINT_SERVICES + " " + builder);
+
+        return previousEnabledServices;
     }
 
     /**
      * Revert {@link #disablePrintServices(String)}
+     *
+     * @param servicesToEnable Services that should be enabled
      */
-    private static  void enablePrintServices() throws IOException {
+    protected static void enablePrintServices(@NonNull String servicesToEnable) throws IOException {
         SystemUtil.runShellCommand(getInstrumentation(),
                 "settings put secure " + Settings.Secure.DISABLED_PRINT_SERVICES + " "
-                        + sDisabledPrintServicesBefore);
+                        + servicesToEnable);
     }
 
     @Before
@@ -315,7 +326,7 @@
         Instrumentation instrumentation = getInstrumentation();
 
         Log.d(LOG_TAG, "enablePrintServices()");
-        enablePrintServices();
+        enablePrintServices(sDisabledPrintServicesBefore);
 
         Log.d(LOG_TAG, "enableImes()");
         enableImes();
@@ -551,62 +562,55 @@
     protected void waitForPrinterUnavailable() throws Exception {
         final String printerUnavailableMessage = "This printer isn\'t available right now.";
 
-        UiObject message = getUiDevice().findObject(new UiSelector().resourceId(
-                "com.android.printspooler:id/message"));
+        UiObject2 message = getUiDevice().wait(Until.findObject(
+                By.res("com.android.printspooler:id/message")), OPERATION_TIMEOUT_MILLIS);
+
+        if (message == null) {
+            dumpWindowHierarchy();
+            throw new UiObjectNotFoundException("Cannot find " + printerUnavailableMessage);
+        }
         if (!message.getText().equals(printerUnavailableMessage)) {
             throw new Exception("Wrong message: " + message.getText() + " instead of "
                     + printerUnavailableMessage);
         }
     }
 
-    protected void selectPrinter(String printerName) throws UiObjectNotFoundException, IOException {
+    protected void selectPrinter(String printerName) throws IOException, UiObjectNotFoundException {
+        selectPrinter(printerName, OPERATION_TIMEOUT_MILLIS);
+    }
+
+    protected void selectPrinter(String printerName, long timeout) throws IOException,
+            UiObjectNotFoundException {
         try {
-            long delay = 1;
-            while (true) {
-                try {
-                    UiDevice uiDevice = getUiDevice();
-                    UiObject destinationSpinner = uiDevice.findObject(new UiSelector()
-                            .resourceId("com.android.printspooler:id/destination_spinner"));
+            UiDevice uiDevice = getUiDevice();
+            UiObject2 destinationSpinner = uiDevice.wait(Until.findObject(
+                    By.res("com.android.printspooler:id/destination_spinner")), timeout);
 
-                    destinationSpinner.click();
-                    getUiDevice().waitForIdle();
-
-                    // Give spinner some time to expand
-                    try {
-                        Thread.sleep(delay);
-                    } catch (InterruptedException e) {
-                        // ignore
-                    }
-
-                    // try to select printer
-                    UiObject printerOption = uiDevice.findObject(
-                            new UiSelector().text(printerName));
-                    printerOption.click();
-                } catch (UiObjectNotFoundException e) {
-                    Log.e(LOG_TAG, "Could not select printer " + printerName, e);
-                }
-
+            if (destinationSpinner != null) {
+                destinationSpinner.click();
                 getUiDevice().waitForIdle();
-
-                if (!printerName.equals("All printers…")) {
-                    // Make sure printer is selected
-                    if (getUiDevice().hasObject(By.text(printerName))) {
-                        break;
-                    } else {
-                        if (delay <= OPERATION_TIMEOUT_MILLIS) {
-                            Log.w(LOG_TAG, "Cannot find printer " + printerName + ", retrying.");
-                            delay *= 2;
-                        } else {
-                            throw new UiObjectNotFoundException(
-                                    "Could find printer " + printerName
-                                            + " even though we retried");
-                        }
-                    }
-                } else {
-                    break;
-                }
             }
-        } catch (UiObjectNotFoundException e) {
+
+            selectPrinterSpinnerOpen(printerName, timeout);
+        } catch (Exception e) {
+            dumpWindowHierarchy();
+            throw e;
+        }
+    }
+
+    protected void selectPrinterSpinnerOpen(String printerName, long timeout)
+            throws IOException, UiObjectNotFoundException {
+        try {
+            UiDevice uiDevice = getUiDevice();
+            UiObject2 printerOption = uiDevice.wait(Until.findObject(By.text(printerName)),
+                    timeout);
+            if (printerOption == null) {
+                throw new UiObjectNotFoundException(printerName + " not found");
+            }
+
+            printerOption.click();
+            getUiDevice().waitForIdle();
+        } catch (Exception e) {
             dumpWindowHierarchy();
             throw e;
         }
@@ -728,14 +732,15 @@
     }
 
     public void clickPrintButton() throws UiObjectNotFoundException, IOException {
-        try {
-            UiObject printButton = getUiDevice().findObject(new UiSelector().resourceId(
-                    "com.android.printspooler:id/print_button"));
-            printButton.click();
-        } catch (UiObjectNotFoundException e) {
+        getUiDevice().waitForIdle();
+
+        UiObject2 printButton = getUiDevice().wait(Until.findObject(By.res(
+                "com.android.printspooler:id/print_button")), OPERATION_TIMEOUT_MILLIS);
+        if (printButton == null) {
             dumpWindowHierarchy();
-            throw e;
+            throw new UiObjectNotFoundException("print button not found");
         }
+        printButton.click();
     }
 
     protected void clickRetryButton() throws UiObjectNotFoundException, IOException {
@@ -864,9 +869,7 @@
 
                     callback.onLayoutFinished(new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
                             .setPageCount(numPages).build(),
-                            !oldAttributes.equals(printAttributes[0]));
-
-                    oldAttributes = printAttributes[0];
+                            !Objects.equals(oldAttributes, printAttributes[0]));
 
                     onLayoutCalled();
                     return null;
diff --git a/tests/tests/print/printTestUtilLib/src/android/print/test/Utils.java b/tests/tests/print/printTestUtilLib/src/android/print/test/Utils.java
index 7e40bad..b7f9fe6 100644
--- a/tests/tests/print/printTestUtilLib/src/android/print/test/Utils.java
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/Utils.java
@@ -16,14 +16,23 @@
 
 package android.print.test;
 
+import static android.print.test.BasePrintTest.getInstrumentation;
+
 import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.ParcelFileDescriptor;
 import android.print.PrintJob;
 import android.print.PrintManager;
-import androidx.annotation.NonNull;
+import android.service.print.nano.PrintServiceDumpProto;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.util.concurrent.TimeUnit;
+
 /**
  * Utilities for print tests
  */
@@ -97,16 +106,18 @@
      * Make sure that a {@link Invokable} eventually finishes without throwing a {@link Throwable}.
      *
      * @param r The {@link Invokable} to run.
+     * @param timeout the maximum time to wait
      */
-    public static void eventually(@NonNull Invokable r) throws Throwable {
-        long start = System.currentTimeMillis();
+    public static void eventually(@NonNull Invokable r, long timeout) throws Throwable {
+        long start = System.nanoTime();
 
         while (true) {
             try {
                 r.run();
                 break;
             } catch (Throwable e) {
-                if (System.currentTimeMillis() - start < BasePrintTest.OPERATION_TIMEOUT_MILLIS) {
+                if (System.nanoTime() - start < TimeUnit.NANOSECONDS.convert(timeout,
+                        TimeUnit.MILLISECONDS)) {
                     Log.e(LOG_TAG, "Ignoring exception", e);
 
                     try {
@@ -122,6 +133,15 @@
     }
 
     /**
+     * Make sure that a {@link Invokable} eventually finishes without throwing a {@link Throwable}.
+     *
+     * @param r The {@link Invokable} to run.
+     */
+    public static void eventually(@NonNull Invokable r) throws Throwable {
+        eventually(r, BasePrintTest.OPERATION_TIMEOUT_MILLIS);
+    }
+
+    /**
      * @param name Name of print job
      *
      * @return The print job for the name
@@ -145,4 +165,30 @@
     public static @NonNull PrintManager getPrintManager(@NonNull Context context) {
         return (PrintManager) context.getSystemService(Context.PRINT_SERVICE);
     }
+
+    /**
+     * Get the {@link PrintServiceDumpProto}
+     */
+    public static PrintServiceDumpProto getProtoDump() throws Exception {
+        ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation()
+                .executeShellCommand("dumpsys print --proto");
+
+        try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+            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 PrintServiceDumpProto.parseFrom(os.toByteArray());
+        }
+    }
 }
diff --git a/tests/tests/print/printTestUtilLib/src/android/print/test/services/StubbablePrintService.java b/tests/tests/print/printTestUtilLib/src/android/print/test/services/StubbablePrintService.java
index 09d1f78..cc82d04 100644
--- a/tests/tests/print/printTestUtilLib/src/android/print/test/services/StubbablePrintService.java
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/services/StubbablePrintService.java
@@ -17,13 +17,16 @@
 package android.print.test.services;
 
 import android.content.Context;
+import android.print.PrinterId;
 import android.printservice.PrintJob;
 import android.printservice.PrintService;
 import android.printservice.PrinterDiscoverySession;
+import android.util.Log;
 
 import java.util.List;
 
 public abstract class StubbablePrintService extends PrintService {
+    private static final String LOG_TAG = StubbablePrintService.class.getSimpleName();
 
     @Override
     public PrinterDiscoverySession onCreatePrinterDiscoverySession() {
@@ -32,7 +35,39 @@
             return new StubbablePrinterDiscoverySession(this,
                     getCallbacks().onCreatePrinterDiscoverySessionCallbacks());
         }
-        return null;
+
+        Log.w(LOG_TAG, "onCreatePrinterDiscoverySession called but no callbacks are set up");
+        return new PrinterDiscoverySession() {
+            @Override
+            public void onStartPrinterDiscovery(List<PrinterId> priorityList) {
+                // empty
+            }
+
+            @Override
+            public void onStopPrinterDiscovery() {
+                // empty
+            }
+
+            @Override
+            public void onValidatePrinters(List<PrinterId> printerIds) {
+                // empty
+            }
+
+            @Override
+            public void onStartPrinterStateTracking(PrinterId printerId) {
+                // empty
+            }
+
+            @Override
+            public void onStopPrinterStateTracking(PrinterId printerId) {
+                // empty
+            }
+
+            @Override
+            public void onDestroy() {
+                // empty
+            }
+        };
     }
 
     @Override
diff --git a/tests/tests/print/src/android/print/cts/InstallBehavior.java b/tests/tests/print/src/android/print/cts/InstallBehavior.java
new file mode 100644
index 0000000..ea0f399
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/InstallBehavior.java
@@ -0,0 +1,91 @@
+/*
+ * 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.print.cts;
+
+import static org.junit.Assert.fail;
+
+import android.print.PrintManager;
+import android.print.test.BasePrintTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+
+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.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Behavior of Android when a print service is installed
+ */
+@RunWith(AndroidJUnit4.class)
+public class InstallBehavior extends BasePrintTest {
+    private static String sPreviousEnabledServices;
+
+    @BeforeClass
+    public static void disableAllPrintServices() throws Exception {
+        sPreviousEnabledServices = disablePrintServices(null);
+    }
+
+    @Before
+    @After
+    public void uninstallExternalPrintService() throws Exception {
+        SystemUtil.runShellCommand(getInstrumentation(),
+                "pm uninstall android.print.cts.externalservice");
+    }
+
+    @AfterClass
+    public static void reenablePrintServices() throws Exception {
+        enablePrintServices(sPreviousEnabledServices);
+    }
+
+    /**
+     * Printers from a newly installed print service should show up immediately.
+     *
+     * <p>This tests:
+     * <ul>
+     *     <li>Print services are enabled by default</li>
+     *     <li>Print services get added to already running printer discovery sessions</li>
+     * </ul>
+     */
+    @Test
+    public void installedServiceIsEnabled() throws Exception {
+        getActivity().getSystemService(PrintManager.class).print("printjob",
+                createDefaultPrintDocumentAdapter(1), null);
+
+        waitForWriteAdapterCallback(1);
+
+        // Printer should not be available
+        try {
+            selectPrinter("ExternalServicePrinter", 500);
+            fail();
+        } catch (UiObjectNotFoundException expected) {
+            // expected
+        }
+
+        SystemUtil.runShellCommand(getInstrumentation(),
+                "pm install /data/local/tmp/cts/print/CtsExternalPrintService.apk");
+
+        selectPrinterSpinnerOpen("ExternalServicePrinter", OPERATION_TIMEOUT_MILLIS);
+
+        // Exit print preview and thereby end printing
+        getUiDevice().pressBack();
+    }
+}
diff --git a/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java b/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java
index 706f971..4b45fea 100644
--- a/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java
@@ -1757,7 +1757,13 @@
                     // Mark layout called.
                     onLayoutCalled();
                     return null;
-                }, null, invocation -> {
+                }, invocation -> {
+                    WriteResultCallback callback = (WriteResultCallback) invocation
+                            .getArguments()[3];
+                    callback.onWriteFailed(null);
+                    onWriteCalled();
+                    return null;
+                }, invocation -> {
                     // Mark finish was called.
                     onFinishCalled();
                     return null;
@@ -1768,6 +1774,7 @@
 
         // Wait for layout.
         waitForLayoutAdapterCallbackCount(1);
+        waitForWriteAdapterCallback(1);
 
         // Cancel printing.
         getUiDevice().pressBack(); // wakes up the device.
@@ -1855,6 +1862,7 @@
 
         // Wait for layout.
         waitForLayoutAdapterCallbackCount(1);
+        waitForWriteAdapterCallback(1);
 
         // Cancel printing.
         getUiDevice().pressBack(); // wakes up the device.
diff --git a/tests/tests/print/src/android/print/cts/PrintDocumentInfoTest.java b/tests/tests/print/src/android/print/cts/PrintDocumentInfoTest.java
index 63e83e3..c8849fa 100644
--- a/tests/tests/print/src/android/print/cts/PrintDocumentInfoTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintDocumentInfoTest.java
@@ -107,9 +107,12 @@
                 invocation -> printerDiscoverySessionCallbacks,
                 invocation -> {
                     PrintJob printJob = (PrintJob) invocation.getArguments()[0];
-                    queuedInfo[0] = printJob.getDocument().getInfo();
-                    queuedData[0] = printJob.getDocument().getData();
+                    synchronized (queuedInfo) {
+                        queuedInfo[0] = printJob.getDocument().getInfo();
+                        queuedData[0] = printJob.getDocument().getData();
+                    }
                     printJob.complete();
+                    onPrintJobQueuedCalled();
                     return null;
                 }, null);
 
@@ -127,12 +130,15 @@
                     return null;
                 }, invocation -> {
                     Object[] args = invocation.getArguments();
-                    PageRange[] pages = (PageRange[]) args[0];
                     ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
                     WriteResultCallback callback = (WriteResultCallback) args[3];
                     writeBlankPages(printAttributes[0], fd, 0, 1);
                     fd.close();
-                    callback.onWriteFinished(pages);
+                    if (pageCount != null && pageCount > 0) {
+                        callback.onWriteFinished(new PageRange[]{new PageRange(0, pageCount - 1)});
+                    } else {
+                        callback.onWriteFinished(new PageRange[]{new PageRange(0, 1)});
+                    }
                     onWriteCalled();
                     return null;
                 }, invocation -> null);
@@ -148,9 +154,14 @@
 
         // Wait for the session to be destroyed to isolate tests.
         waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+        waitForServiceOnPrintJobQueuedCallbackCalled(1);
 
         // Check that the document name was carried over 1:1
-        eventually(() -> assertEquals(name, queuedInfo[0].getName()));
+        eventually(() -> {
+            synchronized (queuedInfo) {
+                assertEquals(name, queuedInfo[0].getName());
+            }
+        });
 
         // Content type is set to document by default, but is otherwise unrestricted
         if (contentType != null) {
@@ -191,7 +202,7 @@
      */
     @Test
     public void documentInfoNothingSet() throws Throwable {
-        printDocumentBaseTest(PRINT_JOB_NAME, null, null);
+        printDocumentBaseTest("documentInfoNothingSet", null, null);
     }
 
     /**
@@ -201,7 +212,8 @@
      */
     @Test
     public void documentInfoUnknownPageCount() throws Throwable {
-        printDocumentBaseTest(PRINT_JOB_NAME, null, PrintDocumentInfo.PAGE_COUNT_UNKNOWN);
+        printDocumentBaseTest("documentInfoUnknownPageCount", null,
+                PrintDocumentInfo.PAGE_COUNT_UNKNOWN);
     }
 
     /**
@@ -211,7 +223,7 @@
      */
     @Test
     public void documentInfoZeroPageCount() throws Throwable {
-        printDocumentBaseTest(PRINT_JOB_NAME, null, 0);
+        printDocumentBaseTest("documentInfoZeroPageCount", null, 0);
     }
 
     /**
@@ -221,7 +233,7 @@
      */
     @Test
     public void documentInfoOnePageCount() throws Throwable {
-        printDocumentBaseTest(PRINT_JOB_NAME, null, 1);
+        printDocumentBaseTest("documentInfoOnePageCount", null, 1);
     }
 
     /**
@@ -231,7 +243,7 @@
      */
     @Test
     public void documentInfoThreePageCount() throws Throwable {
-        printDocumentBaseTest(PRINT_JOB_NAME, null, 3);
+        printDocumentBaseTest("documentInfoThreePageCount", null, 3);
     }
 
     /**
@@ -241,7 +253,8 @@
      */
     @Test
     public void documentInfoContentTypePhoto() throws Throwable {
-        printDocumentBaseTest(PRINT_JOB_NAME, PrintDocumentInfo.CONTENT_TYPE_PHOTO, null);
+        printDocumentBaseTest("documentInfoContentTypePhoto", PrintDocumentInfo.CONTENT_TYPE_PHOTO,
+                null);
     }
 
     /**
@@ -251,7 +264,8 @@
      */
     @Test
     public void documentInfoContentTypeUnknown() throws Throwable {
-        printDocumentBaseTest(PRINT_JOB_NAME, PrintDocumentInfo.CONTENT_TYPE_UNKNOWN, null);
+        printDocumentBaseTest("documentInfoContentTypeUnknown",
+                PrintDocumentInfo.CONTENT_TYPE_UNKNOWN, null);
     }
 
     /**
@@ -261,7 +275,7 @@
      */
     @Test
     public void documentInfoContentTypeNonDefined() throws Throwable {
-        printDocumentBaseTest(PRINT_JOB_NAME, -23, null);
+        printDocumentBaseTest("documentInfoContentTypeNonDefined", -23, null);
     }
 
     private PrinterDiscoverySessionCallbacks createFirstMockDiscoverySessionCallbacks() {
diff --git a/tests/tests/print/src/android/print/cts/PrintJobTest.java b/tests/tests/print/src/android/print/cts/PrintJobTest.java
index 2ab52b3..4f99f7a 100644
--- a/tests/tests/print/src/android/print/cts/PrintJobTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintJobTest.java
@@ -147,8 +147,7 @@
      *
      * @throws Exception If anything is unexpected.
      */
-    private void baseTest(PrintJobTestFn testFn)
-            throws Exception {
+    private void baseTest(PrintJobTestFn testFn) throws Throwable {
         testSuccess[0] = false;
 
         // Create the session of the printers that we will be checking.
@@ -168,12 +167,17 @@
         // Create a print adapter that respects the print contract.
         PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
 
+
         // Start printing.
         print(adapter);
-        clickPrintButton();
+        waitForWriteAdapterCallback(1);
 
-        // Wait for print job to be queued
-        waitForServiceOnPrintJobQueuedCallbackCalled(1);
+        eventually(() -> {
+            clickPrintButton();
+
+            // Wait for print job to be queued
+            waitForServiceOnPrintJobQueuedCallbackCalled(1);
+        }, OPERATION_TIMEOUT_MILLIS * 2);
 
         // Wait for discovery session to be destroyed to isolate tests from each other
         waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
@@ -239,7 +243,7 @@
     }
 
     @Test
-    public void blockWithReason() throws Exception {
+    public void blockWithReason() throws Throwable {
         baseTest(printJob -> {
             printJob.start();
             checkState(printJob, PrintJobInfo.STATE_STARTED);
@@ -267,7 +271,7 @@
     }
 
     @Test
-    public void failWithReason() throws Exception {
+    public void failWithReason() throws Throwable {
         baseTest(printJob -> {
             printJob.start();
             checkState(printJob, PrintJobInfo.STATE_STARTED);
@@ -287,7 +291,7 @@
     }
 
     @Test
-    public void tag() throws Exception {
+    public void tag() throws Throwable {
         baseTest(printJob -> {
             // Default value should be null
             assertNull(printJob.getTag());
@@ -451,7 +455,7 @@
     }
 
     @Test
-    public void other() throws Exception {
+    public void other() throws Throwable {
         baseTest(printJob -> {
             assertNotNull(printJob.getDocument());
             assertNotNull(printJob.getId());
@@ -459,7 +463,7 @@
     }
 
     @Test
-    public void setStatus() throws Exception {
+    public void setStatus() throws Throwable {
         baseTest(printJob -> {
             printJob.start();
 
diff --git a/tests/tests/print/src/android/print/cts/PrintServicesTest.java b/tests/tests/print/src/android/print/cts/PrintServicesTest.java
index b5a23e2..86a4e3f 100644
--- a/tests/tests/print/src/android/print/cts/PrintServicesTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintServicesTest.java
@@ -60,9 +60,13 @@
 import android.printservice.PrintJob;
 import android.printservice.PrintService;
 import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject;
 import android.support.test.uiautomator.UiSelector;
+import android.support.test.uiautomator.Until;
+
+import androidx.annotation.NonNull;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -86,6 +90,16 @@
     /** The custom printer icon to use */
     private Icon mIcon;
 
+    private @NonNull PrinterCapabilitiesInfo getDefaultOptionPrinterCapabilites(
+            @NonNull PrinterId printerId) {
+        return new PrinterCapabilitiesInfo.Builder(printerId)
+                .setMinMargins(new Margins(200, 200, 200, 200))
+                .addMediaSize(MediaSize.ISO_A4, true)
+                .addResolution(new Resolution("300x300", "300x300", 300, 300), true)
+                .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+                        PrintAttributes.COLOR_MODE_COLOR).build();
+    }
+
     /**
      * Create a mock {@link PrinterDiscoverySessionCallbacks} that discovers a single printer with
      * minimal capabilities.
@@ -93,7 +107,7 @@
      * @return The mock session callbacks
      */
     private PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks(
-            String printerName, ArrayList<String> trackedPrinters) {
+            String printerName) {
         return createMockPrinterDiscoverySessionCallbacks(invocation -> {
             // Get the session.
             StubbablePrinterDiscoverySession session =
@@ -106,25 +120,16 @@
                 PrinterId printerId = session.getService()
                         .generatePrinterId(printerName);
 
-                PrinterCapabilitiesInfo capabilities = new PrinterCapabilitiesInfo.Builder(
-                        printerId)
-                        .setMinMargins(new Margins(200, 200, 200, 200))
-                        .addMediaSize(MediaSize.ISO_A4, true)
-                        .addResolution(new Resolution("300x300", "300x300", 300, 300),
-                                true)
-                        .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
-                                PrintAttributes.COLOR_MODE_COLOR)
-                        .build();
-
                 Intent infoIntent = new Intent(getActivity(), InfoActivity.class);
                 infoIntent.putExtra("PRINTER_NAME", PRINTER_NAME);
 
-                PendingIntent infoPendingIntent = PendingIntent.getActivity(getActivity(), 0,
+                PendingIntent infoPendingIntent = PendingIntent.getActivity(getActivity(),
+                        0,
                         infoIntent, PendingIntent.FLAG_UPDATE_CURRENT);
 
                 sPrinter = new PrinterInfo.Builder(printerId, printerName,
                         PrinterInfo.STATUS_IDLE)
-                        .setCapabilities(capabilities)
+                        .setCapabilities(getDefaultOptionPrinterCapabilites(printerId))
                         .setDescription("Minimal capabilities")
                         .setInfoIntent(infoPendingIntent)
                         .build();
@@ -136,16 +141,7 @@
             onPrinterDiscoverySessionCreateCalled();
 
             return null;
-        }, null, null, invocation -> {
-            if (trackedPrinters != null) {
-                synchronized (trackedPrinters) {
-                    trackedPrinters
-                            .add(((PrinterId) invocation.getArguments()[0]).getLocalId());
-                    trackedPrinters.notifyAll();
-                }
-            }
-            return null;
-        }, invocation -> {
+        }, null, null, null, invocation -> {
             CustomPrinterIconCallback callback = (CustomPrinterIconCallback) invocation
                     .getArguments()[2];
 
@@ -153,16 +149,7 @@
                 callback.onCustomPrinterIconLoaded(mIcon);
             }
             return null;
-        }, invocation -> {
-            if (trackedPrinters != null) {
-                synchronized (trackedPrinters) {
-                    trackedPrinters.remove(((PrinterId) invocation.getArguments()[0]).getLocalId());
-                    trackedPrinters.notifyAll();
-                }
-            }
-
-            return null;
-        }, invocation -> {
+        }, null, invocation -> {
             // Take a note onDestroy was called.
             onPrinterDiscoverySessionDestroyCalled();
             return null;
@@ -276,8 +263,8 @@
     @Test
     public void progress() throws Throwable {
         // Create the session callbacks that we will be checking.
-        PrinterDiscoverySessionCallbacks sessionCallbacks
-                = createMockPrinterDiscoverySessionCallbacks(PRINTER_NAME, null);
+        PrinterDiscoverySessionCallbacks sessionCallbacks =
+                createMockPrinterDiscoverySessionCallbacks(PRINTER_NAME);
 
         // Create the service callbacks for the first print service.
         PrintServiceCallbacks serviceCallbacks = createMockPrinterServiceCallbacks(
@@ -382,8 +369,8 @@
     @Test
     public void updateIcon() throws Throwable {
         // Create the session callbacks that we will be checking.
-        final PrinterDiscoverySessionCallbacks sessionCallbacks
-                = createMockPrinterDiscoverySessionCallbacks(PRINTER_NAME, null);
+        final PrinterDiscoverySessionCallbacks sessionCallbacks =
+                createMockPrinterDiscoverySessionCallbacks(PRINTER_NAME);
 
         // Create the service callbacks for the first print service.
         PrintServiceCallbacks serviceCallbacks = createMockPrinterServiceCallbacks(
@@ -446,8 +433,8 @@
     @Test
     public void cannotUseAttachBaseContext() throws Throwable {
         // Create the session callbacks that we will be checking.
-        final PrinterDiscoverySessionCallbacks sessionCallbacks
-                = createMockPrinterDiscoverySessionCallbacks(PRINTER_NAME, null);
+        final PrinterDiscoverySessionCallbacks sessionCallbacks =
+                createMockPrinterDiscoverySessionCallbacks(PRINTER_NAME);
 
         // Create the service callbacks for the first print service.
         PrintServiceCallbacks serviceCallbacks = createMockPrinterServiceCallbacks(
@@ -490,15 +477,15 @@
             PrintManager pm = (PrintManager) getActivity().getSystemService(Context.PRINT_SERVICE);
 
             // Configure first print service
-            PrinterDiscoverySessionCallbacks sessionCallbacks1
-                    = createMockPrinterDiscoverySessionCallbacks("Printer1", null);
+            PrinterDiscoverySessionCallbacks sessionCallbacks1 =
+                    createMockPrinterDiscoverySessionCallbacks("Printer1");
             PrintServiceCallbacks serviceCallbacks1 = createMockPrinterServiceCallbacks(
                     sessionCallbacks1);
             FirstPrintService.setCallbacks(serviceCallbacks1);
 
             // Configure second print service
-            PrinterDiscoverySessionCallbacks sessionCallbacks2
-                    = createMockPrinterDiscoverySessionCallbacks("Printer2", null);
+            PrinterDiscoverySessionCallbacks sessionCallbacks2 =
+                    createMockPrinterDiscoverySessionCallbacks("Printer2");
             PrintServiceCallbacks serviceCallbacks2 = createMockPrinterServiceCallbacks(
                     sessionCallbacks2);
             SecondPrintService.setCallbacks(serviceCallbacks2);
@@ -587,8 +574,60 @@
         ArrayList<String> trackedPrinters = new ArrayList<>();
 
         // Create the session callbacks that we will be checking.
-        final PrinterDiscoverySessionCallbacks sessionCallbacks
-                = createMockPrinterDiscoverySessionCallbacks(PRINTER_NAME, trackedPrinters);
+        final PrinterDiscoverySessionCallbacks sessionCallbacks =
+                createMockPrinterDiscoverySessionCallbacks(invocation -> {
+                    // Get the session.
+                    StubbablePrinterDiscoverySession session =
+                            ((PrinterDiscoverySessionCallbacks) invocation.getMock()).getSession();
+
+                    PrinterId printer1Id = session.getService().generatePrinterId("Printer1");
+
+                    PrinterInfo printer1 = new PrinterInfo.Builder(printer1Id, "Printer1",
+                            PrinterInfo.STATUS_IDLE).setCapabilities(
+                            getDefaultOptionPrinterCapabilites(printer1Id)).build();
+
+                    PrinterId printer2Id = session.getService().generatePrinterId("Printer2");
+
+                    Intent infoIntent = new Intent(getActivity(), InfoActivity.class);
+                    infoIntent.putExtra("PRINTER_NAME", "Printer2");
+
+                    PendingIntent infoPendingIntent = PendingIntent.getActivity(getActivity(), 0,
+                            infoIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+                    PrinterInfo printer2 = new PrinterInfo.Builder(printer2Id, "Printer2",
+                            PrinterInfo.STATUS_IDLE)
+                            .setInfoIntent(infoPendingIntent)
+                            .setCapabilities(getDefaultOptionPrinterCapabilites(printer2Id))
+                            .build();
+
+                    List<PrinterInfo> printers = new ArrayList<>();
+                    printers.add(printer1);
+                    printers.add(printer2);
+                    session.addPrinters(printers);
+
+                    onPrinterDiscoverySessionCreateCalled();
+
+                    return null;
+                }, null, null, invocation -> {
+                    synchronized (trackedPrinters) {
+                        trackedPrinters.add(
+                                ((PrinterId) invocation.getArguments()[0]).getLocalId());
+                        trackedPrinters.notifyAll();
+                    }
+
+                    return null;
+                }, null, invocation -> {
+                    synchronized (trackedPrinters) {
+                        trackedPrinters.remove(
+                                ((PrinterId) invocation.getArguments()[0]).getLocalId());
+                        trackedPrinters.notifyAll();
+                    }
+
+                    return null;
+                }, invocation -> {
+                    onPrinterDiscoverySessionDestroyCalled();
+                    return null;
+                });
 
         // Create the service callbacks for the first print service.
         PrintServiceCallbacks serviceCallbacks = createMockPrinterServiceCallbacks(
@@ -606,34 +645,52 @@
         // Start printing.
         print(adapter);
 
+        selectPrinter("Printer1");
+
+        eventually(() -> {
+            synchronized (trackedPrinters) {
+                assertFalse(trackedPrinters.contains("Printer2"));
+            }
+        });
+
         // Enter select printer activity
         selectPrinter("All printers…");
 
-        assertFalse(trackedPrinters.contains(PRINTER_NAME));
+        try {
+            InfoActivity.addObserver(activity -> {
+                Intent intent = activity.getIntent();
 
-        InfoActivity.addObserver(activity -> {
-            Intent intent = activity.getIntent();
+                assertEquals("Printer2", intent.getStringExtra("PRINTER_NAME"));
+                assertTrue(intent.getBooleanExtra(PrintService.EXTRA_CAN_SELECT_PRINTER,
+                        false));
 
-            assertEquals(PRINTER_NAME, intent.getStringExtra("PRINTER_NAME"));
-            assertTrue(intent.getBooleanExtra(PrintService.EXTRA_CAN_SELECT_PRINTER,
-                            false));
+                activity.setResult(Activity.RESULT_OK,
+                        (new Intent()).putExtra(PrintService.EXTRA_SELECT_PRINTER, true));
+                activity.finish();
+            });
 
-            activity.setResult(Activity.RESULT_OK,
-                    (new Intent()).putExtra(PrintService.EXTRA_SELECT_PRINTER, true));
-            activity.finish();
-        });
+            try {
+                // Wait until printer is selected and thereby tracked
+                eventually(() -> {
+                    getUiDevice().waitForIdle();
+                    // Open info activity which executes the code above
+                    getUiDevice().wait(
+                            Until.findObject(By.res("com.android.printspooler:id/more_info")),
+                            OPERATION_TIMEOUT_MILLIS).click();
 
-        // Open info activity which executed the code above
-        UiObject moreInfoButton = getUiDevice().findObject(new UiSelector().resourceId(
-                "com.android.printspooler:id/more_info"));
-        moreInfoButton.click();
+                    eventually(() -> {
+                        synchronized (trackedPrinters) {
+                            assertTrue(trackedPrinters.contains("Printer2"));
+                        }
+                    }, OPERATION_TIMEOUT_MILLIS  / 2);
+                }, OPERATION_TIMEOUT_MILLIS * 2);
+            } finally {
+                InfoActivity.clearObservers();
+            }
+        } finally {
+            getUiDevice().pressBack();
+        }
 
-        // Wait until printer is selected and thereby tracked
-        eventually(() -> assertTrue(trackedPrinters.contains(PRINTER_NAME)));
-
-        InfoActivity.clearObservers();
-
-        getUiDevice().pressBack();
         getUiDevice().pressBack();
         waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
     }
diff --git a/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java b/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
index 2531f80..3ace7b7 100644
--- a/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
+++ b/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
@@ -41,11 +41,12 @@
 import android.print.test.services.StubbablePrinterDiscoverySession;
 import android.printservice.PrintJob;
 import android.printservice.PrinterDiscoverySession;
-import androidx.annotation.NonNull;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.test.uiautomator.UiObject;
 import android.support.test.uiautomator.UiSelector;
 
+import androidx.annotation.NonNull;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -185,8 +186,11 @@
 
         // Wait for preview to load and finish print
         waitForWriteAdapterCallback(1);
-        clickPrintButton();
-        waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+
+        eventually(() -> {
+            clickPrintButton();
+            waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+        }, OPERATION_TIMEOUT_MILLIS * 2);
     }
 
     @Test
@@ -234,8 +238,11 @@
 
         // Wait for preview to load and finish print
         waitForWriteAdapterCallback(1);
-        clickPrintButton();
-        waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+
+        eventually(() -> {
+            clickPrintButton();
+            waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+        }, OPERATION_TIMEOUT_MILLIS * 2);
     }
 
     @Test
diff --git a/tests/tests/provider/Android.mk b/tests/tests/provider/Android.mk
index 400f0d2a..eb6629b 100644
--- a/tests/tests/provider/Android.mk
+++ b/tests/tests/provider/Android.mk
@@ -48,4 +48,6 @@
 LOCAL_PACKAGE_NAME := CtsProviderTestCases
 LOCAL_PRIVATE_PLATFORM_APIS := true
 
+LOCAL_MIN_SDK_VERSION := 21
+
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStoreAudioTestHelper.java b/tests/tests/provider/src/android/provider/cts/MediaStoreAudioTestHelper.java
index 56d74f3..8756353 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStoreAudioTestHelper.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStoreAudioTestHelper.java
@@ -22,9 +22,13 @@
 import android.os.Environment;
 import android.provider.MediaStore;
 import android.provider.MediaStore.Audio.Media;
+import android.support.test.runner.AndroidJUnit4;
 
 import junit.framework.Assert;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 /**
  * This class contains fake data and convenient methods for testing:
  * {@link MediaStore.Audio.Media}
@@ -45,6 +49,7 @@
  * @see MediaStore_Audio_Artists_AlbumsTest
  * @see MediaStore_Audio_AlbumsTest
  */
+@RunWith(AndroidJUnit4.class)
 public class MediaStoreAudioTestHelper {
     public static abstract class MockAudioMediaInfo {
         public abstract ContentValues getContentValues(boolean isInternal);
@@ -275,6 +280,11 @@
         }
     }
 
+    @Test
+    public void testStub() {
+        // No-op test here to keep atest happy
+    }
+
     // These constants are not part of the public API
     public static final String EXTERNAL_VOLUME_NAME = "external";
     public static final String INTERNAL_VOLUME_NAME = "internal";
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStoreUiTest.java b/tests/tests/provider/src/android/provider/cts/MediaStoreUiTest.java
index b54a5e5..9436f5c 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStoreUiTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStoreUiTest.java
@@ -16,12 +16,20 @@
 
 package android.provider.cts;
 
+import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.Manifest.permission.CAMERA;
+import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.Manifest.permission.RECORD_AUDIO;
+import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
+
 import android.app.Activity;
-import android.app.UiAutomation;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.UriPermission;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.media.ExifInterface;
@@ -41,7 +49,6 @@
 import android.support.test.uiautomator.UiObject2;
 import android.support.test.uiautomator.UiSelector;
 import android.support.test.uiautomator.Until;
-import androidx.core.content.FileProvider;
 import android.test.InstrumentationTestCase;
 import android.text.format.DateUtils;
 import android.util.Log;
@@ -53,9 +60,14 @@
 import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+import androidx.core.content.FileProvider;
+
 public class MediaStoreUiTest extends InstrumentationTestCase {
     private static final String TAG = "MediaStoreUiTest";
 
@@ -141,6 +153,13 @@
         try { mDevice.findObject(sel).click(); } catch (Throwable ignored) { }
     }
 
+    private void maybeGrantRuntimePermission(String pkg, Set<String> requested, String permission) {
+        if (requested.contains(permission)) {
+            InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                    .grantRuntimePermission(pkg, permission);
+        }
+    }
+
     /**
      * Verify that whoever handles {@link MediaStore#ACTION_IMAGE_CAPTURE} can
      * correctly write the contents into a passed {@code content://} Uri.
@@ -167,14 +186,19 @@
         final String pkg = ri.activityInfo.packageName;
         Log.d(TAG, "We're probably launching " + ri);
 
+        final PackageInfo pi = context.getPackageManager().getPackageInfo(pkg,
+                PackageManager.GET_PERMISSIONS);
+        final Set<String> req = new HashSet<>();
+        req.addAll(Arrays.asList(pi.requestedPermissions));
+
         // Grant them all the permissions they might want
-        final UiAutomation ui = InstrumentationRegistry.getInstrumentation().getUiAutomation();
-        ui.grantRuntimePermission(pkg, android.Manifest.permission.CAMERA);
-        ui.grantRuntimePermission(pkg, android.Manifest.permission.ACCESS_COARSE_LOCATION);
-        ui.grantRuntimePermission(pkg, android.Manifest.permission.ACCESS_FINE_LOCATION);
-        ui.grantRuntimePermission(pkg, android.Manifest.permission.RECORD_AUDIO);
-        ui.grantRuntimePermission(pkg, android.Manifest.permission.READ_EXTERNAL_STORAGE);
-        ui.grantRuntimePermission(pkg, android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
+        maybeGrantRuntimePermission(pkg, req, CAMERA);
+        maybeGrantRuntimePermission(pkg, req, ACCESS_COARSE_LOCATION);
+        maybeGrantRuntimePermission(pkg, req, ACCESS_FINE_LOCATION);
+        maybeGrantRuntimePermission(pkg, req, ACCESS_BACKGROUND_LOCATION);
+        maybeGrantRuntimePermission(pkg, req, RECORD_AUDIO);
+        maybeGrantRuntimePermission(pkg, req, READ_EXTERNAL_STORAGE);
+        maybeGrantRuntimePermission(pkg, req, WRITE_EXTERNAL_STORAGE);
         SystemClock.sleep(DateUtils.SECOND_IN_MILLIS);
 
         mActivity.startActivityForResult(intent, REQUEST_CODE);
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Genres_MembersTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Genres_MembersTest.java
index 7310fa1..4073016 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Genres_MembersTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Genres_MembersTest.java
@@ -231,18 +231,6 @@
             assertEquals(1, jamcnt2);
             c.close();
 
-
-            // update the member
-            values.clear();
-            values.put(Members.AUDIO_ID, mAudioIdOfJamLive);
-            try {
-                mContentResolver.update(membersUri, values, null, null);
-                fail("Should throw SQLException because there is no column with name "
-                        + "\"Members.AUDIO_ID\" in the table");
-            } catch (SQLException e) {
-                // expected
-            }
-
             // Delete the members, note that this does not delete the genre itself
             assertEquals(1, mContentResolver.delete(membersUri, null, null)); // check number of rows deleted
 
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
index f57af32..c43aabc 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
@@ -16,8 +16,6 @@
 
 package android.provider.cts;
 
-import android.provider.cts.R;
-
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
@@ -32,12 +30,14 @@
 import android.provider.MediaStore.Images.Thumbnails;
 import android.provider.MediaStore.MediaColumns;
 import android.test.InstrumentationTestCase;
+import android.util.DisplayMetrics;
 
 import com.android.compatibility.common.util.FileCopyHelper;
 
+import junit.framework.AssertionFailedError;
+
 import java.io.File;
 import java.util.ArrayList;
-import android.util.DisplayMetrics;
 
 public class MediaStore_Images_ThumbnailsTest extends InstrumentationTestCase {
     private ArrayList<Uri> mRowsAdded;
@@ -74,6 +74,12 @@
         mRowsAdded = new ArrayList<Uri>();
     }
 
+    public static void assertMostlyEquals(long expected, long actual, long delta) {
+        if (Math.abs(expected - actual) > delta) {
+            throw new AssertionFailedError("Expected roughly " + expected + " but was " + actual);
+        }
+    }
+
     public void testQueryInternalThumbnails() throws Exception {
         Cursor c = Thumbnails.queryMiniThumbnails(mContentResolver,
                 Thumbnails.INTERNAL_CONTENT_URI, Thumbnails.MICRO_KIND, null);
@@ -143,15 +149,15 @@
                 sizeProjection);
         assertEquals(1, c.getCount());
         assertTrue(c.moveToFirst());
-        assertTrue(c.getLong(c.getColumnIndex(Thumbnails.WIDTH)) >= Math.min(src.getWidth(), 240));
-        assertTrue(c.getLong(c.getColumnIndex(Thumbnails.HEIGHT)) >= Math.min(src.getHeight(), 240));
+        assertMostlyEquals(320, c.getInt(c.getColumnIndex(Thumbnails.WIDTH)), 128);
+        assertMostlyEquals(320, c.getInt(c.getColumnIndex(Thumbnails.HEIGHT)), 128);
         c.close();
         c = Thumbnails.queryMiniThumbnail(mContentResolver, imageId, Thumbnails.MICRO_KIND,
                 sizeProjection);
         assertEquals(1, c.getCount());
         assertTrue(c.moveToFirst());
-        assertEquals(50, c.getLong(c.getColumnIndex(Thumbnails.WIDTH)));
-        assertEquals(50, c.getLong(c.getColumnIndex(Thumbnails.HEIGHT)));
+        assertMostlyEquals(96, c.getInt(c.getColumnIndex(Thumbnails.WIDTH)), 64);
+        assertMostlyEquals(96, c.getInt(c.getColumnIndex(Thumbnails.HEIGHT)), 64);
         c.close();
 
         c = Thumbnails.queryMiniThumbnail(mContentResolver, imageId, Thumbnails.MINI_KIND,
@@ -351,7 +357,7 @@
         c.close();
 
         // clean up
-        mContentResolver.delete(uri, null /* where */, null /* where args */);
+        mContentResolver.delete(fileUri, null /* where */, null /* where args */);
         new File(sourcePath).delete();
     }
 
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
index c256722..5888d89 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
@@ -215,7 +215,7 @@
         c.close();
 
         // clean up
-        mResolver.delete(uri, null /* where */, null /* where args */);
+        mResolver.delete(fileUri, null /* where */, null /* where args */);
         new File(sourcePath).delete();
     }
 
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/ContactUtil.java b/tests/tests/provider/src/android/provider/cts/contacts/ContactUtil.java
index ae3e41a..bc7f341 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/ContactUtil.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/ContactUtil.java
@@ -33,10 +33,10 @@
 
     private static final Uri URI = ContactsContract.Contacts.CONTENT_URI;
 
-    public static void update(ContentResolver resolver, long contactId,
+    public static int update(ContentResolver resolver, long contactId,
             ContentValues values) {
         Uri uri = ContentUris.withAppendedId(URI, contactId);
-        resolver.update(uri, values, null, null);
+        return resolver.update(uri, values, null, null);
     }
 
     public static void delete(ContentResolver resolver, long contactId) {
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 3c56aba..c9d1371 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
@@ -71,46 +71,19 @@
         assertEquals(0, rawContact.getLong(Contacts.LAST_TIME_CONTACTED));
         assertEquals(0, rawContact.getLong(Contacts.TIMES_CONTACTED));
 
+        // Note we no longer support contact affinity as of Q, so times_contacted and
+        // last_time_contacted are always 0.
+
         for (int i = 1; i < 10; i++) {
             Contacts.markAsContacted(mResolver, contact.getId());
             contact.load();
             rawContact.load();
 
-            assertEquals(System.currentTimeMillis() / 86400 * 86400,
-                    contact.getLong(Contacts.LAST_TIME_CONTACTED));
-            assertEquals("#" + i, i, contact.getLong(Contacts.TIMES_CONTACTED));
+            assertEquals(0, contact.getLong(Contacts.LAST_TIME_CONTACTED));
+            assertEquals("#" + i, 0, contact.getLong(Contacts.TIMES_CONTACTED));
 
-            assertEquals(System.currentTimeMillis() / 86400 * 86400,
-                    rawContact.getLong(Contacts.LAST_TIME_CONTACTED));
-            assertEquals("#" + i, i, rawContact.getLong(Contacts.TIMES_CONTACTED));
-        }
-
-        for (int i = 0; i < 10; i++) {
-            Contacts.markAsContacted(mResolver, contact.getId());
-            contact.load();
-            rawContact.load();
-
-            assertEquals(System.currentTimeMillis() / 86400 * 86400,
-                    contact.getLong(Contacts.LAST_TIME_CONTACTED));
-            assertEquals("#" + i, 10, contact.getLong(Contacts.TIMES_CONTACTED));
-
-            assertEquals(System.currentTimeMillis() / 86400 * 86400,
-                    rawContact.getLong(Contacts.LAST_TIME_CONTACTED));
-            assertEquals("#" + i, 10, rawContact.getLong(Contacts.TIMES_CONTACTED));
-        }
-
-        for (int i = 0; i < 10; i++) {
-            Contacts.markAsContacted(mResolver, contact.getId());
-            contact.load();
-            rawContact.load();
-
-            assertEquals(System.currentTimeMillis() / 86400 * 86400,
-                    contact.getLong(Contacts.LAST_TIME_CONTACTED));
-            assertEquals("#" + i, 20, contact.getLong(Contacts.TIMES_CONTACTED));
-
-            assertEquals(System.currentTimeMillis() / 86400 * 86400,
-                    rawContact.getLong(Contacts.LAST_TIME_CONTACTED));
-            assertEquals("#" + i, 20, rawContact.getLong(Contacts.TIMES_CONTACTED));
+            assertEquals(0, rawContact.getLong(Contacts.LAST_TIME_CONTACTED));
+            assertEquals("#" + i, 0, rawContact.getLong(Contacts.TIMES_CONTACTED));
         }
     }
 
@@ -201,6 +174,10 @@
         RawContactUtil.delete(mResolver, ids.mRawContactId, true);
     }
 
+    /**
+     * Note we no longer support contact affinity as of Q, so times_contacted and
+     * last_time_contacted are always 0.
+     */
     public void testContactUpdate_usageStats() throws Exception {
         final TestRawContact rawContact = mBuilder.newRawContact().insert().load();
         final TestContact contact = rawContact.getContact().load();
@@ -218,8 +195,8 @@
         ContactUtil.update(mResolver, contact.getId(), values);
 
         contact.load();
-        assertEquals(3L, contact.getLong(Contacts.TIMES_CONTACTED));
-        assertEquals(now / 86400 * 86400, contact.getLong(Contacts.LAST_TIME_CONTACTED));
+        assertEquals(0, contact.getLong(Contacts.TIMES_CONTACTED));
+        assertEquals(0, contact.getLong(Contacts.LAST_TIME_CONTACTED));
 
         // This is also the same as markAsContacted().
         values.clear();
@@ -227,8 +204,8 @@
         ContactUtil.update(mResolver, contact.getId(), values);
 
         contact.load();
-        assertEquals(4L, contact.getLong(Contacts.TIMES_CONTACTED));
-        assertEquals(now / 86400 * 86400, contact.getLong(Contacts.LAST_TIME_CONTACTED));
+        assertEquals(0, contact.getLong(Contacts.TIMES_CONTACTED));
+        assertEquals(0, contact.getLong(Contacts.LAST_TIME_CONTACTED));
 
         values.clear();
         values.put(Contacts.TIMES_CONTACTED, 10);
@@ -236,8 +213,8 @@
         ContactUtil.update(mResolver, contact.getId(), values);
 
         contact.load();
-        assertEquals(10L, contact.getLong(Contacts.TIMES_CONTACTED));
-        assertEquals(now / 86400 * 86400, contact.getLong(Contacts.LAST_TIME_CONTACTED));
+        assertEquals(0, contact.getLong(Contacts.TIMES_CONTACTED));
+        assertEquals(0, contact.getLong(Contacts.LAST_TIME_CONTACTED));
     }
 
     /**
@@ -264,27 +241,11 @@
         contact.load();
         rawContact.load();
 
-        assertEquals(now / 86400 * 86400, contact.getLong(Contacts.LAST_TIME_CONTACTED));
-        assertEquals(30, contact.getLong(Contacts.TIMES_CONTACTED));
+        assertEquals(0, contact.getLong(Contacts.LAST_TIME_CONTACTED));
+        assertEquals(0, contact.getLong(Contacts.TIMES_CONTACTED));
 
-        assertEquals(now / 86400 * 86400, rawContact.getLong(Contacts.LAST_TIME_CONTACTED));
-        assertEquals(30, rawContact.getLong(Contacts.TIMES_CONTACTED));
-
-        final ContentValues cv = new ContentValues();
-            cv.put(Contacts.STARRED, 1);
-
-        final String where =
-                (Contacts.LAST_TIME_CONTACTED + "=P1 AND " + Contacts.TIMES_CONTACTED + "=P2")
-                        .replaceAll("P1", String.valueOf(now / 86400 * 86400))
-                        .replaceAll("P2", "30");
-        assertEquals(1, mResolver.update(Contacts.CONTENT_URI, cv, where, null));
-        assertEquals(1, mResolver.update(RawContacts.CONTENT_URI, cv, where, null));
-
-        // Also delete.  This will actually delete the row, so we can test it only for one of the
-        // contact or the raw contact.
-        assertEquals(1, mResolver.delete(RawContacts.CONTENT_URI, where, null));
-        rawContact.setAlreadyDeleted();
-        contact.setAlreadyDeleted();
+        assertEquals(0, rawContact.getLong(Contacts.LAST_TIME_CONTACTED));
+        assertEquals(0, rawContact.getLong(Contacts.TIMES_CONTACTED));
     }
 
     public void testProjection() throws Exception {
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_DataUsageTest.java b/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_DataUsageTest.java
index d5ab172..f1a6409 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_DataUsageTest.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_DataUsageTest.java
@@ -18,25 +18,40 @@
 
 import static android.provider.ContactsContract.DataUsageFeedback;
 
+import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.net.Uri;
 import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
-import android.provider.cts.contacts.DataUtil;
-import android.provider.cts.contacts.DatabaseAsserts;
-import android.provider.cts.contacts.RawContactUtil;
+import android.provider.cts.contacts.ContactsContract_TestDataBuilder.TestContact;
+import android.provider.cts.contacts.ContactsContract_TestDataBuilder.TestRawContact;
 import android.test.AndroidTestCase;
 import android.text.TextUtils;
 
+/**
+ * Note we no longer support contact affinity as of Q, so times_contacted and
+ * last_time_contacted are always 0.
+ */
 public class ContactsContract_DataUsageTest extends AndroidTestCase {
 
     private ContentResolver mResolver;
+    private ContactsContract_TestDataBuilder mBuilder;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
         mResolver = getContext().getContentResolver();
+        ContentProviderClient provider =
+                mResolver.acquireContentProviderClient(ContactsContract.AUTHORITY);
+        mBuilder = new ContactsContract_TestDataBuilder(provider);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mBuilder.cleanup();
+        super.tearDown();
     }
 
     public void testSingleDataUsageFeedback_incrementsCorrectDataItems() {
@@ -44,45 +59,14 @@
 
         long[] dataIds = setupRawContactDataItems(ids.mRawContactId);
 
-        // Update just 1 data item at a time.
-        updateDataUsageAndAssert(dataIds[1], 1);
-        updateDataUsageAndAssert(dataIds[1], 2);
-        updateDataUsageAndAssert(dataIds[1], 3);
-        updateDataUsageAndAssert(dataIds[1], 4);
-        updateDataUsageAndAssert(dataIds[1], 5);
-        updateDataUsageAndAssert(dataIds[1], 6);
-        updateDataUsageAndAssert(dataIds[1], 7);
-        updateDataUsageAndAssert(dataIds[1], 8);
-        updateDataUsageAndAssert(dataIds[1], 9);
-        updateDataUsageAndAssert(dataIds[1], 10);
-        updateDataUsageAndAssert(dataIds[1], 10);
-        updateDataUsageAndAssert(dataIds[1], 10);
-        updateDataUsageAndAssert(dataIds[1], 10);
-        updateDataUsageAndAssert(dataIds[1], 10);
-        updateDataUsageAndAssert(dataIds[1], 10);
-        updateDataUsageAndAssert(dataIds[1], 10);
-        updateDataUsageAndAssert(dataIds[1], 10);
-        updateDataUsageAndAssert(dataIds[1], 10);
+        updateDataUsageAndAssert(dataIds[1], 0);
+        updateDataUsageAndAssert(dataIds[1], 0);
 
-        updateDataUsageAndAssert(dataIds[2], 1);
-        updateDataUsageAndAssert(dataIds[2], 2);
-        updateDataUsageAndAssert(dataIds[2], 3);
+        updateDataUsageAndAssert(dataIds[2], 0);
+        updateDataUsageAndAssert(dataIds[2], 0);
 
-        // Go back and update the previous data item again.
-        updateDataUsageAndAssert(dataIds[1], 10);
-        updateDataUsageAndAssert(dataIds[1], 20);
-
-        updateDataUsageAndAssert(dataIds[2], 4);
-        updateDataUsageAndAssert(dataIds[2], 5);
-        updateDataUsageAndAssert(dataIds[2], 6);
-        updateDataUsageAndAssert(dataIds[2], 7);
-        updateDataUsageAndAssert(dataIds[2], 8);
-        updateDataUsageAndAssert(dataIds[2], 9);
-        updateDataUsageAndAssert(dataIds[2], 10);
-
-        updateDataUsageAndAssert(dataIds[1], 20);
-        updateDataUsageAndAssert(dataIds[1], 20);
-        updateDataUsageAndAssert(dataIds[1], 20);
+        updateDataUsageAndAssert(dataIds[1], 0);
+        updateDataUsageAndAssert(dataIds[1], 0);
 
         deleteDataUsage();
         RawContactUtil.delete(mResolver, ids.mRawContactId, true);
@@ -96,25 +80,7 @@
         assertDataUsageEquals(dataIds, 0, 0, 0, 0);
 
         updateMultipleAndAssertUpdateSuccess(new long[] {dataIds[1], dataIds[2]});
-        assertDataUsageEquals(dataIds, 0, 1, 1, 0);
-
-        updateMultipleAndAssertUpdateSuccess(new long[]{dataIds[1], dataIds[2]});
-        assertDataUsageEquals(dataIds, 0, 2, 2, 0);
-
-        for (int i = 3; i <= 10; i++) {
-            updateMultipleAndAssertUpdateSuccess(new long[]{dataIds[1]});
-        }
-        assertDataUsageEquals(dataIds, 0, 10, 2, 0);
-
-        updateMultipleAndAssertUpdateSuccess(new long[]{dataIds[0], dataIds[1]});
-        assertDataUsageEquals(dataIds, 1, 10, 2, 0);
-
-        for (int i = 12; i <= 19; i++) {
-            updateMultipleAndAssertUpdateSuccess(new long[]{dataIds[1]});
-            assertDataUsageEquals(dataIds, 1, 10, 2, 0);
-        }
-        updateMultipleAndAssertUpdateSuccess(new long[]{dataIds[1]});
-        assertDataUsageEquals(dataIds, 1, 20, 2, 0);
+        assertDataUsageEquals(dataIds, 0, 0, 0, 0);
 
         deleteDataUsage();
         RawContactUtil.delete(mResolver, ids.mRawContactId, true);
@@ -130,9 +96,6 @@
         return dataIds;
     }
 
-    /**
-     * Updates multiple data ids at once.  And asserts the update returned success.
-     */
     private void updateMultipleAndAssertUpdateSuccess(long[] dataIds) {
         String[] ids = new String[dataIds.length];
         for (int i = 0; i < dataIds.length; i++) {
@@ -142,26 +105,19 @@
                 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
                         DataUsageFeedback.USAGE_TYPE_CALL).build();
         int result = mResolver.update(uri, new ContentValues(), null, null);
-        assertTrue(result > 0);
+        assertEquals(0, result); // always 0
     }
 
-    /**
-     * Updates a single data item usage.  Asserts the update was successful.  Asserts the usage
-     * number is equal to expected value.
-     */
     private void updateDataUsageAndAssert(long dataId, int assertValue) {
         Uri uri = DataUsageFeedback.FEEDBACK_URI.buildUpon().appendPath(String.valueOf(dataId))
                 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
                         DataUsageFeedback.USAGE_TYPE_CALL).build();
         int result = mResolver.update(uri, new ContentValues(), null, null);
-        assertTrue(result > 0);
+        assertEquals(0, result); // always 0
 
         assertDataUsageEquals(dataId, assertValue);
     }
 
-    /**
-     * Assert that the given data ids have usage values in the respective order.
-     */
     private void assertDataUsageEquals(long[] dataIds, int... expectedValues) {
         if (dataIds.length != expectedValues.length) {
             throw new IllegalArgumentException("dataIds and expectedValues must be the same size");
@@ -195,4 +151,48 @@
     private void deleteDataUsage() {
         mResolver.delete(DataUsageFeedback.DELETE_USAGE_URI, null, null);
     }
+
+
+    public void testUsageUpdate() throws Exception {
+        final TestRawContact rawContact = mBuilder.newRawContact().insert().load();
+        final TestContact contact = rawContact.getContact().load();
+
+        ContentValues values;
+
+        values = new ContentValues();
+        values.put(Contacts.LAST_TIME_CONTACTED, 123);
+        assertEquals(1, ContactUtil.update(mResolver, contact.getId(), values));
+
+        values = new ContentValues();
+        values.put(Contacts.LAST_TIME_CONTACTED, 123);
+        assertEquals(1, RawContactUtil.update(mResolver, rawContact.getId(), values));
+
+        values = new ContentValues();
+        values.put(Contacts.TIMES_CONTACTED, 456);
+        assertEquals(1, ContactUtil.update(mResolver, contact.getId(), values));
+
+        values = new ContentValues();
+        values.put(Contacts.TIMES_CONTACTED, 456);
+        assertEquals(1, RawContactUtil.update(mResolver, rawContact.getId(), values));
+
+
+        values = new ContentValues();
+        values.put(Contacts.LAST_TIME_CONTACTED, 123);
+        values.put(Contacts.TIMES_CONTACTED, 456);
+        assertEquals(1, ContactUtil.update(mResolver, contact.getId(), values));
+
+        values = new ContentValues();
+        values.put(Contacts.LAST_TIME_CONTACTED, 123);
+        values.put(Contacts.TIMES_CONTACTED, 456);
+        assertEquals(1, RawContactUtil.update(mResolver, rawContact.getId(), values));
+
+        contact.load();
+        rawContact.load();
+
+        assertEquals(0, contact.getLong(Contacts.LAST_TIME_CONTACTED));
+        assertEquals(0, contact.getLong(Contacts.TIMES_CONTACTED));
+
+        assertEquals(0, rawContact.getLong(Contacts.LAST_TIME_CONTACTED));
+        assertEquals(0, rawContact.getLong(Contacts.TIMES_CONTACTED));
+    }
 }
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_FrequentsStrequentsTest.java b/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_FrequentsStrequentsTest.java
index 2231bd2f..3fb6720 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_FrequentsStrequentsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_FrequentsStrequentsTest.java
@@ -37,7 +37,10 @@
 /**
  * CTS tests for {@link android.provider.ContactsContract.Contacts#CONTENT_FREQUENT_URI},
  * {@link android.provider.ContactsContract.Contacts#CONTENT_STREQUENT_URI} and
- * {@link android.provider.ContactsContract.Contacts#CONTENT_STREQUENT_FILTER_URI} apis.
+ * {@link android.provider.ContactsContract.Contacts#CONTENT_STREQUENT_FILTER_URI} APIs.
+ *
+ * Note we no longer support contact affinity as of Q, so times_contacted and
+ * last_time_contacted are always 0, and "frequent" is always empty.
  */
 public class ContactsContract_FrequentsStrequentsTest extends InstrumentationTestCase {
     private ContentResolver mResolver;
@@ -180,11 +183,6 @@
                 false, sContentValues[1], sContentValues[0]);
     }
 
-    /**
-     * Tests that {@link android.provider.ContactsContract.Contacts#CONTENT_STREQUENT_URI} returns
-     * frequent contacts in the correct order if there are only frequent contacts in the user's
-     * contacts.
-     */
     public void testStrequents_frequentsOnlyInCorrectOrder() throws Exception {
         long[] ids = setupTestData();
 
@@ -200,14 +198,9 @@
         // The strequents uri should now return contact 2, 3, 1 in order due to ranking by
         // data usage.
         assertCursorStoredValuesWithContactsFilter(Contacts.CONTENT_STREQUENT_URI, ids,
-                false, sContentValues[1], sContentValues[2], sContentValues[0]);
+                false);
     }
 
-    /**
-     * Tests that {@link android.provider.ContactsContract.Contacts#CONTENT_STREQUENT_URI} returns
-     * first starred, then frequent contacts in their respective correct orders if there are both
-     * starred and frequent contacts in the user's contacts.
-     */
     public void testStrequents_starredAndFrequentsInCorrectOrder() throws Exception {
         long[] ids = setupTestData();
 
@@ -226,7 +219,7 @@
         // Note that contact 3 is only returned once (as a starred contact) even though it is also
         // a frequently contacted contact.
         assertCursorStoredValuesWithContactsFilter(Contacts.CONTENT_STREQUENT_URI, ids,
-                false, sContentValues[2], sContentValues[1], sContentValues[0]);
+                false, sContentValues[2]);
     }
 
     /**
@@ -246,19 +239,20 @@
 
         // Only contact 1 and 2 should be returned (sorted in alphabetical order) due to the
         // filtered query.
-        assertCursorStoredValuesWithContactsFilter(uri, ids, false, sContentValues[1], sContentValues[0]);
+        assertCursorStoredValuesWithContactsFilter(uri, ids, false,
+                sContentValues[1], sContentValues[0]);
     }
 
     public void testStrequents_projection() throws Exception {
         long[] ids = setupTestData();
 
-        // Start contact 0 and mark contact 2 as frequent
+        // Star contact 0 and mark contact 2 as frequent
         starContact(ids[0]);
         markDataAsUsed(mDataIds[2], 1);
 
         DatabaseAsserts.checkProjection(mResolver, Contacts.CONTENT_STREQUENT_URI,
                 STREQUENT_PROJECTION,
-                new long[]{ids[0], ids[2]}
+                new long[]{ids[0]}
         );
 
         // Strequent filter.
@@ -305,8 +299,7 @@
                 appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true").build();
 
         // Only the contacts with phone numbers are returned, in frequency ranking order.
-        assertCursorStoredValuesWithContactsFilter(uri, mDataIds, false,
-                sContentValues[2], sContentValues[0]);
+        assertCursorStoredValuesWithContactsFilter(uri, mDataIds, false);
     }
 
     public void testStrequents_phoneOnly_projection() throws Exception {
@@ -322,7 +315,7 @@
 
         DatabaseAsserts.checkProjection(mResolver, uri,
                 STREQUENT_PHONE_ONLY_PROJECTION,
-                new long[]{mDataIds[0], mDataIds[2]} // Note _id from phone_only is data._id
+                new long[]{mDataIds[0]}
         );
     }
 
@@ -346,7 +339,7 @@
         // The frequents uri should now return contact 2, 3, 1 in order due to ranking by
         // data usage.
         assertCursorStoredValuesWithContactsFilter(Contacts.CONTENT_FREQUENT_URI, ids,
-                true /* inOrder */, sContentValues[1], sContentValues[2], sContentValues[0]);
+                true);
     }
 
     public void testFrequent_projection() throws Exception {
@@ -356,7 +349,7 @@
 
         DatabaseAsserts.checkProjection(mResolver, Contacts.CONTENT_FREQUENT_URI,
                 STREQUENT_PROJECTION,
-                new long[]{ids[0]}
+                new long[]{}
         );
     }
 
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 7c971b3..89889f7 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
@@ -233,6 +233,9 @@
     }
 
     public void testInsertUsageStat() throws Exception {
+        // Note we no longer support contact affinity as of Q, so times_contacted and
+        // last_time_contacted are always 0, and "frequent" is always empty.
+
         final long now = System.currentTimeMillis();
         {
             TestRawContact rawContact = mBuilder.newRawContact()
@@ -243,8 +246,8 @@
                     .insert();
 
             rawContact.load();
-            assertEquals(12340, rawContact.getLong(RawContacts.TIMES_CONTACTED));
-            assertEquals(now / 86400 * 86400, rawContact.getLong(RawContacts.LAST_TIME_CONTACTED));
+            assertEquals(0, rawContact.getLong(RawContacts.TIMES_CONTACTED));
+            assertEquals(0, rawContact.getLong(RawContacts.LAST_TIME_CONTACTED));
         }
 
         {
@@ -255,7 +258,7 @@
                     .insert();
 
             rawContact.load();
-            assertEquals(5L, rawContact.getLong(RawContacts.TIMES_CONTACTED));
+            assertEquals(0, rawContact.getLong(RawContacts.TIMES_CONTACTED));
             assertEquals(0, rawContact.getLong(RawContacts.LAST_TIME_CONTACTED));
         }
         {
@@ -266,8 +269,8 @@
                     .insert();
 
             rawContact.load();
-            assertEquals(0L, rawContact.getLong(RawContacts.TIMES_CONTACTED));
-            assertEquals(now / 86400 * 86400, rawContact.getLong(RawContacts.LAST_TIME_CONTACTED));
+            assertEquals(0, rawContact.getLong(RawContacts.TIMES_CONTACTED));
+            assertEquals(0, rawContact.getLong(RawContacts.LAST_TIME_CONTACTED));
         }
     }
 
@@ -283,24 +286,23 @@
                 .insert();
 
         rawContact.load();
-        assertEquals(12340L, rawContact.getLong(RawContacts.TIMES_CONTACTED));
-        assertEquals(now / 86400 * 86400, rawContact.getLong(RawContacts.LAST_TIME_CONTACTED));
+        assertEquals(0, rawContact.getLong(RawContacts.TIMES_CONTACTED));
+        assertEquals(0, rawContact.getLong(RawContacts.LAST_TIME_CONTACTED));
 
         values.clear();
         values.put(RawContacts.TIMES_CONTACTED, 99999);
         RawContactUtil.update(mResolver, rawContact.getId(), values);
 
         rawContact.load();
-        assertEquals(99990L, rawContact.getLong(RawContacts.TIMES_CONTACTED));
-        assertEquals(now / 86400 * 86400, rawContact.getLong(RawContacts.LAST_TIME_CONTACTED));
+        assertEquals(0, rawContact.getLong(RawContacts.TIMES_CONTACTED));
+        assertEquals(0, rawContact.getLong(RawContacts.LAST_TIME_CONTACTED));
 
         values.clear();
         values.put(RawContacts.LAST_TIME_CONTACTED, now + 86400);
         RawContactUtil.update(mResolver, rawContact.getId(), values);
 
         rawContact.load();
-        assertEquals(99990L, rawContact.getLong(RawContacts.TIMES_CONTACTED));
-        assertEquals((now / 86400 * 86400) + 86400,
-                rawContact.getLong(RawContacts.LAST_TIME_CONTACTED));
+        assertEquals(0, rawContact.getLong(RawContacts.TIMES_CONTACTED));
+        assertEquals(0, rawContact.getLong(RawContacts.LAST_TIME_CONTACTED));
     }
 }
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 38ff33d..b8e1576 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/RawContactUtil.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/RawContactUtil.java
@@ -33,10 +33,10 @@
 
     private static final Uri URI = ContactsContract.RawContacts.CONTENT_URI;
 
-    public static void update(ContentResolver resolver, long rawContactId,
+    public static int update(ContentResolver resolver, long rawContactId,
             ContentValues values) {
         Uri uri = ContentUris.withAppendedId(URI, rawContactId);
-        resolver.update(uri, values, null, null);
+        return resolver.update(uri, values, null, null);
     }
 
     public static long createRawContactWithName(ContentResolver resolver, Account account,
diff --git a/tests/tests/selinux/selinuxTargetSdk25/Android.mk b/tests/tests/selinux/selinuxTargetSdk25/Android.mk
index f9bfc95..040bc62 100755
--- a/tests/tests/selinux/selinuxTargetSdk25/Android.mk
+++ b/tests/tests/selinux/selinuxTargetSdk25/Android.mk
@@ -40,6 +40,9 @@
 LOCAL_PACKAGE_NAME := CtsSelinuxTargetSdk25TestCases
 LOCAL_PRIVATE_PLATFORM_APIS := true
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_MIN_SDK_VERSION := 21
+
 include $(BUILD_CTS_PACKAGE)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/selinux/selinuxTargetSdk27/Android.mk b/tests/tests/selinux/selinuxTargetSdk27/Android.mk
index 37660a3..ae9e5bf 100755
--- a/tests/tests/selinux/selinuxTargetSdk27/Android.mk
+++ b/tests/tests/selinux/selinuxTargetSdk27/Android.mk
@@ -40,6 +40,9 @@
 LOCAL_PACKAGE_NAME := CtsSelinuxTargetSdk27TestCases
 LOCAL_PRIVATE_PLATFORM_APIS := true
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_MIN_SDK_VERSION := 21
+
 include $(BUILD_CTS_PACKAGE)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/selinux/selinuxTargetSdkCurrent/Android.mk b/tests/tests/selinux/selinuxTargetSdkCurrent/Android.mk
index 63bc768..fa65d3d 100755
--- a/tests/tests/selinux/selinuxTargetSdkCurrent/Android.mk
+++ b/tests/tests/selinux/selinuxTargetSdkCurrent/Android.mk
@@ -40,6 +40,9 @@
 LOCAL_PACKAGE_NAME := CtsSelinuxTargetSdkCurrentTestCases
 LOCAL_PRIVATE_PLATFORM_APIS := true
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_MIN_SDK_VERSION := 21
+
 include $(BUILD_CTS_PACKAGE)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/shortcutmanager/throttling/Android.mk b/tests/tests/shortcutmanager/throttling/Android.mk
index 5a174cc..eb62622 100644
--- a/tests/tests/shortcutmanager/throttling/Android.mk
+++ b/tests/tests/shortcutmanager/throttling/Android.mk
@@ -38,5 +38,6 @@
     ShortcutManagerTestUtils
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 25
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/slice/src/android/slice/cts/LocalSliceProvider.java b/tests/tests/slice/src/android/slice/cts/LocalSliceProvider.java
index 53b13cb..b6c162b 100644
--- a/tests/tests/slice/src/android/slice/cts/LocalSliceProvider.java
+++ b/tests/tests/slice/src/android/slice/cts/LocalSliceProvider.java
@@ -47,7 +47,7 @@
     @Override
     public void attachInfo(Context context, ProviderInfo info) {
         mSliceService = mock(SliceManager.class, withSettings()
-                .spiedInstance(context.getSystemService(Context.SLICE_SERVICE))
+                .spiedInstance(context.getSystemService(SliceManager.class))
                 .defaultAnswer(invocation -> {
                     Answer s = sAnswer != null ? sAnswer : Answers.CALLS_REAL_METHODS;
                     return s.answer(invocation);
@@ -55,7 +55,7 @@
         Context wrapped = new ContextWrapper(context) {
             @Override
             public Object getSystemService(String name) {
-                if (Context.SLICE_SERVICE.equals(name)) {
+                if (getSystemServiceName(SliceManager.class).equals(name)) {
                     return mSliceService;
                 }
                 return super.getSystemService(name);
diff --git a/tests/tests/systemui/AndroidTest.xml b/tests/tests/systemui/AndroidTest.xml
index 208f844..cbee4a1 100644
--- a/tests/tests/systemui/AndroidTest.xml
+++ b/tests/tests/systemui/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS SystemUI test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="sysui" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/systemui/src/android/systemui/cts/LightBarTestBase.java b/tests/tests/systemui/src/android/systemui/cts/LightBarTestBase.java
index c88e7d1..3f1b0a1 100644
--- a/tests/tests/systemui/src/android/systemui/cts/LightBarTestBase.java
+++ b/tests/tests/systemui/src/android/systemui/cts/LightBarTestBase.java
@@ -42,6 +42,8 @@
 
     public static final String DUMP_PATH = "/sdcard/lightstatustest.png";
 
+    public static final int WAIT_TIME = 2000;
+
     protected Bitmap takeStatusBarScreenshot(LightBarBaseActivity activity) {
         Bitmap fullBitmap = getInstrumentation().getUiAutomation().takeScreenshot();
         return Bitmap.createBitmap(fullBitmap, 0, 0, activity.getWidth(), activity.getTop());
@@ -99,9 +101,11 @@
                 PackageManager.FEATURE_EMBEDDED));
 
         // No bars on TVs and watches.
+        // Automotive navigation bar is not transparent
         assumeFalse(pm.hasSystemFeature(PackageManager.FEATURE_WATCH)
                 || pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
-                || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK));
+                || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+                || pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE));
 
 
         // Non-highEndGfx devices don't do colored system bars.
diff --git a/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java b/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java
index fc7e70f..afa350f 100644
--- a/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java
+++ b/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java
@@ -50,8 +50,6 @@
 
     public static final String TAG = "LightStatusBarTests";
 
-    private static final int WAIT_TIME = 2000;
-
     /**
      * Color may be slightly off-spec when resources are resized for lower densities. Use this error
      * margin to accommodate for that when comparing colors.
diff --git a/tests/tests/systemui/src/android/systemui/cts/LightBarThemeTest.java b/tests/tests/systemui/src/android/systemui/cts/LightBarThemeTest.java
index 4d20dfb..8961d39 100644
--- a/tests/tests/systemui/src/android/systemui/cts/LightBarThemeTest.java
+++ b/tests/tests/systemui/src/android/systemui/cts/LightBarThemeTest.java
@@ -76,6 +76,9 @@
         // Wait until the activity is fully visible
         mDevice.waitForIdle();
 
+        // Wait until window animation is finished
+        Thread.sleep(WAIT_TIME);
+
         final Context instrumentationContext = getInstrumentation().getContext();
         checkNavigationBarDivider(mActivityRule.getActivity(),
                 instrumentationContext.getColor(R.color.navigationBarDividerColor),
diff --git a/tests/tests/telecom/Android.mk b/tests/tests/telecom/Android.mk
index 8f46fd7..e4f99b8 100644
--- a/tests/tests/telecom/Android.mk
+++ b/tests/tests/telecom/Android.mk
@@ -34,6 +34,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := test_current
+LOCAL_MIN_SDK_VERSION := 21
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/tests/tests/telecom2/Android.mk b/tests/tests/telecom2/Android.mk
index 5b22a92..9ceb4d4 100644
--- a/tests/tests/telecom2/Android.mk
+++ b/tests/tests/telecom2/Android.mk
@@ -48,6 +48,7 @@
     --rename-manifest-package android.telecom2.cts \
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 21
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/tests/tests/telecom3/Android.mk b/tests/tests/telecom3/Android.mk
index 3138e6c..fc95b23 100644
--- a/tests/tests/telecom3/Android.mk
+++ b/tests/tests/telecom3/Android.mk
@@ -45,6 +45,7 @@
     --rename-manifest-package android.telecom3.cts \
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 25
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/tests/tests/telephony/src/android/telephony/cts/CarrierConfigManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/CarrierConfigManagerTest.java
index 7323f30..901b47d 100644
--- a/tests/tests/telephony/src/android/telephony/cts/CarrierConfigManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/CarrierConfigManagerTest.java
@@ -16,14 +16,23 @@
 
 package android.telephony.cts;
 
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.app.AppOpsManager.OPSTR_READ_PHONE_STATE;
+
+import static com.android.compatibility.common.util.AppOpsUtils.setOpMode;
+
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.os.PersistableBundle;
+import android.platform.test.annotations.SecurityTest;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.test.AndroidTestCase;
 
+import java.io.IOException;
+
 public class CarrierConfigManagerTest extends AndroidTestCase {
     private CarrierConfigManager mConfigManager;
     private TelephonyManager mTelephonyManager;
@@ -37,6 +46,16 @@
                 getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
     }
 
+    @Override
+    protected void tearDown() throws Exception {
+        try {
+            setOpMode("android.telephony.cts", OPSTR_READ_PHONE_STATE, MODE_ALLOWED);
+        } catch (IOException e) {
+            fail();
+        }
+        super.tearDown();
+    }
+
     /**
      * Checks whether the telephony stack should be running on this device.
      *
@@ -87,6 +106,29 @@
         checkConfig(config);
     }
 
+    @SecurityTest
+    public void testRevokePermission() {
+        PersistableBundle config;
+
+        try {
+            setOpMode("android.telephony.cts", OPSTR_READ_PHONE_STATE, MODE_IGNORED);
+        } catch (IOException e) {
+            fail();
+        }
+
+        config = mConfigManager.getConfig();
+        assertTrue(config.isEmptyParcel());
+
+        try {
+            setOpMode("android.telephony.cts", OPSTR_READ_PHONE_STATE, MODE_ALLOWED);
+        } catch (IOException e) {
+            fail();
+        }
+
+        config = mConfigManager.getConfig();
+        checkConfig(config);
+    }
+
     public void testGetConfigForSubId() {
         PersistableBundle config =
                 mConfigManager.getConfigForSubId(SubscriptionManager.getDefaultSubscriptionId());
diff --git a/tests/tests/text/AndroidTest.xml b/tests/tests/text/AndroidTest.xml
index b8da91e..477f610 100644
--- a/tests/tests/text/AndroidTest.xml
+++ b/tests/tests/text/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS Text test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="uitoolkit" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsTextTestCases.apk" />
diff --git a/tests/tests/text/src/android/text/cts/PrecomputedTextTest.java b/tests/tests/text/src/android/text/cts/PrecomputedTextTest.java
index 9491eff..7b42687 100644
--- a/tests/tests/text/src/android/text/cts/PrecomputedTextTest.java
+++ b/tests/tests/text/src/android/text/cts/PrecomputedTextTest.java
@@ -26,6 +26,8 @@
 import static org.junit.Assert.fail;
 
 import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.Typeface;
@@ -43,6 +45,10 @@
 import android.text.style.BackgroundColorSpan;
 import android.text.style.LocaleSpan;
 import android.text.style.TextAppearanceSpan;
+import android.text.style.TypefaceSpan;
+
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -620,4 +626,82 @@
         PrecomputedText.create("a\nb", param).getBounds(0, 3, rect);
     }
 
+    private static Bitmap drawToBitmap(@NonNull CharSequence cs,
+            @IntRange(from = 0) int start, @IntRange(from = 0) int end,
+            @IntRange(from = 0) int ctxStart, @IntRange(from = 0) int ctxEnd,
+            @NonNull TextPaint paint) {
+
+        Rect rect = new Rect();
+        paint.getTextBounds(cs.toString(), start, end, rect);
+        final Bitmap bmp = Bitmap.createBitmap(rect.width(),
+                rect.height(), Bitmap.Config.ARGB_8888);
+        final Canvas c = new Canvas(bmp);
+        c.save();
+        c.translate(0, 0);
+        c.drawTextRun(cs, start, end, ctxStart, ctxEnd, 0, 0, false /* isRtl */, paint);
+        c.restore();
+        return bmp;
+    }
+
+    private static void assertSameOutput(@NonNull CharSequence cs,
+            @IntRange(from = 0) int start, @IntRange(from = 0) int end,
+            @IntRange(from = 0) int ctxStart, @IntRange(from = 0) int ctxEnd,
+            @NonNull TextPaint paint) {
+        final Params params = new Params.Builder(paint).build();
+        final PrecomputedText pt = PrecomputedText.create(cs, params);
+
+        final Bitmap originalDrawOutput = drawToBitmap(cs, start, end, ctxStart, ctxEnd, paint);
+        final Bitmap precomputedDrawOutput = drawToBitmap(pt, start, end, ctxStart, ctxEnd, paint);
+        assertTrue(originalDrawOutput.sameAs(precomputedDrawOutput));
+    }
+
+    @Test
+    public void testDrawText() {
+        final TextPaint paint = new TextPaint();
+        paint.setTextSize(32.0f);
+
+        final SpannableStringBuilder ssb = new SpannableStringBuilder("Hello, World");
+        assertSameOutput(ssb, 0, ssb.length(), 0, ssb.length(), paint);
+        assertSameOutput(ssb, 3, ssb.length() - 3, 0, ssb.length(), paint);
+        assertSameOutput(ssb, 5, ssb.length() - 5, 2, ssb.length() - 2, paint);
+    }
+
+    @Test
+    public void testDrawText_MultiStyle() {
+        final TextPaint paint = new TextPaint();
+        paint.setTextSize(32.0f);
+
+        final SpannableStringBuilder ssb = new SpannableStringBuilder("Hello, World");
+        ssb.setSpan(new TypefaceSpan("serif"), 0, 6, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+        assertSameOutput(ssb, 0, ssb.length(), 0, ssb.length(), paint);
+        assertSameOutput(ssb, 3, ssb.length() - 3, 0, ssb.length(), paint);
+        assertSameOutput(ssb, 5, ssb.length() - 5, 2, ssb.length() - 2, paint);
+    }
+
+    @Test
+    public void testDrawText_MultiParagraph() {
+        final TextPaint paint = new TextPaint();
+        paint.setTextSize(32.0f);
+
+        final SpannableStringBuilder ssb = new SpannableStringBuilder(
+                "Hello, World\nHello, Android");
+
+        // The first line
+        final int firstLineLen = "Hello, World\n".length();
+        assertSameOutput(ssb, 0, firstLineLen, 0, firstLineLen, paint);
+        assertSameOutput(ssb, 3, firstLineLen - 3, 0, firstLineLen, paint);
+        assertSameOutput(ssb, 3, firstLineLen - 3, 2, firstLineLen - 2, paint);
+
+        // The second line.
+        assertSameOutput(ssb, firstLineLen, ssb.length(), firstLineLen, ssb.length(), paint);
+        assertSameOutput(ssb, firstLineLen + 3, ssb.length() - 3,
+                firstLineLen, ssb.length(), paint);
+        assertSameOutput(ssb, firstLineLen + 5, ssb.length() - 5,
+                firstLineLen + 2, ssb.length() - 2, paint);
+
+        // Across the paragraph
+        assertSameOutput(ssb, 0, ssb.length(), 0, ssb.length(), paint);
+        assertSameOutput(ssb, 3, firstLineLen - 3, 0, ssb.length(), paint);
+        assertSameOutput(ssb, 3, firstLineLen - 3, 2, ssb.length() - 2, paint);
+    }
 }
diff --git a/tests/tests/text/src/android/text/util/cts/LinkifyTest.java b/tests/tests/text/src/android/text/util/cts/LinkifyTest.java
index c0ad4b6..f9c2536 100644
--- a/tests/tests/text/src/android/text/util/cts/LinkifyTest.java
+++ b/tests/tests/text/src/android/text/util/cts/LinkifyTest.java
@@ -16,9 +16,15 @@
 
 package android.text.util.cts;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 
 import android.content.Context;
 import android.support.test.InstrumentationRegistry;
@@ -452,6 +458,68 @@
         assertEquals("https://android.com", spans[0].getURL());
     }
 
+    public class MyUrlSpan extends URLSpan {
+        public MyUrlSpan(String url) {
+            super(url);
+        }
+    }
+
+    public class MyUrlSpanFactory extends Linkify.UrlSpanFactory {
+        @Override
+        public URLSpan create(String url) {
+            return new MyUrlSpan(url);
+        }
+    }
+
+    @Test
+    public void testAddLinks_UrlSpanFactory_withSpannable() {
+        final String text = "a https://android.com a +1 123 456 7878 a android@android.com a";
+        final Spannable spannable = new SpannableString(text);
+        final int mask = Linkify.WEB_URLS | Linkify.PHONE_NUMBERS | Linkify.EMAIL_ADDRESSES;
+        final Linkify.UrlSpanFactory urlSpanFactory = spy(new MyUrlSpanFactory());
+
+        Linkify.addLinks(spannable, mask, urlSpanFactory);
+
+        verify(urlSpanFactory, times(3)).create(anyString());
+        final MyUrlSpan[] myUrlSpans = spannable.getSpans(0, spannable.length(), MyUrlSpan.class);
+        assertNotNull(myUrlSpans);
+        assertEquals(3, myUrlSpans.length);
+        assertEquals("https://android.com", myUrlSpans[0].getURL());
+        assertEquals("tel:+11234567878", myUrlSpans[1].getURL());
+        assertEquals("mailto:android@android.com", myUrlSpans[2].getURL());
+
+        final URLSpan[] urlSpans = spannable.getSpans(0, spannable.length(), URLSpan.class);
+        assertArrayEquals(myUrlSpans, urlSpans);
+    }
+
+    @Test
+    public void testAddLinks_UrlSpanFactory_withSpannableAndFilter() {
+        final String text = "google.pattern, test:AZ0101.pattern";
+        final SpannableString spannable = new SpannableString(text);
+        final Linkify.UrlSpanFactory urlSpanFactory = spy(new MyUrlSpanFactory());
+
+        Linkify.addLinks(spannable, LINKIFY_TEST_PATTERN, "test:", null /*schemes*/,
+                null /*matchFilter*/, null /*transformFilter*/, urlSpanFactory);
+
+        verify(urlSpanFactory, times(2)).create(anyString());
+
+        final MyUrlSpan[] myUrlSpans = spannable.getSpans(0, spannable.length(), MyUrlSpan.class);
+        assertNotNull(myUrlSpans);
+        assertEquals(2, myUrlSpans.length);
+        assertEquals("test:google.pattern", myUrlSpans[0].getURL());
+        assertEquals("test:AZ0101.pattern", myUrlSpans[1].getURL());
+
+        final URLSpan[] urlSpans = spannable.getSpans(0, spannable.length(), URLSpan.class);
+        assertArrayEquals(myUrlSpans, urlSpans);
+    }
+
+    @Test
+    public void testDefaultUrlSpanFactory() {
+        URLSpan urlSpan = new Linkify.UrlSpanFactory().create("some url");
+        assertNotNull(urlSpan);
+        assertEquals("some url", urlSpan.getURL());
+    }
+
     // WEB_URLS Related Tests
 
     @Test
diff --git a/tests/tests/toast/Android.mk b/tests/tests/toast/Android.mk
index bcf7b7e..9ab8465 100644
--- a/tests/tests/toast/Android.mk
+++ b/tests/tests/toast/Android.mk
@@ -29,6 +29,7 @@
 
 LOCAL_PACKAGE_NAME := CtsToastTestCases
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 25
 
 include $(BUILD_CTS_PACKAGE)
 
diff --git a/tests/tests/toast/AndroidTest.xml b/tests/tests/toast/AndroidTest.xml
index 0f00913..e56f6a4 100644
--- a/tests/tests/toast/AndroidTest.xml
+++ b/tests/tests/toast/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for Toast 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="instant_app" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/toastlegacy/Android.mk b/tests/tests/toastlegacy/Android.mk
index 217fe55..390222d 100644
--- a/tests/tests/toastlegacy/Android.mk
+++ b/tests/tests/toastlegacy/Android.mk
@@ -30,6 +30,7 @@
 
 LOCAL_PACKAGE_NAME := CtsToastLegacyTestCases
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 25
 
 include $(BUILD_CTS_PACKAGE)
 
diff --git a/tests/tests/transition/src/android/transition/cts/TransitionSetTest.java b/tests/tests/transition/src/android/transition/cts/TransitionSetTest.java
index 489fb2d..9fed9ee 100644
--- a/tests/tests/transition/src/android/transition/cts/TransitionSetTest.java
+++ b/tests/tests/transition/src/android/transition/cts/TransitionSetTest.java
@@ -18,18 +18,28 @@
 import static com.android.compatibility.common.util.CtsMockitoUtils.within;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.animation.TimeInterpolator;
+import android.graphics.Rect;
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.transition.ArcMotion;
 import android.transition.ChangeBounds;
 import android.transition.Fade;
+import android.transition.PathMotion;
 import android.transition.Transition;
+import android.transition.TransitionPropagation;
 import android.transition.TransitionSet;
+import android.transition.TransitionValues;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -123,5 +133,119 @@
         assertEquals(1, transitionSet.getTransitionCount());
         assertSame(changeBounds, transitionSet.getTransitionAt(0));
     }
+
+    @Test
+    public void testSetTransferValuesDuringAdd() throws Throwable {
+        Fade fade = new Fade();
+        fade.setDuration(500);
+        fade.setPropagation(new TestPropagation());
+        fade.setEpicenterCallback(new Transition.EpicenterCallback() {
+            @Override
+            public Rect onGetEpicenter(Transition transition) {
+                return null;
+            }
+        });
+        fade.setInterpolator(new AccelerateDecelerateInterpolator());
+        fade.setPathMotion(new ArcMotion());
+
+        TransitionSet transitionSet = new TransitionSet();
+        int duration = 100;
+        TestPropagation propagation = new TestPropagation();
+        TimeInterpolator interpolator = new DecelerateInterpolator();
+        PathMotion pathMotion = new ArcMotion();
+        Transition.EpicenterCallback epicenterCallback = new Transition.EpicenterCallback() {
+            @Override
+            public Rect onGetEpicenter(Transition transition) {
+                return null;
+            }
+        };
+        transitionSet.setDuration(duration);
+        transitionSet.setPropagation(propagation);
+        transitionSet.setInterpolator(interpolator);
+        transitionSet.setPathMotion(pathMotion);
+        transitionSet.setEpicenterCallback(epicenterCallback);
+
+        transitionSet.addTransition(fade);
+        assertEquals(duration, fade.getDuration());
+        assertSame(propagation, fade.getPropagation());
+        assertSame(interpolator, fade.getInterpolator());
+        assertSame(pathMotion, fade.getPathMotion());
+        assertSame(epicenterCallback, fade.getEpicenterCallback());
+    }
+
+    @Test
+    public void testSetTransferNullValuesDuringAdd() throws Throwable {
+        Fade fade = new Fade();
+        fade.setDuration(500);
+        fade.setPropagation(new TestPropagation());
+        fade.setEpicenterCallback(new Transition.EpicenterCallback() {
+            @Override
+            public Rect onGetEpicenter(Transition transition) {
+                return null;
+            }
+        });
+        fade.setInterpolator(new AccelerateDecelerateInterpolator());
+        fade.setPathMotion(new ArcMotion());
+
+        TransitionSet transitionSet = new TransitionSet();
+        transitionSet.setDuration(0);
+        transitionSet.setPropagation(null);
+        transitionSet.setInterpolator(null);
+        transitionSet.setPathMotion(null);
+        transitionSet.setEpicenterCallback(null);
+
+        transitionSet.addTransition(fade);
+        assertEquals(0, fade.getDuration());
+        assertNull(fade.getPropagation());
+        assertNull(fade.getInterpolator());
+        assertSame(transitionSet.getPathMotion(), fade.getPathMotion());
+        assertNull(fade.getEpicenterCallback());
+    }
+
+    @Test
+    public void testSetNoTransferValuesDuringAdd() throws Throwable {
+        Fade fade = new Fade();
+        int duration = 100;
+        TestPropagation propagation = new TestPropagation();
+        TimeInterpolator interpolator = new DecelerateInterpolator();
+        PathMotion pathMotion = new ArcMotion();
+        Transition.EpicenterCallback epicenterCallback = new Transition.EpicenterCallback() {
+            @Override
+            public Rect onGetEpicenter(Transition transition) {
+                return null;
+            }
+        };
+        fade.setDuration(duration);
+        fade.setPropagation(propagation);
+        fade.setInterpolator(interpolator);
+        fade.setPathMotion(pathMotion);
+        fade.setEpicenterCallback(epicenterCallback);
+
+        TransitionSet transitionSet = new TransitionSet();
+
+        transitionSet.addTransition(fade);
+        assertEquals(duration, fade.getDuration());
+        assertSame(propagation, fade.getPropagation());
+        assertSame(interpolator, fade.getInterpolator());
+        assertSame(pathMotion, fade.getPathMotion());
+        assertSame(epicenterCallback, fade.getEpicenterCallback());
+    }
+
+    private static class TestPropagation extends TransitionPropagation {
+        @Override
+        public long getStartDelay(ViewGroup sceneRoot, Transition transition,
+                TransitionValues startValues, TransitionValues endValues) {
+            return 0;
+        }
+
+        @Override
+        public void captureValues(TransitionValues transitionValues) {
+        }
+
+        @Override
+        public String[] getPropagationProperties() {
+            return new String[] { };
+        }
+    }
 }
 
diff --git a/tests/tests/uiautomation/AndroidManifest.xml b/tests/tests/uiautomation/AndroidManifest.xml
index 0573a12..70c51ae 100644
--- a/tests/tests/uiautomation/AndroidManifest.xml
+++ b/tests/tests/uiautomation/AndroidManifest.xml
@@ -22,6 +22,7 @@
 
   <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
   <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+  <uses-permission android:name="android.permission.CAMERA" />
 
   <application android:theme="@android:style/Theme.Holo.NoActionBar" >
 
diff --git a/tests/tests/uiautomation/AndroidTest.xml b/tests/tests/uiautomation/AndroidTest.xml
index c4bcdd6..604a35e 100644
--- a/tests/tests/uiautomation/AndroidTest.xml
+++ b/tests/tests/uiautomation/AndroidTest.xml
@@ -16,10 +16,14 @@
 <configuration description="Config for CTS UI Automation 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="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <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" />
+    </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.app.uiautomation.cts" />
         <option name="runtime-hint" value="6m42s" />
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 357ffb3..bf38e5c0 100755
--- a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
@@ -16,18 +16,30 @@
 
 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.accessibilityservice.AccessibilityServiceInfo;
 import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.Instrumentation;
 import android.app.UiAutomation;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
-import android.os.ParcelFileDescriptor;
+import android.content.pm.PackageManager;
+import android.os.Process;
 import android.os.SystemClock;
 import android.platform.test.annotations.AppModeFull;
 import android.platform.test.annotations.Presubmit;
 import android.provider.Settings;
-import android.test.InstrumentationTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
 import android.view.FrameStats;
 import android.view.WindowAnimationFrameStats;
 import android.view.WindowContentFrameStats;
@@ -36,6 +48,10 @@
 import android.view.accessibility.AccessibilityWindowInfo;
 import android.widget.ListView;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.io.IOException;
 import java.util.List;
 import java.util.concurrent.TimeoutException;
@@ -43,7 +59,8 @@
 /**
  * Tests for the UiAutomation APIs.
  */
-public class UiAutomationTest extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class UiAutomationTest {
     private static final long QUIET_TIME_TO_BE_CONSIDERED_IDLE_STATE = 1000;//ms
 
     private static final long TOTAL_TIME_TO_WAIT_FOR_IDLE_STATE = 1000 * 10;//ms
@@ -52,9 +69,8 @@
     private static final String COMPONENT_NAME_SEPARATOR = ":";
     private static final int TIMEOUT_FOR_SERVICE_ENABLE = 10000; // millis; 10s
 
-    @Override
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
         UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
         AccessibilityServiceInfo info = uiAutomation.getServiceInfo();
         info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
@@ -62,7 +78,66 @@
         grantWriteSecureSettingsPermission(uiAutomation);
     }
 
-    @Presubmit
+    @Test
+    public void testAdoptShellPermissions() {
+        final Context context = getInstrumentation().getContext();
+        final ActivityManager activityManager = context.getSystemService(ActivityManager.class);
+        final PackageManager packageManager = context.getPackageManager();
+
+        // Try to access APIs guarded by a platform defined signature permissions
+        try {
+            activityManager.getPackageImportance("foo.bar.baz");
+            fail("Should not be able to access APIs protected by a permission apps cannot get");
+        } catch (SecurityException e) {
+            /* expected */
+        }
+        try {
+            packageManager.grantRuntimePermission(context.getPackageName(),
+                    Manifest.permission.CAMERA, Process.myUserHandle());
+            fail("Should not be able to access APIs protected by a permission apps cannot get");
+        } catch (SecurityException e) {
+            /* expected */
+        }
+
+        // Access APIs guarded by a platform defined signature permissions
+        try {
+            getInstrumentation().getUiAutomation().adoptShellPermissionIdentity();
+
+            // Access APIs guarded by a platform defined signature permission
+            activityManager.getPackageImportance("foo.bar.baz");
+
+            // Grant ourselves a runtime permission (was granted at install)
+            assertSame(packageManager.checkPermission(Manifest.permission.CAMERA,
+                    context.getPackageName()), PackageManager.PERMISSION_DENIED);
+            packageManager.grantRuntimePermission(context.getPackageName(),
+                    Manifest.permission.CAMERA, 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,
+                context.getPackageName()), PackageManager.PERMISSION_GRANTED);
+
+
+        // Try to access APIs guarded by a platform defined signature permissions
+        try {
+            activityManager.getPackageImportance("foo.bar.baz");
+            fail("Should not be able to access APIs protected by a permission apps cannot get");
+        } catch (SecurityException e) {
+            /* expected */
+        }
+        try {
+            packageManager.revokeRuntimePermission(context.getPackageName(),
+                    Manifest.permission.CAMERA, Process.myUserHandle());
+            fail("Should not be able to access APIs protected by a permission apps cannot get");
+        } catch (SecurityException e) {
+            /* expected */
+        }
+    }
+
+    @Test
     public void testWindowContentFrameStats() throws Exception {
         Activity activity = null;
         try {
@@ -121,6 +196,7 @@
         }
     }
 
+    @Test
     public void testWindowContentFrameStatsNoAnimation() throws Exception {
         Activity activity = null;
         try {
@@ -172,6 +248,7 @@
     }
 
     @Presubmit
+    @Test
     public void testWindowAnimationFrameStats() throws Exception {
         Activity firstActivity = null;
         Activity secondActivity = null;
@@ -239,6 +316,7 @@
         }
     }
 
+    @Test
     public void testWindowAnimationFrameStatsNoAnimation() throws Exception {
         UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
 
@@ -269,6 +347,7 @@
     }
 
     @Presubmit
+    @Test
     public void testUsingUiAutomationAfterDestroy_shouldThrowException() {
         UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
         uiAutomation.destroy();
@@ -280,8 +359,8 @@
     }
 
     @AppModeFull
-    public void testDontSuppressAccessibility_canStartA11yService() throws IOException,
-            InterruptedException {
+    @Test
+    public void testDontSuppressAccessibility_canStartA11yService() throws Exception {
         turnAccessibilityOff();
         try {
             getInstrumentation()
@@ -294,7 +373,8 @@
     }
 
     @AppModeFull
-    public void testServiceWithNoFlags_shutsDownA11yService() throws IOException {
+    @Test
+    public void testServiceWithNoFlags_shutsDownA11yService() throws Exception {
         turnAccessibilityOff();
         try {
             UiAutomation uiAutomation = getInstrumentation()
@@ -311,8 +391,9 @@
     }
 
     @AppModeFull
+    @Test
     public void testServiceSupressingA11yServices_a11yServiceStartsWhenDestroyed()
-            throws IOException, InterruptedException {
+            throws Exception {
         turnAccessibilityOff();
         try {
             UiAutomation uiAutomation = getInstrumentation()
@@ -331,8 +412,9 @@
     }
 
     @AppModeFull
+    @Test
     public void testServiceSupressingA11yServices_a11yServiceStartsWhenFlagsChange()
-            throws IOException, InterruptedException {
+            throws Exception {
         turnAccessibilityOff();
         try {
             getInstrumentation()
@@ -377,7 +459,7 @@
                 QUIET_TIME_TO_BE_CONSIDERED_IDLE_STATE, TOTAL_TIME_TO_WAIT_FOR_IDLE_STATE);
     }
 
-    private void grantWriteSecureSettingsPermission(UiAutomation uiAutomation) throws IOException {
+    private void grantWriteSecureSettingsPermission(UiAutomation uiAutomation) {
         uiAutomation.grantRuntimePermission(getInstrumentation().getContext().getPackageName(),
                 android.Manifest.permission.WRITE_SECURE_SETTINGS);
     }
@@ -512,4 +594,8 @@
         }
         return -1;
     }
+
+    private Instrumentation getInstrumentation() {
+        return InstrumentationRegistry.getInstrumentation();
+    }
 }
diff --git a/tests/tests/uidisolation/AndroidTest.xml b/tests/tests/uidisolation/AndroidTest.xml
index 0a83f12..590627b 100644
--- a/tests/tests/uidisolation/AndroidTest.xml
+++ b/tests/tests/uidisolation/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS UID Isolation test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="security" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/uirendering/Android.bp b/tests/tests/uirendering/Android.bp
new file mode 100644
index 0000000..6c8bb8fa
--- /dev/null
+++ b/tests/tests/uirendering/Android.bp
@@ -0,0 +1,38 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+    name: "CtsUiRenderingTestCases",
+    sdk_version: "test_current",
+
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+
+    static_libs: [
+        "compatibility-device-util",
+        "ctsdeviceutillegacy",
+        "mockito-target-minus-junit4",
+        "android-support-test",
+    ],
+
+    libs: ["android.test.runner.stubs"],
+
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+}
\ No newline at end of file
diff --git a/tests/tests/uirendering/res/layout/test_content_canvasclientview.xml b/tests/tests/uirendering/res/layout/skew_layout.xml
similarity index 61%
rename from tests/tests/uirendering/res/layout/test_content_canvasclientview.xml
rename to tests/tests/uirendering/res/layout/skew_layout.xml
index 9f8a139..0b67a8f 100644
--- a/tests/tests/uirendering/res/layout/test_content_canvasclientview.xml
+++ b/tests/tests/uirendering/res/layout/skew_layout.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2015 The Android Open Source Project
+<?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.
@@ -12,7 +13,14 @@
        See the License for the specific language governing permissions and
        limitations under the License.
   -->
-<android.uirendering.cts.testinfrastructure.CanvasClientView
+<android.uirendering.cts.testclasses.view.SkewLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="@dimen/test_width"
-    android:layout_height="@dimen/test_height"/>
\ No newline at end of file
+    android:layout_height="@dimen/test_height">
+    <View
+        android:id="@+id/view1"
+        android:layout_width="50dp"
+        android:layout_height="50dp"
+        android:background="#F00"
+        android:elevation="8dp" />
+</android.uirendering.cts.testclasses.view.SkewLayout>
diff --git a/tests/tests/uirendering/res/layout/test_container.xml b/tests/tests/uirendering/res/layout/test_container.xml
index deff9de..fd7b4e2 100644
--- a/tests/tests/uirendering/res/layout/test_container.xml
+++ b/tests/tests/uirendering/res/layout/test_container.xml
@@ -20,9 +20,5 @@
         android:id="@+id/test_content_wrapper"
         android:layout_width="@dimen/test_width"
         android:layout_height="@dimen/test_height">
-        <ViewStub
-            android:id="@+id/test_content_stub"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"/>
     </FrameLayout>
 </FrameLayout>
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/CameraTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/CameraTests.java
new file mode 100644
index 0000000..c46fbaf
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/CameraTests.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uirendering.cts.testclasses;
+
+import android.graphics.Camera;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.uirendering.cts.bitmapverifiers.RectVerifier;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class CameraTests extends ActivityTestBase {
+    @Test
+    public void testBasicTranslate() {
+        createTest()
+                .addCanvasClient((canvas, width, height) -> {
+                    Paint paint = new Paint();
+                    paint.setAntiAlias(false);
+                    paint.setColor(Color.BLUE);
+                    Camera camera = new Camera();
+                    camera.translate(0, 50, 0);
+                    camera.applyToCanvas(canvas);
+                    canvas.drawRect(0, 50, 100, 100, paint);
+                })
+                .runWithVerifier(new RectVerifier(Color.WHITE, Color.BLUE,
+                        new Rect(0, 0, 100, 50)));
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
index cce9bb0..d771212 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
@@ -253,4 +253,23 @@
                 .runWithVerifier(new RectVerifier(Color.WHITE, Color.BLACK,
                         new Rect(20, 20, 70, 70)));
     }
+
+    @Test
+    public void testBlackTriangleVertices() {
+        createTest()
+                .addCanvasClient((canvas, width, height) -> {
+                    float[] vertices = new float[6];
+                    vertices[0] = width / 2.0f;
+                    vertices[1] = 0;
+                    vertices[2] = width;
+                    vertices[3] = height;
+                    vertices[4] = 0;
+                    vertices[5] = height;
+                    int[] colors = new int[] { Color.BLACK, Color.BLACK, Color.BLACK };
+                    canvas.drawVertices(Canvas.VertexMode.TRIANGLES, vertices.length, vertices, 0,
+                            null, 0, colors, 0, null, 0, 0,
+                            new Paint());
+                })
+                .runWithComparer(mExactComparer);
+    }
 }
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java
index abaf96e..7d4b403 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java
@@ -16,7 +16,6 @@
 package android.uirendering.cts.testclasses;
 
 import android.graphics.Color;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
@@ -36,14 +35,6 @@
 @RunWith(AndroidJUnit4.class)
 public class InfrastructureTests extends ActivityTestBase {
 
-    @Test
-    public void testScreenshot() {
-        for (int i = 0 ; i < 500 ; i ++) {
-            takeScreenshot(new TestPositionInfo(new Point(), new Point()));
-            System.gc();
-        }
-    }
-
     /**
      * Ensure that both render paths are producing independent output. We do this
      * by verifying that two paths that should render differently *do* render
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 b4c7798..49b6fc9 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
@@ -49,7 +49,6 @@
 
 import androidx.annotation.ColorInt;
 
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -193,7 +192,7 @@
     @Test
     public void testLayerClear() {
         ViewInitializer initializer = new ViewInitializer() {
-            ValueAnimator mAnimator;
+            ValueAnimator mAnimator = ValueAnimator.ofInt(0, 20);
             @Override
             public void initializeView(View view) {
                 FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout);
@@ -207,7 +206,6 @@
                 child.setLayerType(View.LAYER_TYPE_HARDWARE, null);
                 root.addView(child);
 
-                mAnimator = ValueAnimator.ofInt(0, 20);
                 mAnimator.setRepeatMode(ValueAnimator.REVERSE);
                 mAnimator.setRepeatCount(ValueAnimator.INFINITE);
                 mAnimator.setDuration(200);
@@ -539,12 +537,19 @@
 
     @LargeTest
     @Test
-    @Ignore // b/109839751
     public void testWebViewWithUnclippedLayer() {
         if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {
             return; // no WebView to run test on
         }
         CountDownLatch hwFence = new CountDownLatch(1);
+        Point[] testPoints = new Point[] {
+            // solid area
+            new Point(0, 0),
+            new Point(0, TEST_HEIGHT - 1),
+            // fade area
+            new Point(0, TEST_HEIGHT - 10),
+            new Point(0, TEST_HEIGHT - 5)
+        };
         createTest()
                 .addLayout(R.layout.test_content_webview, (ViewInitializer) view -> {
                     WebView webview = view.requireViewById(R.id.webview);
@@ -554,32 +559,39 @@
                     webview.setVerticalScrollBarEnabled(false);
                     webview.setLayerType(View.LAYER_TYPE_HARDWARE, null);
 
+                    // 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;
                 }, true, hwFence)
                 .runWithVerifier(new SamplePointVerifier(
-                        new Point[] {
-                                // solid area
-                                new Point(0, 0),
-                                new Point(0, TEST_HEIGHT - 1),
-                                // fade area
-                                new Point(0, TEST_HEIGHT - 10),
-                                new Point(0, TEST_HEIGHT - 5)
-                        },
+                        testPoints,
                         new int[] {
                                 Color.BLUE,
                                 Color.WHITE,
-                                0xffb3b3ff, // white blended with blue
+                                0xffc5c5ff, // white blended with blue
                                 0xffdbdbff  // white blended with blue
-                        }));
+                        }, 50));
     }
 
     @LargeTest
     @Test
-    @Ignore // b/109839751
     public void testWebViewWithUnclippedLayerAndComplexClip() {
         if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {
             return; // no WebView to run test on
         }
         CountDownLatch hwFence = new CountDownLatch(1);
+        Point[] testPoints = new Point[] {
+            // solid white area
+            new Point(0, 0),
+            new Point(0, TEST_HEIGHT - 1),
+            // solid blue area
+            new Point(TEST_WIDTH / 2 , 5),
+            // fade area
+            new Point(TEST_WIDTH / 2, TEST_HEIGHT - 10),
+            new Point(TEST_WIDTH / 2, TEST_HEIGHT - 5)
+        };
         createTest()
                 .addLayout(R.layout.circle_clipped_webview, (ViewInitializer) view -> {
                     WebView webview = view.requireViewById(R.id.webview);
@@ -588,26 +600,21 @@
                     webview.setVerticalFadingEdgeEnabled(true);
                     webview.setVerticalScrollBarEnabled(false);
                     webview.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-
+                    // 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;
                 }, true, hwFence)
                 .runWithVerifier(new SamplePointVerifier(
-                        new Point[] {
-                                // solid white area
-                                new Point(0, 0),
-                                new Point(0, TEST_HEIGHT - 1),
-                                // solid blue area
-                                new Point(TEST_WIDTH / 2 , 5),
-                                // fade area
-                                new Point(TEST_WIDTH / 2, TEST_HEIGHT - 10),
-                                new Point(TEST_WIDTH / 2, TEST_HEIGHT - 5)
-                        },
+                        testPoints,
                         new int[] {
                                 Color.WHITE,
                                 Color.WHITE,
                                 Color.BLUE,
-                                0xffb3b3ff, // white blended with blue
+                                0xffc5c5ff, // white blended with blue
                                 0xffdbdbff  // white blended with blue
-                        }));
+                        }, 50));
     }
 
     @LargeTest
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java
index 3d1c10e..285db5a 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java
@@ -16,12 +16,14 @@
 package android.uirendering.cts.testclasses;
 
 import android.graphics.Color;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.uirendering.cts.R;
 import android.uirendering.cts.bitmapverifiers.ColorVerifier;
 import android.uirendering.cts.bitmapverifiers.RectVerifier;
+import android.uirendering.cts.bitmapverifiers.SamplePointVerifier;
 import android.uirendering.cts.testinfrastructure.ActivityTestBase;
 
 import org.junit.Test;
@@ -41,5 +43,21 @@
         createTest().addLayout(R.layout.simple_rect_layout, null, false).runWithVerifier(
                 new RectVerifier(Color.WHITE, Color.BLUE, new Rect(5, 5, 85, 85)));
     }
+
+    @Test
+    public void testTransformChildViewLayout() {
+        createTest()
+                // Verify skew tranform matrix is applied for child view.
+                .addLayout(R.layout.skew_layout, null)
+                .runWithVerifier(new SamplePointVerifier(
+                        new Point[] {
+                                new Point(20, 10),
+                                new Point(0, TEST_HEIGHT - 1)
+                        },
+                        new int[] {
+                                Color.RED,
+                                Color.WHITE
+                        }));
+    }
 }
 
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/TextureViewTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/TextureViewTests.java
index ad20dd7..e25260b 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/TextureViewTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/TextureViewTests.java
@@ -25,8 +25,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.SurfaceTexture;
-import androidx.annotation.ColorInt;
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.LargeTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.uirendering.cts.R;
 import android.uirendering.cts.bitmapverifiers.ColorVerifier;
@@ -38,6 +37,8 @@
 import android.view.TextureView.SurfaceTextureListener;
 import android.view.ViewGroup;
 
+import androidx.annotation.ColorInt;
+
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Assert;
@@ -47,7 +48,8 @@
 
 import java.util.concurrent.CountDownLatch;
 
-@MediumTest
+// Temporarily mark @LargeTest to surpress it from presubmit b/37773896
+@LargeTest
 @RunWith(AndroidJUnit4.class)
 public class TextureViewTests extends ActivityTestBase {
 
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/VectorDrawableTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/VectorDrawableTests.java
index f4db101..1368420 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/VectorDrawableTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/VectorDrawableTests.java
@@ -16,9 +16,6 @@
 
 package android.uirendering.cts.testclasses;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -31,14 +28,15 @@
 import android.uirendering.cts.bitmapverifiers.RectVerifier;
 import android.uirendering.cts.bitmapverifiers.SamplePointVerifier;
 import android.uirendering.cts.testinfrastructure.ActivityTestBase;
-
-import android.uirendering.cts.testinfrastructure.ViewInitializer;
+import android.view.Gravity;
 import android.view.View;
+import android.view.ViewTreeObserver;
 import android.widget.FrameLayout;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
+
 import java.util.concurrent.CountDownLatch;
-import android.animation.Animator;
 
 @MediumTest
 @RunWith(AndroidJUnit4.class)
@@ -82,68 +80,51 @@
      */
     @Test
     public void testInvalidateCache() {
-        CountDownLatch testFinishedFence = new CountDownLatch(1);
-
-        ViewInitializer initializer = new ViewInitializer() {
-            ValueAnimator mAnimator;
-
-            @Override
-            public void initializeView(View view) {
-                FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout);
-                root.setBackgroundColor(Color.BLUE);
-
-                final VectorDrawableView child = new VectorDrawableView(view.getContext());
-
-                child.setLayoutParams(new FrameLayout.LayoutParams(ActivityTestBase.TEST_WIDTH,
-                    ActivityTestBase.TEST_HEIGHT));
-                // VectorDrawable is a red circle drawn on top of a blue background.
-                // The first frame has VectorDrawable size set to 1x1 pixels, which deforms
-                // the red circle into a 1x1 red-ish square.
-                // An animation grows VectorDrawable bounds from 0x0 to 90x90. If VD cache is
-                // refreshed, then we should see a red circle on top of a blue background.
-                // If VD cache is stale, then VD will upscale the original 1x1 cached image to
-                // 90x90 red-ish square.
-                // At the end of the animation, we verify the color of top left pixel, which should
-                // be a blue background pixel.
-                child.setVDSize(new Rect(0, 0, 2, 2)); //first draw with VD size set to 1x1 pixels.
-                root.addView(child);
-
-                mAnimator = ValueAnimator.ofFloat(0, 1);
-                mAnimator.setRepeatCount(0);
-                mAnimator.setDuration(400);
-                mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                    @Override
-                    public void onAnimationUpdate(ValueAnimator animation) {
-                        float progress = (float) mAnimator.getAnimatedValue();
-                        child.setVDSize(new Rect(0, 0, (int)(progress*child.getWidth()),
-                                (int)(progress*child.getHeight())));
-                        child.invalidate();
-                    }
-                });
-                mAnimator.addListener(
-                    new AnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            super.onAnimationEnd(animation);
-                            testFinishedFence.countDown();
-                        }
-                    });
-
-                mAnimator.start();
-            }
-
-            @Override
-            public void teardownView() {
-              mAnimator.cancel();
-            }
-        };
-
+        final CountDownLatch fence = new CountDownLatch(1);
         createTest()
-            .addLayout(R.layout.frame_layout, initializer, true, testFinishedFence)
-            .runWithVerifier(new SamplePointVerifier(
-                new Point[] { new Point(0, 0) },
-                new int[] { 0xff0000ff }
-            ));
+                .addLayout(R.layout.frame_layout, view -> {
+                    FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout);
+                    root.setBackgroundColor(Color.BLUE);
+                    final VectorDrawableView child = new VectorDrawableView(view.getContext());
+                    // VectorDrawable is a red circle drawn on top of a blue background.
+                    // The first frame has VectorDrawable size set to 1x1 pixels, which deforms
+                    // the red circle into a 1x1 red-ish square.
+                    // After first draw we grow VectorDrawable bounds from 0x0 to 90x90. If VD cache
+                    // is refreshed, then we should see a red circle on top of a blue background.
+                    // If VD cache is stale, then VD will upscale the original 1x1 cached image to
+                    // 90x90 red-ish square.
+                    // At the end we verify the color of top left pixel, which should be a blue
+                    // background pixel.
+                    child.setVDSize(new Rect(0, 0, 2, 2));
+
+                    root.addView(child, new FrameLayout.LayoutParams(TEST_WIDTH, TEST_HEIGHT,
+                              Gravity.TOP | Gravity.LEFT));
+
+                    // Post a new VD size a few frames in, so that the initial draw completes.
+                    root.getViewTreeObserver().addOnPreDrawListener(
+                            new ViewTreeObserver.OnPreDrawListener() {
+                                int mDrawCount = 0;
+                                @Override
+                                public boolean onPreDraw() {
+                                    if (mDrawCount++ == 5) {
+                                        child.setVDSize(new Rect(0, 0,
+                                                (int) (child.getWidth()),
+                                                (int) (child.getHeight())));
+                                        child.invalidate();
+
+                                        root.getViewTreeObserver().removeOnPreDrawListener(this);
+                                        root.post(fence::countDown);
+                                    } else {
+                                        root.postInvalidate();
+                                    }
+                                    return true;
+                                }
+                            });
+                }, true, fence)
+                .runWithVerifier(new SamplePointVerifier(
+                    new Point[] { new Point(0, 0) },
+                    new int[] { 0xff0000ff }
+                ));
     }
 }
 
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/SkewLayout.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/SkewLayout.java
new file mode 100644
index 0000000..f9b8a3c
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/SkewLayout.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uirendering.cts.testclasses.view;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+
+public class SkewLayout extends FrameLayout {
+
+    public SkewLayout(Context context) {
+        super(context);
+    }
+
+    public SkewLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public SkewLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+        canvas.save();
+        canvas.skew(0.5f, 0f);
+        boolean result = super.drawChild(canvas, child, drawingTime);
+        canvas.restore();
+        return result;
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
index 6f2ac81..c04980c 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
@@ -21,7 +21,6 @@
 import android.graphics.Bitmap.Config;
 import android.graphics.Point;
 import android.graphics.Rect;
-import androidx.annotation.Nullable;
 import android.support.test.InstrumentationRegistry;
 import android.uirendering.cts.bitmapcomparers.BitmapComparer;
 import android.uirendering.cts.bitmapverifiers.BitmapVerifier;
@@ -29,6 +28,8 @@
 import android.util.Log;
 import android.view.PixelCopy;
 
+import androidx.annotation.Nullable;
+
 import com.android.compatibility.common.util.SynchronousPixelCopy;
 
 import org.junit.After;
@@ -118,7 +119,7 @@
         }
     }
 
-    public Bitmap takeScreenshot(TestPositionInfo testPositionInfo) {
+    private Bitmap takeScreenshot(TestPositionInfo testPositionInfo) {
         if (mScreenshotter == null) {
             SynchronousPixelCopy copy = new SynchronousPixelCopy();
             Bitmap dest = Bitmap.createBitmap(
@@ -135,7 +136,8 @@
             return mScreenshotter.takeScreenshot(testPositionInfo);
         }
     }
-    protected TestPositionInfo runRenderSpec(TestCase testCase) {
+
+    private TestPositionInfo runRenderSpec(TestCase testCase) {
         TestPositionInfo testPositionInfo = getActivity().enqueueRenderSpecAndWait(
                 testCase.layoutID, testCase.canvasClient,
                 testCase.viewInitializer, testCase.useHardware, testCase.usePicture);
@@ -146,6 +148,9 @@
             } catch (InterruptedException e) {
                 throw new RuntimeException("readyFence didn't signal within 5 seconds");
             }
+            // The fence setup may have (and probably did) changed things that we need to wait
+            // have been drawn. So force an invalidate() and wait for it to finish
+            getActivity().waitForRedraw();
         }
         return testPositionInfo;
     }
@@ -153,7 +158,7 @@
     /**
      * Used to execute a specific part of a test and get the resultant bitmap
      */
-    protected Bitmap captureRenderSpec(TestCase testCase) {
+    private Bitmap captureRenderSpec(TestCase testCase) {
         return takeScreenshot(runRenderSpec(testCase));
     }
 
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
deleted file mode 100644
index d876c6c..0000000
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
+++ /dev/null
@@ -1,278 +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 android.uirendering.cts.testinfrastructure;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-
-import android.app.Activity;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
-import android.graphics.Point;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import androidx.annotation.Nullable;
-import android.uirendering.cts.R;
-import android.util.Log;
-import android.util.Pair;
-import android.view.FrameMetrics;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewStub;
-import android.view.ViewTreeObserver;
-import android.view.Window;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * A generic activity that uses a view specified by the user.
- */
-public class DrawActivity extends Activity {
-    static final String EXTRA_WIDE_COLOR_GAMUT = "DrawActivity.WIDE_COLOR_GAMUT";
-
-    private final static long TIME_OUT_MS = 10000;
-    private final Object mLock = new Object();
-    private ActivityTestBase.TestPositionInfo mPositionInfo;
-
-    private Handler mHandler;
-    private View mView;
-    private View mViewWrapper;
-    private boolean mOnTv;
-    private DrawMonitor mDrawMonitor;
-
-    public void onCreate(Bundle bundle){
-        super.onCreate(bundle);
-        getWindow().getDecorView().setSystemUiVisibility(
-                View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN);
-        if (getIntent().getBooleanExtra(EXTRA_WIDE_COLOR_GAMUT, false)) {
-            getWindow().setColorMode(ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT);
-        }
-        mHandler = new RenderSpecHandler();
-        int uiMode = getResources().getConfiguration().uiMode;
-        mOnTv = (uiMode & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION;
-        mDrawMonitor = new DrawMonitor(getWindow());
-    }
-
-    public boolean getOnTv() {
-        return mOnTv;
-    }
-
-    public ActivityTestBase.TestPositionInfo enqueueRenderSpecAndWait(int layoutId,
-            CanvasClient canvasClient, @Nullable ViewInitializer viewInitializer,
-            boolean useHardware, boolean usePicture) {
-        ((RenderSpecHandler) mHandler).setViewInitializer(viewInitializer);
-        int arg2 = (useHardware ? View.LAYER_TYPE_NONE : View.LAYER_TYPE_SOFTWARE);
-        synchronized (mLock) {
-            if (canvasClient != null) {
-                mHandler.obtainMessage(RenderSpecHandler.CANVAS_MSG, usePicture ? 1 : 0,
-                        arg2, canvasClient).sendToTarget();
-            } else {
-                mHandler.obtainMessage(RenderSpecHandler.LAYOUT_MSG, layoutId, arg2).sendToTarget();
-            }
-
-            try {
-                mLock.wait(TIME_OUT_MS);
-            } catch (InterruptedException e) {
-                e.printStackTrace();
-            }
-        }
-        assertNotNull("Timeout waiting for draw", mPositionInfo);
-        return mPositionInfo;
-    }
-
-    public void reset() {
-        CountDownLatch fence = new CountDownLatch(1);
-        mHandler.obtainMessage(RenderSpecHandler.RESET_MSG, fence).sendToTarget();
-        try {
-            if (!fence.await(10, TimeUnit.SECONDS)) {
-                fail("Timeout exception");
-            }
-        } catch (InterruptedException ex) {
-            fail(ex.getMessage());
-        }
-    }
-
-    private ViewInitializer mViewInitializer;
-
-    private void notifyOnDrawCompleted() {
-        DrawCounterListener onDrawListener = new DrawCounterListener();
-        mView.getViewTreeObserver().addOnDrawListener(onDrawListener);
-        mView.invalidate();
-    }
-
-    private class RenderSpecHandler extends Handler {
-        public static final int RESET_MSG = 0;
-        public static final int LAYOUT_MSG = 1;
-        public static final int CANVAS_MSG = 2;
-
-
-        public void setViewInitializer(ViewInitializer viewInitializer) {
-            mViewInitializer = viewInitializer;
-        }
-
-        public void handleMessage(Message message) {
-            Log.d("UiRendering", "message of type " + message.what);
-            if (message.what == RESET_MSG) {
-                ((ViewGroup)findViewById(android.R.id.content)).removeAllViews();
-                ((CountDownLatch)message.obj).countDown();
-                return;
-            }
-            setContentView(R.layout.test_container);
-            ViewStub stub = (ViewStub) findViewById(R.id.test_content_stub);
-            mViewWrapper = findViewById(R.id.test_content_wrapper);
-            switch (message.what) {
-                case LAYOUT_MSG: {
-                    stub.setLayoutResource(message.arg1);
-                    mView = stub.inflate();
-                } break;
-
-                case CANVAS_MSG: {
-                    stub.setLayoutResource(R.layout.test_content_canvasclientview);
-                    mView = stub.inflate();
-                    ((CanvasClientView) mView).setCanvasClient((CanvasClient) (message.obj));
-                    if (message.arg1 != 0) {
-                        ((CanvasClientView) mView).setUsePicture(true);
-                    }
-                } break;
-            }
-
-            if (mView == null) {
-                throw new IllegalStateException("failed to inflate test content");
-            }
-
-            if (mViewInitializer != null) {
-                mViewInitializer.initializeView(mView);
-            }
-
-            // set layer on wrapper parent of view, so view initializer
-            // can control layer type of View under test.
-            mViewWrapper.setLayerType(message.arg2, null);
-
-            notifyOnDrawCompleted();
-        }
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        if (mViewInitializer != null) {
-            mViewInitializer.teardownView();
-        }
-    }
-
-    @Override
-    public void finish() {
-        // Ignore
-    }
-
-    /** Call this when all the tests that use this activity have completed.
-     * This will then clean up any internal state and finish the activity. */
-    public void allTestsFinished() {
-        super.finish();
-    }
-
-    private class DrawCounterListener implements ViewTreeObserver.OnDrawListener {
-        private static final int DEBUG_REQUIRE_EXTRA_FRAMES = 1;
-        private int mDrawCount = 0;
-
-        @Override
-        public void onDraw() {
-            if (++mDrawCount <= DEBUG_REQUIRE_EXTRA_FRAMES) {
-                mView.postInvalidate();
-                return;
-            }
-
-            long vsyncMillis = mView.getDrawingTime();
-
-            mView.post(() -> mView.getViewTreeObserver().removeOnDrawListener(this));
-
-            mDrawMonitor.notifyWhenDrawn(vsyncMillis, () -> {
-                final int[] location = new int[2];
-                mViewWrapper.getLocationInWindow(location);
-                Point surfaceOffset = new Point(location[0], location[1]);
-                mViewWrapper.getLocationOnScreen(location);
-                Point screenOffset = new Point(location[0], location[1]);
-                synchronized (mLock) {
-                    mPositionInfo = new ActivityTestBase.TestPositionInfo(
-                            surfaceOffset, screenOffset);
-                    mLock.notify();
-                }
-            });
-        }
-    }
-
-    private static class DrawMonitor {
-
-        private ArrayList<Pair<Long, Runnable>> mListeners = new ArrayList<>();
-
-        private DrawMonitor(Window window) {
-            window.addOnFrameMetricsAvailableListener(this::onFrameMetricsAvailable,
-                    new Handler());
-        }
-
-        private void onFrameMetricsAvailable(Window window, FrameMetrics frameMetrics,
-                /* This isn't actually unused as it's necessary for the method handle */
-                @SuppressWarnings("unused") int dropCountSinceLastInvocation) {
-            ArrayList<Runnable> toInvoke = null;
-            synchronized (mListeners) {
-                if (mListeners.size() == 0) {
-                    return;
-                }
-
-                long vsyncAtMillis = TimeUnit.NANOSECONDS.convert(frameMetrics
-                        .getMetric(FrameMetrics.VSYNC_TIMESTAMP), TimeUnit.MILLISECONDS);
-                boolean isUiThreadDraw = frameMetrics.getMetric(FrameMetrics.DRAW_DURATION) > 0;
-
-                Iterator<Pair<Long, Runnable>> iter = mListeners.iterator();
-                while (iter.hasNext()) {
-                    Pair<Long, Runnable> listener = iter.next();
-                    if ((listener.first == vsyncAtMillis && isUiThreadDraw)
-                            || (listener.first < vsyncAtMillis)) {
-                        if (toInvoke == null) {
-                            toInvoke = new ArrayList<>();
-                        }
-                        Log.d("UiRendering", "adding listener for vsync " + listener.first
-                                + "; got vsync timestamp: " + vsyncAtMillis
-                                + " with isUiThreadDraw " + isUiThreadDraw);
-                        toInvoke.add(listener.second);
-                        iter.remove();
-                    } else if (listener.first == vsyncAtMillis && !isUiThreadDraw) {
-                        Log.d("UiRendering",
-                                "Ignoring draw that's not from the UI thread at vsync: "
-                                        + vsyncAtMillis);
-                    }
-                }
-            }
-
-            if (toInvoke != null && toInvoke.size() > 0) {
-                for (Runnable run : toInvoke) {
-                    run.run();
-                }
-            }
-
-        }
-
-        public void notifyWhenDrawn(long contentVsyncMillis, Runnable runWhenDrawn) {
-            synchronized (mListeners) {
-                mListeners.add(new Pair<>(contentVsyncMillis, runWhenDrawn));
-            }
-        }
-    }
-}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.kt b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.kt
new file mode 100644
index 0000000..6239759
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.kt
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.uirendering.cts.testinfrastructure
+
+import android.app.Activity
+import android.content.pm.ActivityInfo
+import android.graphics.Point
+import android.os.Bundle
+import android.os.Handler
+import android.os.Message
+import android.uirendering.cts.R
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.Nullable
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertTrue
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+
+/**
+ * A generic activity that uses a view specified by the user.
+ */
+class DrawActivity : Activity() {
+    companion object {
+        internal const val EXTRA_WIDE_COLOR_GAMUT = "DrawActivity.WIDE_COLOR_GAMUT"
+
+        private const val TIME_OUT_MS: Long = 10000
+
+        private const val LAYOUT_MSG = 1
+        private const val CANVAS_MSG = 2
+    }
+
+    private val mLock = java.lang.Object()
+    private var mPositionInfo: ActivityTestBase.TestPositionInfo? = null
+
+    private lateinit var mHandler: Handler
+    private lateinit var mTestContainer: ViewGroup
+
+    private var mView: View? = null
+
+    private var mViewInitializer: ViewInitializer? = null
+
+    public override fun onCreate(bundle: Bundle?) {
+        super.onCreate(bundle)
+        window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
+                View.SYSTEM_UI_FLAG_FULLSCREEN
+        if (intent.getBooleanExtra(EXTRA_WIDE_COLOR_GAMUT, false)) {
+            window.colorMode = ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT
+        }
+        mHandler = RenderSpecHandler()
+
+        setContentView(R.layout.test_container)
+        mTestContainer = findViewById(R.id.test_content_wrapper)
+    }
+
+    fun enqueueRenderSpecAndWait(
+        layoutId: Int,
+        canvasClient: CanvasClient?, @Nullable viewInitializer: ViewInitializer?,
+        useHardware: Boolean, usePicture: Boolean
+    ): ActivityTestBase.TestPositionInfo {
+        (mHandler as RenderSpecHandler).setViewInitializer(viewInitializer)
+        val arg2 = if (useHardware) View.LAYER_TYPE_NONE else View.LAYER_TYPE_SOFTWARE
+        synchronized(mLock) {
+            if (canvasClient != null) {
+                mHandler.obtainMessage(CANVAS_MSG, if (usePicture) 1 else 0,
+                    arg2, canvasClient
+                ).sendToTarget()
+            } else {
+                mHandler.obtainMessage(LAYOUT_MSG, layoutId, arg2)
+                    .sendToTarget()
+            }
+
+            try {
+                mLock.wait(TIME_OUT_MS)
+            } catch (e: InterruptedException) {
+                throw AssertionError(e)
+            }
+
+        }
+        assertNotNull("Timeout waiting for draw", mPositionInfo)
+        return mPositionInfo!!
+    }
+
+    fun waitForRedraw() {
+        synchronized(mLock) {
+            mHandler.post {
+                mTestContainer.invalidate()
+                notifyOnDrawCompleted()
+            }
+            try {
+                mLock.wait(TIME_OUT_MS)
+            } catch (e: InterruptedException) {
+                throw AssertionError(e)
+            }
+        }
+    }
+
+    private fun notifyOnDrawCompleted() {
+        mTestContainer.viewTreeObserver.registerFrameCommitCallback {
+            val location = IntArray(2)
+            mTestContainer.getLocationInWindow(location)
+            val surfaceOffset = Point(location[0], location[1])
+            mTestContainer.getLocationOnScreen(location)
+            val screenOffset = Point(location[0], location[1])
+            synchronized(mLock) {
+                mPositionInfo = ActivityTestBase.TestPositionInfo(
+                    surfaceOffset, screenOffset
+                )
+                mLock.notify()
+            }
+        }
+    }
+
+    fun reset() {
+        val fence = CountDownLatch(1)
+        mHandler.post {
+            mViewInitializer?.teardownView()
+            mViewInitializer = null
+            mView = null
+            mTestContainer.removeAllViews()
+            fence.countDown()
+        }
+        assertTrue(fence.await(TIME_OUT_MS, TimeUnit.MILLISECONDS))
+    }
+
+    private inner class RenderSpecHandler : Handler() {
+
+        fun setViewInitializer(viewInitializer: ViewInitializer?) {
+            mViewInitializer = viewInitializer
+        }
+
+        override fun handleMessage(message: Message) {
+            mTestContainer.removeAllViews()
+            when (message.what) {
+                LAYOUT_MSG -> {
+                    mView = LayoutInflater.from(this@DrawActivity).inflate(
+                        message.arg1, mTestContainer, false
+                    )
+                }
+
+                CANVAS_MSG -> {
+                    val canvasClientView = CanvasClientView(this@DrawActivity)
+                    canvasClientView.setCanvasClient(message.obj as CanvasClient)
+                    if (message.arg1 != 0) {
+                        canvasClientView.setUsePicture(true)
+                    }
+                    mView = canvasClientView
+                }
+            }
+
+            if (mView == null) {
+                throw IllegalStateException("failed to inflate test content")
+            }
+
+            mTestContainer.addView(
+                mView, ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT
+            )
+
+            if (mViewInitializer != null) {
+                mViewInitializer!!.initializeView(mView)
+            }
+
+            // set layer on wrapper parent of view, so view initializer
+            // can control layer type of View under test.
+            mTestContainer.setLayerType(message.arg2, null)
+
+            notifyOnDrawCompleted()
+        }
+    }
+
+    override fun onPause() {
+        super.onPause()
+        mViewInitializer?.run {
+            teardownView()
+        }
+    }
+
+    override fun finish() {
+        // Ignore
+    }
+
+    /** Call this when all the tests that use this activity have completed.
+     * This will then clean up any internal state and finish the activity.  */
+    fun allTestsFinished() {
+        super.finish()
+    }
+}
diff --git a/tests/tests/view/AndroidTest.xml b/tests/tests/view/AndroidTest.xml
index ac1a731..27e8c1e 100644
--- a/tests/tests/view/AndroidTest.xml
+++ b/tests/tests/view/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS View test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="uitoolkit" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsViewTestCases.apk" />
diff --git a/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java b/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java
index 598552b..3045d1f2 100644
--- a/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java
+++ b/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java
@@ -16,6 +16,7 @@
 
 package android.view.cts;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
 import android.app.Instrumentation;
@@ -34,16 +35,20 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.concurrent.TimeUnit;
-
 
 /**
  * Certain KeyEvents should never be delivered to apps. These keys are:
  *      KEYCODE_ASSIST
  *      KEYCODE_VOICE_ASSIST
  *      KEYCODE_HOME
- * This test launches an Activity and inject KeyEvents with the corresponding key codes.
+ * This test launches an Activity and injects KeyEvents with the corresponding key codes.
  * The test will fail if any of these keys are received by the activity.
+ *
+ * Certain combinations of keys should be treated as shortcuts. Those are:
+ *      KEYCODE_META_* + KEYCODE_ENTER --> KEYCODE_BACK
+ *      KEYCODE_META_* + KEYCODE_DEL --> KEYCODE_HOME
+ * For those combinations, we make sure that they are either delivered to the app
+ * as the desired key (KEYCODE_BACK), or not delivered to the app (KEYCODE_HOME).
  */
 @MediumTest
 @RunWith(AndroidJUnit4.class)
@@ -77,6 +82,48 @@
         testKey(KeyEvent.KEYCODE_HOME);
     }
 
+    @Test
+    public void testKeyCodeHomeShortcutLeftMeta() {
+        testKeyCodeHomeShortcut(KeyEvent.META_META_LEFT_ON | KeyEvent.META_META_ON);
+    }
+
+    @Test
+    public void testKeyCodeHomeShortcutRightMeta() {
+        testKeyCodeHomeShortcut(KeyEvent.META_META_RIGHT_ON | KeyEvent.META_META_ON);
+    }
+
+    @Test
+    public void testKeyCodeBackShortcutLeftMeta() {
+        testKeyCodeBackShortcut(KeyEvent.META_META_LEFT_ON | KeyEvent.META_META_ON);
+    }
+
+    @Test
+    public void testKeyCodeBackShortcutRightMeta() {
+        testKeyCodeBackShortcut(KeyEvent.META_META_RIGHT_ON | KeyEvent.META_META_ON);
+    }
+
+    private void testKeyCodeHomeShortcut(int metaState) {
+        long downTime = SystemClock.uptimeMillis();
+        injectEvent(new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_ENTER, 0, metaState));
+        injectEvent(new KeyEvent(downTime, downTime + 1, KeyEvent.ACTION_UP,
+                KeyEvent.KEYCODE_ENTER, 0, metaState));
+
+        assertKeyNotReceived();
+    }
+
+    private void testKeyCodeBackShortcut(int metaState) {
+        long downTime = SystemClock.uptimeMillis();
+        injectEvent(new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_DEL, 0, metaState));
+        injectEvent(new KeyEvent(downTime, downTime + 1, KeyEvent.ACTION_UP,
+                KeyEvent.KEYCODE_DEL, 0, metaState));
+
+        assertKeyReceived(KeyEvent.KEYCODE_BACK, KeyEvent.ACTION_DOWN);
+        assertKeyReceived(KeyEvent.KEYCODE_BACK, KeyEvent.ACTION_UP);
+        assertKeyNotReceived();
+    }
+
     private void testKey(int keyCode) {
         sendKey(keyCode);
         assertKeyNotReceived();
@@ -97,14 +144,19 @@
     }
 
     private void assertKeyNotReceived() {
-        try {
-            KeyEvent keyEvent = mActivity.mKeyEvents.poll(1, TimeUnit.SECONDS);
-            if (keyEvent == null) {
-                return;
-            }
-            fail("Should not have received " + KeyEvent.keyCodeToString(keyEvent.getKeyCode()));
-        } catch (InterruptedException ex) {
-            fail("BlockingQueue.poll(..) was unexpectedly interrupted");
+        KeyEvent keyEvent = mActivity.mKeyEvents.poll();
+        if (keyEvent == null) {
+            return;
         }
+        fail("Should not have received " + KeyEvent.keyCodeToString(keyEvent.getKeyCode()));
+    }
+
+    private void assertKeyReceived(int keyCode, int action) {
+        KeyEvent keyEvent = mActivity.mKeyEvents.poll();
+        if (keyEvent == null) {
+            fail("Did not receive " + KeyEvent.keyCodeToString(keyCode) + ", queue is empty");
+        }
+        assertEquals(keyCode, keyEvent.getKeyCode());
+        assertEquals(action, keyEvent.getAction());
     }
 }
diff --git a/tests/tests/view/src/android/view/cts/KeyEventInterceptTestActivity.java b/tests/tests/view/src/android/view/cts/KeyEventInterceptTestActivity.java
index 18e0713..2422a61 100644
--- a/tests/tests/view/src/android/view/cts/KeyEventInterceptTestActivity.java
+++ b/tests/tests/view/src/android/view/cts/KeyEventInterceptTestActivity.java
@@ -19,21 +19,14 @@
 import android.app.Activity;
 import android.view.KeyEvent;
 
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingDeque;
+import java.util.LinkedList;
+import java.util.Queue;
 
 public class KeyEventInterceptTestActivity extends Activity {
-    final BlockingQueue<KeyEvent> mKeyEvents = new LinkedBlockingDeque<>();
+    final Queue<KeyEvent> mKeyEvents = new LinkedList<>();
 
     @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        mKeyEvents.add(event);
-        return true;
-    }
-
-    @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        // Check this in case some spurious event with ACTION_UP is received
+    public boolean dispatchKeyEvent(KeyEvent event) {
         mKeyEvents.add(event);
         return true;
     }
diff --git a/tests/tests/view/src/android/view/cts/KeyEventTest.java b/tests/tests/view/src/android/view/cts/KeyEventTest.java
index fde3f81..1d33865 100644
--- a/tests/tests/view/src/android/view/cts/KeyEventTest.java
+++ b/tests/tests/view/src/android/view/cts/KeyEventTest.java
@@ -760,4 +760,37 @@
                 1, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_TOUCHSCREEN);
         assertFalse(mKeyEvent.isLongPress());
     }
+
+    @Test
+    public void testKeyCodeFromString() {
+        assertEquals(KeyEvent.KEYCODE_A, KeyEvent.keyCodeFromString("KEYCODE_A"));
+        assertEquals(KeyEvent.KEYCODE_A, KeyEvent.keyCodeFromString("A"));
+        assertEquals(KeyEvent.KEYCODE_A,
+                KeyEvent.keyCodeFromString(Integer.toString(KeyEvent.KEYCODE_A)));
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.keyCodeFromString("keycode_a"));
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.keyCodeFromString("a"));
+        assertEquals(0, KeyEvent.keyCodeFromString("0"));
+        assertEquals(1, KeyEvent.keyCodeFromString("1"));
+        assertEquals(KeyEvent.KEYCODE_HOME, KeyEvent.keyCodeFromString("3"));
+        assertEquals(KeyEvent.KEYCODE_POWER,
+                KeyEvent.keyCodeFromString(Integer.toString(KeyEvent.KEYCODE_POWER)));
+        assertEquals(KeyEvent.KEYCODE_MENU,
+                KeyEvent.keyCodeFromString(Integer.toString(KeyEvent.KEYCODE_MENU)));
+        assertEquals(KeyEvent.KEYCODE_BACK, KeyEvent.keyCodeFromString("BACK"));
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.keyCodeFromString("back"));
+
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN,
+                KeyEvent.keyCodeFromString("KEYCODE_NOT_A_REAL_KEYCODE"));
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.keyCodeFromString("NOT_A_REAL_KEYCODE"));
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.keyCodeFromString("-1"));
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.keyCodeFromString("1001"));
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.keyCodeFromString("KEYCODE_123"));
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.keyCodeFromString("KEYCODE"));
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.keyCodeFromString("KEYCODE_"));
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.keyCodeFromString(""));
+        assertEquals(KeyEvent.LAST_KEYCODE,
+                KeyEvent.keyCodeFromString(Integer.toString(KeyEvent.LAST_KEYCODE)));
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN,
+                KeyEvent.keyCodeFromString(Integer.toString(KeyEvent.LAST_KEYCODE + 1)));
+    }
 }
diff --git a/tests/tests/view/src/android/view/cts/PixelCopyTest.java b/tests/tests/view/src/android/view/cts/PixelCopyTest.java
index e5f6560..4c3fe0e 100644
--- a/tests/tests/view/src/android/view/cts/PixelCopyTest.java
+++ b/tests/tests/view/src/android/view/cts/PixelCopyTest.java
@@ -272,7 +272,7 @@
     private Window waitForWindowProducerActivity() {
         PixelCopyViewProducerActivity activity =
                 mWindowSourceActivityRule.launchActivity(null);
-        activity.waitForFirstDrawCompleted(3, TimeUnit.SECONDS);
+        activity.waitForFirstDrawCompleted(10, TimeUnit.SECONDS);
         return activity.getWindow();
     }
 
@@ -382,7 +382,7 @@
     private Window waitForWideGamutWindowProducerActivity() {
         PixelCopyWideGamutViewProducerActivity activity =
                 mWideGamutWindowSourceActivityRule.launchActivity(null);
-        activity.waitForFirstDrawCompleted(3, TimeUnit.SECONDS);
+        activity.waitForFirstDrawCompleted(10, TimeUnit.SECONDS);
         return activity.getWindow();
     }
 
@@ -465,7 +465,7 @@
     private Window waitForDialogProducerActivity() {
         PixelCopyViewProducerActivity activity =
                 mDialogSourceActivityRule.launchActivity(null);
-        activity.waitForFirstDrawCompleted(3, TimeUnit.SECONDS);
+        activity.waitForFirstDrawCompleted(10, TimeUnit.SECONDS);
         return activity.getWindow();
     }
 
diff --git a/tests/tests/view/src/android/view/cts/SurfaceViewSyncTest.java b/tests/tests/view/src/android/view/cts/SurfaceViewSyncTest.java
index 2a14a8d..e3744fd 100644
--- a/tests/tests/view/src/android/view/cts/SurfaceViewSyncTest.java
+++ b/tests/tests/view/src/android/view/cts/SurfaceViewSyncTest.java
@@ -28,6 +28,7 @@
 import android.media.MediaPlayer;
 import android.os.Environment;
 import android.support.test.filters.LargeTest;
+import android.support.test.filters.RequiresDevice;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.test.uiautomator.UiObjectNotFoundException;
@@ -60,6 +61,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 @SuppressLint("RtlHardcoded")
+@RequiresDevice
 public class SurfaceViewSyncTest {
     private static final String TAG = "SurfaceViewSyncTests";
 
diff --git a/tests/tests/view/src/android/view/cts/ViewGroupTest.java b/tests/tests/view/src/android/view/cts/ViewGroupTest.java
index fef5eba..270b174 100644
--- a/tests/tests/view/src/android/view/cts/ViewGroupTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewGroupTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 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.
@@ -28,6 +28,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.app.Activity;
 import android.content.Context;
@@ -43,7 +44,6 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.os.Parcelable;
 import android.os.SystemClock;
-import androidx.annotation.NonNull;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.LargeTest;
@@ -60,6 +60,7 @@
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.MotionEvent;
+import android.view.PointerIcon;
 import android.view.View;
 import android.view.View.BaseSavedState;
 import android.view.View.MeasureSpec;
@@ -72,11 +73,15 @@
 import android.view.animation.LayoutAnimationController;
 import android.view.animation.RotateAnimation;
 import android.view.animation.Transformation;
+import android.view.cts.util.EventUtils;
 import android.view.cts.util.XmlUtils;
 import android.widget.Button;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
+
 import com.android.compatibility.common.util.CTSResult;
+import com.android.internal.widget.ScrollBarUtils;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -1006,6 +1011,180 @@
     }
 
     @Test
+    public void onInterceptHoverEvent_verticalCanScroll_intercepts() {
+        onInterceptHoverEvent_scrollabilityAffectsResult(true, true, true);
+    }
+
+    @Test
+    public void onInterceptHoverEvent_verticalCantScroll_doesntIntercept() {
+        onInterceptHoverEvent_scrollabilityAffectsResult(true, false, false);
+    }
+
+    @Test
+    public void onInterceptHoverEvent_horizontalCanScroll_intercepts() {
+        onInterceptHoverEvent_scrollabilityAffectsResult(false, true, true);
+    }
+
+    @Test
+    public void onInterceptHoverEvent_horizontalCantScroll_doesntIntercept() {
+        onInterceptHoverEvent_scrollabilityAffectsResult(false, false, false);
+    }
+
+    private void onInterceptHoverEvent_scrollabilityAffectsResult(boolean vertical,
+            boolean canScroll, boolean intercepts) {
+
+        // Arrange
+
+        int range = canScroll ? 101 : 100;
+
+        final ScrollTestView viewGroup = spy(new ScrollTestView(mContext));
+        viewGroup.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
+        viewGroup.setHorizontalScrollBarEnabled(true);
+        viewGroup.setVerticalScrollBarEnabled(true);
+        viewGroup.setScrollBarSize(10);
+        viewGroup.layout(0, 0, 100, 100);
+
+        when(viewGroup.computeVerticalScrollExtent()).thenReturn(100);
+        when(viewGroup.computeVerticalScrollRange()).thenReturn(range);
+        when(viewGroup.computeHorizontalScrollExtent()).thenReturn(100);
+        when(viewGroup.computeHorizontalScrollRange()).thenReturn(range);
+
+        int touchX = vertical ? 95 : 50;
+        int touchY = vertical ? 50 : 95;
+        MotionEvent event =
+                EventUtils.generateMouseEvent(touchX, touchY, MotionEvent.ACTION_HOVER_ENTER, 0);
+
+        // Act
+
+        boolean actualResult = viewGroup.onInterceptHoverEvent(event);
+        event.recycle();
+
+        // Assert
+
+        assertEquals(actualResult, intercepts);
+    }
+
+    @Test
+    public void onInterceptTouchEvent_verticalCanScroll_intercepts() {
+        onInterceptTouchEvent_scrollabilityAffectsResult(true, true, true);
+    }
+
+    @Test
+    public void onInterceptTouchEvent_verticalCantScroll_doesntIntercept() {
+        onInterceptTouchEvent_scrollabilityAffectsResult(true, false, false);
+    }
+
+    @Test
+    public void onInterceptTouchEvent_horizontalCanScroll_intercepts() {
+        onInterceptTouchEvent_scrollabilityAffectsResult(false, true, true);
+    }
+
+    @Test
+    public void onInterceptTouchEvent_horizontalCantScroll_doesntIntercept() {
+        onInterceptTouchEvent_scrollabilityAffectsResult(false, false, false);
+    }
+
+    private void onInterceptTouchEvent_scrollabilityAffectsResult(boolean vertical,
+            boolean canScroll, boolean intercepts) {
+        int range = canScroll ? 101 : 100;
+        int thumbLength = ScrollBarUtils.getThumbLength(1, 10, 100, range);
+
+        PointerIcon expectedPointerIcon = PointerIcon.getSystemIcon(mContext,
+                PointerIcon.TYPE_HAND);
+
+        final ScrollTestView viewGroup = spy(new ScrollTestView(mContext));
+        viewGroup.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
+        viewGroup.setHorizontalScrollBarEnabled(true);
+        viewGroup.setVerticalScrollBarEnabled(true);
+        viewGroup.setScrollBarSize(10);
+        viewGroup.setPointerIcon(expectedPointerIcon);
+        viewGroup.layout(0, 0, 100, 100);
+
+        when(viewGroup.computeVerticalScrollExtent()).thenReturn(100);
+        when(viewGroup.computeVerticalScrollRange()).thenReturn(range);
+        when(viewGroup.computeHorizontalScrollExtent()).thenReturn(100);
+        when(viewGroup.computeHorizontalScrollRange()).thenReturn(range);
+
+        int touchX = vertical ? 95 : thumbLength / 2;
+        int touchY = vertical ? thumbLength / 2 : 95;
+        MotionEvent event = EventUtils.generateMouseEvent(touchX, touchY, MotionEvent.ACTION_DOWN,
+                MotionEvent.BUTTON_PRIMARY);
+
+        // Act
+
+        boolean actualResult = viewGroup.onInterceptTouchEvent(event);
+        event.recycle();
+
+        // Assert
+
+        assertEquals(intercepts, actualResult);
+    }
+
+    @Test
+    public void onResolvePointerIcon_verticalCanScroll_pointerIsArrow() {
+        onResolvePointerIcon_scrollabilityAffectsPointerIcon(true, true, true);
+    }
+
+    @Test
+    public void onResolvePointerIcon_verticalCantScroll_pointerIsProperty() {
+        onResolvePointerIcon_scrollabilityAffectsPointerIcon(true, false, false);
+    }
+
+    @Test
+    public void onResolvePointerIcon_horizontalCanScroll_pointerIsArrow() {
+        onResolvePointerIcon_scrollabilityAffectsPointerIcon(false, true, true);
+    }
+
+    @Test
+    public void onResolvePointerIcon_horizontalCantScroll_pointerIsProperty() {
+        onResolvePointerIcon_scrollabilityAffectsPointerIcon(false, false, false);
+    }
+
+    private void onResolvePointerIcon_scrollabilityAffectsPointerIcon(boolean vertical,
+            boolean canScroll, boolean pointerIsSystemArrow) {
+
+        // Arrange
+
+        int range = canScroll ? 101 : 100;
+        int thumbLength = ScrollBarUtils.getThumbLength(1, 10, 100, range);
+
+        PointerIcon expectedPointerIcon = PointerIcon.getSystemIcon(mContext,
+                PointerIcon.TYPE_HAND);
+
+        final ScrollTestView viewGroup = spy(new ScrollTestView(mContext));
+        viewGroup.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
+        viewGroup.setHorizontalScrollBarEnabled(true);
+        viewGroup.setVerticalScrollBarEnabled(true);
+        viewGroup.setScrollBarSize(10);
+        viewGroup.setPointerIcon(expectedPointerIcon);
+        viewGroup.layout(0, 0, 100, 100);
+
+        when(viewGroup.computeVerticalScrollExtent()).thenReturn(100);
+        when(viewGroup.computeVerticalScrollRange()).thenReturn(range);
+        when(viewGroup.computeHorizontalScrollExtent()).thenReturn(100);
+        when(viewGroup.computeHorizontalScrollRange()).thenReturn(range);
+
+        int touchX = vertical ? 95 : thumbLength / 2;
+        int touchY = vertical ? thumbLength / 2 : 95;
+        MotionEvent event =
+                EventUtils.generateMouseEvent(touchX, touchY, MotionEvent.ACTION_HOVER_ENTER, 0);
+
+        // Act
+
+        PointerIcon actualResult = viewGroup.onResolvePointerIcon(event, 0);
+        event.recycle();
+
+        // Assert
+
+        if (pointerIsSystemArrow) {
+            assertEquals(PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_ARROW), actualResult);
+        } else {
+            assertEquals(expectedPointerIcon, actualResult);
+        }
+    }
+
+
+    @Test
     public void testOnDescendantInvalidated() throws Throwable {
         Activity activity = null;
         try {
@@ -3185,6 +3364,47 @@
         }
     }
 
+    public static class ScrollTestView extends ViewGroup {
+        public ScrollTestView(Context context) {
+            super(context);
+        }
+
+        @Override
+        protected void onLayout(boolean changed, int l, int t, int r, int b) {
+
+        }
+
+        @Override
+        public boolean awakenScrollBars() {
+            return super.awakenScrollBars();
+        }
+
+        @Override
+        public int computeHorizontalScrollRange() {
+            return super.computeHorizontalScrollRange();
+        }
+
+        @Override
+        public int computeHorizontalScrollExtent() {
+            return super.computeHorizontalScrollExtent();
+        }
+
+        @Override
+        public int computeVerticalScrollRange() {
+            return super.computeVerticalScrollRange();
+        }
+
+        @Override
+        public int computeVerticalScrollExtent() {
+            return super.computeVerticalScrollExtent();
+        }
+
+        @Override
+        protected int getHorizontalScrollbarHeight() {
+            return super.getHorizontalScrollbarHeight();
+        }
+    }
+
     @Override
     public void setResult(int resultCode) {
         synchronized (mSync) {
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index 346a513..e483362 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 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.
@@ -96,6 +96,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
+import android.view.cts.util.EventUtils;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
@@ -107,6 +108,7 @@
 import com.android.compatibility.common.util.CtsMouseUtil;
 import com.android.compatibility.common.util.CtsTouchUtils;
 import com.android.compatibility.common.util.PollingCheck;
+import com.android.internal.widget.ScrollBarUtils;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -412,6 +414,62 @@
     }
 
     @Test
+    public void onHoverEvent_verticalCanScroll_awakenScrollBarsCalled() {
+        onHoverEvent_awakensScrollBars(true, true, true);
+    }
+
+    @Test
+    public void onHoverEvent_verticalCantScroll_awakenScrollBarsNotCalled() {
+        onHoverEvent_awakensScrollBars(true, false, false);
+    }
+
+    @Test
+    public void onHoverEvent_horizontalCanScroll_awakenScrollBarsCalled() {
+        onHoverEvent_awakensScrollBars(false, true, true);
+    }
+
+    @Test
+    public void onHoverEvent_horizontalCantScroll_awakenScrollBarsNotCalled() {
+        onHoverEvent_awakensScrollBars(false, false, false);
+    }
+
+    private void onHoverEvent_awakensScrollBars(boolean vertical, boolean canScroll,
+            boolean awakenScrollBarsCalled) {
+
+        // Arrange
+
+        final ScrollTestView view = spy(new ScrollTestView(mContext));
+        view.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
+        view.setHorizontalScrollBarEnabled(true);
+        view.setVerticalScrollBarEnabled(true);
+        view.setScrollBarSize(10);
+        view.layout(0, 0, 100, 100);
+
+        when(view.computeVerticalScrollExtent()).thenReturn(100);
+        when(view.computeVerticalScrollRange()).thenReturn(canScroll ? 101 : 100);
+        when(view.computeHorizontalScrollExtent()).thenReturn(100);
+        when(view.computeHorizontalScrollRange()).thenReturn(canScroll ? 101 : 100);
+
+        int x = vertical ? 95 : 50;
+        int y = vertical ? 50 : 95;
+
+        MotionEvent event = EventUtils.generateMouseEvent(x, y, MotionEvent.ACTION_HOVER_ENTER, 0);
+
+        // Act
+
+        view.onHoverEvent(event);
+        event.recycle();
+
+        // Assert
+
+        if (awakenScrollBarsCalled) {
+            verify(view).awakenScrollBars();
+        } else {
+            verify(view, never()).awakenScrollBars();
+        }
+    }
+
+    @Test
     public void testMouseEventCallsGetPointerIcon() {
         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
 
@@ -510,6 +568,69 @@
     }
 
     @Test
+    public void onResolvePointerIcon_verticalCanScroll_pointerIsArrow() {
+        onResolvePointerIcon_scrollabilityAffectsPointerIcon(true, true, true);
+    }
+
+    @Test
+    public void onResolvePointerIcon_verticalCantScroll_pointerIsProperty() {
+        onResolvePointerIcon_scrollabilityAffectsPointerIcon(true, false, false);
+    }
+
+    @Test
+    public void onResolvePointerIcon_horizontalCanScroll_pointerIsArrow() {
+        onResolvePointerIcon_scrollabilityAffectsPointerIcon(false, true, true);
+    }
+
+    @Test
+    public void onResolvePointerIcon_horizontalCantScroll_pointerIsProperty() {
+        onResolvePointerIcon_scrollabilityAffectsPointerIcon(false, false, false);
+    }
+
+    private void onResolvePointerIcon_scrollabilityAffectsPointerIcon(boolean vertical,
+            boolean canScroll, boolean pointerIsSystemArrow) {
+
+        // Arrange
+
+        int range = canScroll ? 101 : 100;
+        int thumbLength = ScrollBarUtils.getThumbLength(1, 10, 100, range);
+
+        PointerIcon expectedPointerIcon = PointerIcon.getSystemIcon(mContext,
+                PointerIcon.TYPE_HAND);
+
+        final ScrollTestView view = spy(new ScrollTestView(mContext));
+        view.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
+        view.setHorizontalScrollBarEnabled(true);
+        view.setVerticalScrollBarEnabled(true);
+        view.setScrollBarSize(10);
+        view.setPointerIcon(expectedPointerIcon);
+        view.layout(0, 0, 100, 100);
+
+        when(view.computeVerticalScrollExtent()).thenReturn(100);
+        when(view.computeVerticalScrollRange()).thenReturn(range);
+        when(view.computeHorizontalScrollExtent()).thenReturn(100);
+        when(view.computeHorizontalScrollRange()).thenReturn(range);
+
+        int touchX = vertical ? 95 : thumbLength / 2;
+        int touchY = vertical ? thumbLength / 2 : 95;
+        MotionEvent event =
+                EventUtils.generateMouseEvent(touchX, touchY, MotionEvent.ACTION_HOVER_ENTER, 0);
+
+        // Act
+
+        PointerIcon actualResult = view.onResolvePointerIcon(event, 0);
+        event.recycle();
+
+        // Assert
+
+        if (pointerIsSystemArrow) {
+            assertEquals(PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_ARROW), actualResult);
+        } else {
+            assertEquals(expectedPointerIcon, actualResult);
+        }
+    }
+
+    @Test
     public void testCreatePointerIcons() {
         assertSystemPointerIcon(PointerIcon.TYPE_NULL);
         assertSystemPointerIcon(PointerIcon.TYPE_DEFAULT);
@@ -5012,6 +5133,42 @@
         }
     }
 
+    public static class ScrollTestView extends View {
+        public ScrollTestView(Context context) {
+            super(context);
+        }
+
+        @Override
+        public boolean awakenScrollBars() {
+            return super.awakenScrollBars();
+        }
+
+        @Override
+        public int computeHorizontalScrollRange() {
+            return super.computeHorizontalScrollRange();
+        }
+
+        @Override
+        public int computeHorizontalScrollExtent() {
+            return super.computeHorizontalScrollExtent();
+        }
+
+        @Override
+        public int computeVerticalScrollRange() {
+            return super.computeVerticalScrollRange();
+        }
+
+        @Override
+        public int computeVerticalScrollExtent() {
+            return super.computeVerticalScrollExtent();
+        }
+
+        @Override
+        protected int getHorizontalScrollbarHeight() {
+            return super.getHorizontalScrollbarHeight();
+        }
+    }
+
     private static final Class<?> ASYNC_INFLATE_VIEWS[] = {
         android.app.FragmentBreadCrumbs.class,
 // DISABLED because it doesn't have a AppWidgetHostView(Context, AttributeSet)
diff --git a/tests/tests/view/src/android/view/cts/util/EventUtils.java b/tests/tests/view/src/android/view/cts/util/EventUtils.java
new file mode 100644
index 0000000..a3ff488
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/util/EventUtils.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.cts.util;
+
+import android.os.SystemClock;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+
+public class EventUtils {
+
+    public static MotionEvent generateMouseEvent(int x, int y, int eventType, int buttonState) {
+
+        MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[1];
+        pointerCoords[0] = new MotionEvent.PointerCoords();
+        pointerCoords[0].x = x;
+        pointerCoords[0].y = y;
+
+        MotionEvent.PointerProperties[] pp = new MotionEvent.PointerProperties[1];
+        pp[0] = new MotionEvent.PointerProperties();
+        pp[0].id = 0;
+
+        return MotionEvent.obtain(0, SystemClock.uptimeMillis(), eventType, 1, pp,
+                pointerCoords, 0, buttonState, 0, 0, 0, 0, InputDevice.SOURCE_MOUSE, 0);
+    }
+
+}
diff --git a/tests/tests/webkit/AndroidManifest.xml b/tests/tests/webkit/AndroidManifest.xml
index 21e116d..6e3ba4e 100644
--- a/tests/tests/webkit/AndroidManifest.xml
+++ b/tests/tests/webkit/AndroidManifest.xml
@@ -20,6 +20,8 @@
 
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
     <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
+    <!-- Note: we must provide INTERNET permission for
+     ServiceWorkerWebSettingsTest#testBlockNetworkLoads -->
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <application android:maxRecents="1" android:usesCleartextTraffic="true">
diff --git a/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java b/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java
index 0fa239d..811c372 100644
--- a/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java
@@ -16,6 +16,9 @@
 
 package android.webkit.cts;
 
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
 import android.net.Uri;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.UiThreadTest;
@@ -99,16 +102,31 @@
         }.run();
     }
 
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.PostMessageTest#testSimpleMessageToMainFrame. Modifications to this test
+     * should be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
     // Post a string message to main frame and make sure it is received.
     public void testSimpleMessageToMainFrame() throws Throwable {
         verifyPostMessageToOrigin(Uri.parse(BASE_URI));
     }
 
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.PostMessageTest#testWildcardOriginMatchesAnything. Modifications to this
+     * test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
     // Post a string message to main frame passing a wildcard as target origin
     public void testWildcardOriginMatchesAnything() throws Throwable {
         verifyPostMessageToOrigin(Uri.parse("*"));
     }
 
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.PostMessageTest#testEmptyStringOriginMatchesAnything. Modifications to
+     * this test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
     // Post a string message to main frame passing an empty string as target origin
     public void testEmptyStringOriginMatchesAnything() throws Throwable {
         verifyPostMessageToOrigin(Uri.parse(""));
@@ -124,6 +142,11 @@
         waitForTitle(WEBVIEW_MESSAGE);
     }
 
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.PostMessageTest#testMultipleMessagesToMainFrame. Modifications to this
+     * test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
     // Post multiple messages to main frame and make sure they are received in
     // correct order.
     public void testMultipleMessagesToMainFrame() throws Throwable {
@@ -138,6 +161,11 @@
         waitForTitle("0123456789");
     }
 
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.PostMessageTest#testMessageChannel. Modifications to this test should be
+     * reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
     // Create a message channel and make sure it can be used for data transfer to/from js.
     public void testMessageChannel() throws Throwable {
         if (!NullWebViewUtils.isWebViewAvailable()) {
@@ -166,9 +194,14 @@
             }
         });
         // Wait for all the responses to arrive.
-        boolean ignore = latch.await(TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS);
+        assertTrue(latch.await(TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS));
     }
 
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.PostMessageTest#testClose. Modifications to this test should be reflected in
+     * that test as necessary. See http://go/modifying-webview-cts.
+     */
     // Test that a message port that is closed cannot used to send a message
     public void testClose() throws Throwable {
         if (!NullWebViewUtils.isWebViewAvailable()) {
@@ -211,6 +244,11 @@
             + "   </script>"
             + "</body></html>";
 
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.PostMessageTest#testReceiveMessagePort. Modifications to this test should
+     * be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
     // Test a message port created in JS can be received and used for message transfer.
     public void testReceiveMessagePort() throws Throwable {
         final String hello = "HELLO";
@@ -234,4 +272,77 @@
         });
         waitForTitle(hello);
     }
+
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.PostMessageTest#testWebMessageHandler. Modifications to this test should
+     * be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
+    // Ensure the callback is invoked on the correct Handler.
+    public void testWebMessageHandler() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+        loadPage(CHANNEL_MESSAGE);
+        final WebMessagePort[] channel = mOnUiThread.createWebMessageChannel();
+        WebMessage message = new WebMessage(WEBVIEW_MESSAGE, new WebMessagePort[]{channel[1]});
+        mOnUiThread.postWebMessage(message, Uri.parse(BASE_URI));
+        final int messageCount = 1;
+        final CountDownLatch latch = new CountDownLatch(messageCount);
+
+        // Create a new thread for the WebMessageCallback.
+        final HandlerThread messageHandlerThread = new HandlerThread("POST_MESSAGE_THREAD");
+        messageHandlerThread.start();
+        final Handler messageHandler = new Handler(messageHandlerThread.getLooper());
+
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                channel[0].postMessage(new WebMessage(WEBVIEW_MESSAGE));
+                channel[0].setWebMessageCallback(new WebMessagePort.WebMessageCallback() {
+                    @Override
+                    public void onMessage(WebMessagePort port, WebMessage message) {
+                        assertTrue(messageHandlerThread.getLooper().isCurrentThread());
+                        latch.countDown();
+                    }
+                }, messageHandler);
+            }
+        });
+        // Wait for all the responses to arrive.
+        assertTrue(latch.await(TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS));
+    }
+
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.PostMessageTest#testWebMessageDefaultHandler. Modifications to this test
+     * should be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
+    // Ensure the callback is invoked on the MainLooper by default.
+    public void testWebMessageDefaultHandler() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+        loadPage(CHANNEL_MESSAGE);
+        final WebMessagePort[] channel = mOnUiThread.createWebMessageChannel();
+        WebMessage message = new WebMessage(WEBVIEW_MESSAGE, new WebMessagePort[]{channel[1]});
+        mOnUiThread.postWebMessage(message, Uri.parse(BASE_URI));
+        final int messageCount = 1;
+        final CountDownLatch latch = new CountDownLatch(messageCount);
+
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                channel[0].postMessage(new WebMessage(WEBVIEW_MESSAGE));
+                channel[0].setWebMessageCallback(new WebMessagePort.WebMessageCallback() {
+                    @Override
+                    public void onMessage(WebMessagePort port, WebMessage message) {
+                        assertTrue(Looper.getMainLooper().isCurrentThread());
+                        latch.countDown();
+                    }
+                });
+            }
+        });
+        // Wait for all the responses to arrive.
+        assertTrue(latch.await(TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS));
+    }
 }
diff --git a/tests/tests/webkit/src/android/webkit/cts/ServiceWorkerClientTest.java b/tests/tests/webkit/src/android/webkit/cts/ServiceWorkerClientTest.java
index ac736cf..38f4510 100644
--- a/tests/tests/webkit/src/android/webkit/cts/ServiceWorkerClientTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/ServiceWorkerClientTest.java
@@ -145,6 +145,12 @@
         super.tearDown();
     }
 
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.ServiceWorkerClientCompatTest#testServiceWorkerClientInterceptCallback.
+     * Modifications to this test should be reflected in that test as necessary. See
+     * http://go/modifying-webview-cts.
+     */
     // Test correct invocation of shouldInterceptRequest for Service Workers.
     public void testServiceWorkerClientInterceptCallback() throws Exception {
         if (!NullWebViewUtils.isWebViewAvailable()) {
diff --git a/tests/tests/webkit/src/android/webkit/cts/ServiceWorkerWebSettingsTest.java b/tests/tests/webkit/src/android/webkit/cts/ServiceWorkerWebSettingsTest.java
new file mode 100644
index 0000000..07b702f
--- /dev/null
+++ b/tests/tests/webkit/src/android/webkit/cts/ServiceWorkerWebSettingsTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.webkit.cts;
+
+import android.content.pm.PackageManager;
+import android.os.Process;
+import android.test.ActivityInstrumentationTestCase2;
+import android.webkit.ServiceWorkerController;
+import android.webkit.ServiceWorkerWebSettings;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+
+import com.android.compatibility.common.util.NullWebViewUtils;
+
+
+public class ServiceWorkerWebSettingsTest extends
+        ActivityInstrumentationTestCase2<WebViewCtsActivity> {
+
+    private ServiceWorkerWebSettings mSettings;
+    private WebViewOnUiThread mOnUiThread;
+
+    public ServiceWorkerWebSettingsTest() {
+        super("android.webkit.cts", WebViewCtsActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        WebView webview = getActivity().getWebView();
+        if (webview != null) {
+            mOnUiThread = new WebViewOnUiThread(this, webview);
+            mSettings = ServiceWorkerController.getInstance().getServiceWorkerWebSettings();
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mOnUiThread != null) {
+            mOnUiThread.cleanUp();
+        }
+        super.tearDown();
+    }
+
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.ServiceWorkerWebSettingsCompatTest#testCacheMode. Modifications to this test
+     * should be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
+    public void testCacheMode() {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        int i = WebSettings.LOAD_DEFAULT;
+        assertEquals(i, mSettings.getCacheMode());
+        for (; i <= WebSettings.LOAD_CACHE_ONLY; i++) {
+            mSettings.setCacheMode(i);
+            assertEquals(i, mSettings.getCacheMode());
+        }
+    }
+
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.ServiceWorkerWebSettingsCompatTest#testAllowContentAccess. Modifications to
+     * this test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
+    public void testAllowContentAccess() {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        assertEquals(mSettings.getAllowContentAccess(), true);
+        for (boolean b : new boolean[]{false, true}) {
+            mSettings.setAllowContentAccess(b);
+            assertEquals(b, mSettings.getAllowContentAccess());
+        }
+    }
+
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.ServiceWorkerWebSettingsCompatTest#testAllowFileAccess. Modifications to
+     * this test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
+    public void testAllowFileAccess() {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        assertEquals(mSettings.getAllowFileAccess(), true);
+        for (boolean b : new boolean[]{false, true}) {
+            mSettings.setAllowFileAccess(b);
+            assertEquals(b, mSettings.getAllowFileAccess());
+        }
+    }
+
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.ServiceWorkerWebSettingsCompatTest#testBlockNetworkLoads. Modifications to
+     * this test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
+    public void testBlockNetworkLoads() {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        // Note: we cannot test this setter unless we provide the INTERNET permission, otherwise we
+        // get a SecurityException when we pass 'false'.
+        final boolean hasInternetPermission = true;
+
+        assertEquals(mSettings.getBlockNetworkLoads(), !hasInternetPermission);
+        for (boolean b : new boolean[]{false, true}) {
+            mSettings.setBlockNetworkLoads(b);
+            assertEquals(b, mSettings.getBlockNetworkLoads());
+        }
+    }
+}
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebBackForwardListTest.java b/tests/tests/webkit/src/android/webkit/cts/WebBackForwardListTest.java
index eb5b413..fedf521 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebBackForwardListTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebBackForwardListTest.java
@@ -108,7 +108,4 @@
         }.run();
     }
 
-    public void testClone() {
-    }
-
 }
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
index ae875a5..1fd0ef2 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
@@ -528,6 +528,11 @@
         assertTrue(mSettings.getPluginsEnabled());
     }
 
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.WebSettingsCompatTest#testOffscreenPreRaster. Modifications to this test
+     * should be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
     public void testOffscreenPreRaster() {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
@@ -741,6 +746,26 @@
         assertEquals("No database", mOnUiThread.getTitle());
     }
 
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.WebSettingsCompatTest#testDisabledActionModeMenuItems. Modifications to this
+     * test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
+    public void testDisabledActionModeMenuItems() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        assertEquals(WebSettings.MENU_ITEM_NONE, mSettings.getDisabledActionModeMenuItems());
+
+        int allDisabledFlags = WebSettings.MENU_ITEM_NONE | WebSettings.MENU_ITEM_SHARE |
+                WebSettings.MENU_ITEM_WEB_SEARCH | WebSettings.MENU_ITEM_PROCESS_TEXT;
+        for (int i = WebSettings.MENU_ITEM_NONE; i <= allDisabledFlags; i++) {
+            mSettings.setDisabledActionModeMenuItems(i);
+            assertEquals(i, mSettings.getDisabledActionModeMenuItems());
+        }
+    }
+
     public void testLoadsImagesAutomatically() throws Throwable {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
@@ -1042,6 +1067,11 @@
         }
     }
 
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.WebSettingsCompatTest#testEnableSafeBrowsing. Modifications to this test
+     * should be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
     public void testEnableSafeBrowsing() throws Throwable {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
index 58e59e5..ab21f76 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
@@ -43,6 +43,8 @@
 
 import java.io.ByteArrayInputStream;
 import java.nio.charset.StandardCharsets;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.List;
@@ -90,6 +92,12 @@
         super.tearDown();
     }
 
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.WebViewClientCompatTest#testShouldOverrideUrlLoadingDefault. Modifications
+     * to this test should be reflected in that test as necessary. See
+     * http://go/modifying-webview-cts.
+     */
     // Verify that the shouldoverrideurlloading is false by default
     public void testShouldOverrideUrlLoadingDefault() {
         if (!NullWebViewUtils.isWebViewAvailable()) {
@@ -99,6 +107,11 @@
         assertFalse(webViewClient.shouldOverrideUrlLoading(mOnUiThread.getWebView(), new String()));
     }
 
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.WebViewClientCompatTest#testShouldOverrideUrlLoading. Modifications to this
+     * test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
     // Verify shouldoverrideurlloading called on top level navigation
     public void testShouldOverrideUrlLoading() {
         if (!NullWebViewUtils.isWebViewAvailable()) {
@@ -259,6 +272,11 @@
             testServer.shutdown();
         }
     }
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.WebViewClientCompatTest#testOnReceivedError. Modifications to this test
+     * should be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
     public void testOnReceivedError() throws Exception {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
@@ -273,6 +291,11 @@
                 webViewClient.hasOnReceivedErrorCode());
     }
 
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.WebViewClientCompatTest#testOnReceivedErrorForSubresource. Modifications to
+     * this test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
     public void testOnReceivedErrorForSubresource() throws Exception {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
@@ -604,20 +627,29 @@
         assertFalse(webViewClient.didRenderProcessCrash());
     }
 
-    public void testOnSafeBrowsingHit() throws Throwable {
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.WebViewClientCompatTest#testOnSafeBrowsingHitBackToSafety.
+     * Modifications to this test should be reflected in that test as necessary. See
+     * http://go/modifying-webview-cts.
+     */
+    public void testOnSafeBrowsingHitBackToSafety() throws Throwable {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
         }
-        final SafeBrowsingBackToSafetyClient backToSafetyWebViewClient =
-                new SafeBrowsingBackToSafetyClient();
-        mOnUiThread.setWebViewClient(backToSafetyWebViewClient);
-        mOnUiThread.getSettings().setSafeBrowsingEnabled(true);
-
         mWebServer = new CtsTestServer(getActivity());
         String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
         mOnUiThread.loadUrlAndWaitForCompletion(url);
         final String ORIGINAL_URL = mOnUiThread.getUrl();
 
+        final SafeBrowsingBackToSafetyClient backToSafetyWebViewClient =
+                new SafeBrowsingBackToSafetyClient();
+        mOnUiThread.setWebViewClient(backToSafetyWebViewClient);
+        mOnUiThread.getSettings().setSafeBrowsingEnabled(true);
+
+        // Note: Safe Browsing depends on user opt-in as well, so we can't assume it's actually
+        // enabled. #getSafeBrowsingEnabled will tell us the true state of whether Safe Browsing is
+        // enabled.
         if (mOnUiThread.getSettings().getSafeBrowsingEnabled()) {
             assertEquals(0, backToSafetyWebViewClient.hasOnReceivedErrorCode());
             mOnUiThread.loadUrlAndWaitForCompletion(TEST_SAFE_BROWSING_URL);
@@ -633,11 +665,31 @@
             // Check that we actually navigated backward
             assertEquals(ORIGINAL_URL, mOnUiThread.getUrl());
         }
+    }
 
-        final SafeBrowsingProceedClient proceedWebViewClient = new SafeBrowsingProceedClient();
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.WebViewClientCompatTest#testOnSafeBrowsingHitProceed.
+     * Modifications to this test should be reflected in that test as necessary. See
+     * http://go/modifying-webview-cts.
+     */
+    public void testOnSafeBrowsingHitProceed() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+        mWebServer = new CtsTestServer(getActivity());
+        String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        final String ORIGINAL_URL = mOnUiThread.getUrl();
+
+        final SafeBrowsingProceedClient proceedWebViewClient =
+                new SafeBrowsingProceedClient();
         mOnUiThread.setWebViewClient(proceedWebViewClient);
-
         mOnUiThread.getSettings().setSafeBrowsingEnabled(true);
+
+        // Note: Safe Browsing depends on user opt-in as well, so we can't assume it's actually
+        // enabled. #getSafeBrowsingEnabled will tell us the true state of whether Safe Browsing is
+        // enabled.
         if (mOnUiThread.getSettings().getSafeBrowsingEnabled()) {
             assertEquals(0, proceedWebViewClient.hasOnReceivedErrorCode());
             mOnUiThread.loadUrlAndWaitForCompletion(TEST_SAFE_BROWSING_URL);
@@ -658,6 +710,32 @@
         mOnUiThread.loadUrlAndWaitForCompletion("about:blank");
     }
 
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.WebViewClientCompatTest#testOnPageCommitVisibleCalled.
+     * Modifications to this test should be reflected in that test as necessary. See
+     * http://go/modifying-webview-cts.
+     */
+    public void testOnPageCommitVisibleCalled() throws Exception {
+        // Check that the onPageCommitVisible callback is called
+        // correctly.
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        final CountDownLatch callbackLatch = new CountDownLatch(1);
+
+        mOnUiThread.setWebViewClient(new WebViewClient() {
+                public void onPageCommitVisible(WebView view, String url) {
+                    assertEquals(url, "about:blank");
+                    callbackLatch.countDown();
+                }
+            });
+
+        mOnUiThread.loadUrl("about:blank");
+        assertTrue(callbackLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
+    }
+
     private class MockWebViewClient extends WaitForLoadedClient {
         private boolean mOnPageStartedCalled;
         private boolean mOnPageFinishedCalled;
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
index 4bedbb1..590c447 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
@@ -1361,7 +1361,7 @@
         assertNull(mWebView.getUrl());
         String imgUrl = TestHtmlConstants.SMALL_IMG_URL; // relative
         // Snippet of HTML that will prevent favicon requests to the test server.
-        final String HTML_HEADER = "<html><head><link rel=\"shortcut icon\" href=\"#\" /></head>";
+        final String HTML_HEADER = "<html><head><link rel=\"shortcut icon\" href=\"%23\" /></head>";
 
         // Trying to resolve a relative URL against a data URL without a base URL
         // will fail and we won't make a request to the test web server.
@@ -1968,8 +1968,8 @@
         final String imgUrl = mWebServer.getAssetUrl(TestHtmlConstants.LARGE_IMG_URL);
         mOnUiThread.loadDataAndWaitForCompletion(
                 "<html><head><title>Title</title><style type=\"text/css\">"
-                + "#imgElement { -webkit-transform: translate3d(0,0,1); }"
-                + "#imgElement.finish { -webkit-transform: translate3d(0,0,0);"
+                + "%23imgElement { -webkit-transform: translate3d(0,0,1); }"
+                + "%23imgElement.finish { -webkit-transform: translate3d(0,0,0);"
                 + " -webkit-transition-duration: 1ms; }</style>"
                 + "<script type=\"text/javascript\">function imgLoad() {"
                 + "imgElement = document.getElementById('imgElement');"
@@ -2635,6 +2635,11 @@
         }
     }
 
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.WebViewCompatTest#testVisualStateCallbackCalled. Modifications to this test
+     * should be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
     public void testVisualStateCallbackCalled() throws Exception {
         // Check that the visual state callback is called correctly.
         if (!NullWebViewUtils.isWebViewAvailable()) {
@@ -2656,26 +2661,12 @@
         assertTrue(callbackLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
     }
 
-    public void testOnPageCommitVisibleCalled() throws Exception {
-        // Check that the onPageCommitVisible callback is called
-        // correctly.
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
-        final CountDownLatch callbackLatch = new CountDownLatch(1);
-
-        mOnUiThread.setWebViewClient(new WebViewClient() {
-                public void onPageCommitVisible(WebView view, String url) {
-                    assertEquals(url, "about:blank");
-                    callbackLatch.countDown();
-                }
-            });
-
-        mOnUiThread.loadUrl("about:blank");
-        assertTrue(callbackLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
-    }
-
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.WebViewCompatTest#testSetSafeBrowsingWhitelistWithMalformedList.
+     * Modifications to this test should be reflected in that test as necessary. See
+     * http://go/modifying-webview-cts.
+     */
     public void testSetSafeBrowsingWhitelistWithMalformedList() throws Exception {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
@@ -2695,6 +2686,12 @@
         assertTrue(resultLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
     }
 
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.WebViewCompatTest#testSetSafeBrowsingWhitelistWithValidList. Modifications
+     * to this test should be reflected in that test as necessary. See
+     * http://go/modifying-webview-cts.
+     */
     public void testSetSafeBrowsingWhitelistWithValidList() throws Exception {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
@@ -2732,6 +2729,11 @@
         assertTrue(resultLatch2.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
     }
 
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.WebViewCompatTest#testGetWebViewClient. Modifications to this test should be
+     * reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
     @UiThreadTest
     public void testGetWebViewClient() throws Exception {
         if (!NullWebViewUtils.isWebViewAvailable()) {
@@ -2751,6 +2753,11 @@
         assertSame(client2, webView.getWebViewClient());
     }
 
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.WebViewCompatTest#testGetWebChromeClient. Modifications to this test should
+     * be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
     @UiThreadTest
     public void testGetWebChromeClient() throws Exception {
         if (!NullWebViewUtils.isWebViewAvailable()) {
@@ -2818,6 +2825,11 @@
         }
     }
 
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.WebViewCompatTest#testStartSafeBrowsingUseApplicationContext. Modifications to
+     * this test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
     public void testStartSafeBrowsingUseApplicationContext() throws Exception {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
@@ -2836,6 +2848,12 @@
         assertTrue(resultLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
     }
 
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.WebViewCompatTest#testStartSafeBrowsingWithNullCallbackDoesntCrash.
+     * Modifications to this test should be reflected in that test as necessary. See
+     * http://go/modifying-webview-cts.
+     */
     public void testStartSafeBrowsingWithNullCallbackDoesntCrash() throws Exception {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
@@ -2844,6 +2862,11 @@
         WebView.startSafeBrowsing(getActivity().getApplicationContext(), null);
     }
 
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.WebViewCompatTest#testStartSafeBrowsingInvokesCallback. Modifications to
+     * this test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
     public void testStartSafeBrowsingInvokesCallback() throws Exception {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
@@ -3060,6 +3083,11 @@
         }
     }
 
+    /**
+     * This should remain functionally equivalent to
+     * androidx.webkit.WebViewCompatTest#testGetSafeBrowsingPrivacyPolicyUrl. Modifications to this
+     * test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
+     */
     public void testGetSafeBrowsingPrivacyPolicyUrl() throws Exception {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
diff --git a/tests/tests/widget/AndroidManifest.xml b/tests/tests/widget/AndroidManifest.xml
index 80e65f7..33c0aa2 100644
--- a/tests/tests/widget/AndroidManifest.xml
+++ b/tests/tests/widget/AndroidManifest.xml
@@ -599,17 +599,6 @@
             </intent-filter>
         </activity>
 
-        <receiver android:name="android.widget.cts.appwidget.MyAppWidgetProvider" >
-            <intent-filter>
-                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
-            </intent-filter>
-            <meta-data android:name="android.appwidget.provider"
-                       android:resource="@xml/remoteviews_appwidget_info" />
-        </receiver>
-
-        <service android:name="android.widget.cts.appwidget.MyAppWidgetService"
-                 android:permission="android.permission.BIND_REMOTEVIEWS"
-                 android:exported="false" />
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/widget/AndroidTest.xml b/tests/tests/widget/AndroidTest.xml
index 27c6ea3..123b9e7 100644
--- a/tests/tests/widget/AndroidTest.xml
+++ b/tests/tests/widget/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS Widget test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="uitoolkit" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsWidgetTestCases.apk" />
diff --git a/tests/tests/widget/res/layout/textview_layout.xml b/tests/tests/widget/res/layout/textview_layout.xml
index 86882de..6f1c188 100644
--- a/tests/tests/widget/res/layout/textview_layout.xml
+++ b/tests/tests/widget/res/layout/textview_layout.xml
@@ -44,6 +44,12 @@
                 android:layout_height="wrap_content"/>
 
             <TextView
+                android:id="@+id/textview_textAttr_zeroTextSize"
+                android:textSize="0sp"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"/>
+
+            <TextView
                 android:id="@+id/textview_password"
                 android:password="true"
                 android:minWidth="1dp"
diff --git a/tests/tests/widget/res/values/strings.xml b/tests/tests/widget/res/values/strings.xml
index 78f0f7c..9e36cc0 100644
--- a/tests/tests/widget/res/values/strings.xml
+++ b/tests/tests/widget/res/values/strings.xml
@@ -197,6 +197,7 @@
     <string name="toolbar_subtitle">Toolbar subtitle</string>
     <string name="toolbar_navigation">Toolbar navigation</string>
     <string name="toolbar_logo">Toolbar logo</string>
+    <string name="toolbar_collapse">Toolbar collapse</string>
 
     <string name="search_query_hint">query hint</string>
 
diff --git a/tests/tests/widget/src/android/widget/cts/EditTextTest.java b/tests/tests/widget/src/android/widget/cts/EditTextTest.java
index 32e54f1..0982aeb 100644
--- a/tests/tests/widget/src/android/widget/cts/EditTextTest.java
+++ b/tests/tests/widget/src/android/widget/cts/EditTextTest.java
@@ -31,6 +31,7 @@
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 import android.text.Editable;
+import android.text.Layout;
 import android.text.TextUtils;
 import android.text.method.ArrowKeyMovementMethod;
 import android.text.method.MovementMethod;
@@ -338,6 +339,20 @@
         assertEquals(mEditText1.getSelectionEnd(), mEditText2.getSelectionEnd());
     }
 
+    @Test
+    public void testHyphenationFrequencyDefaultValue() {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        final EditText editText = new EditText(context);
+        assertEquals(Layout.HYPHENATION_FREQUENCY_NONE, editText.getHyphenationFrequency());
+    }
+
+    @Test
+    public void testBreakStrategyDefaultValue() {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        final EditText editText = new EditText(context);
+        assertEquals(Layout.BREAK_STRATEGY_SIMPLE, editText.getBreakStrategy());
+    }
+
     private class MockEditText extends EditText {
         public MockEditText(Context context) {
             super(context);
diff --git a/tests/tests/widget/src/android/widget/cts/ImageViewTest.java b/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
index 5476b22..d59fe97 100644
--- a/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
@@ -33,6 +33,7 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -42,13 +43,19 @@
 import android.graphics.Color;
 import android.graphics.ColorFilter;
 import android.graphics.Matrix;
+import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Xfermode;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.graphics.drawable.PaintDrawable;
 import android.net.Uri;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.MediumTest;
 import android.support.test.rule.ActivityTestRule;
@@ -500,6 +507,46 @@
         verify(mockImageView, times(1)).onSizeChanged(anyInt(), anyInt(), anyInt(), anyInt());
     }
 
+    @Test
+    public void testSetColorFilterPreservesDrawableProperties() {
+        ImageView imageView = new ImageView(InstrumentationRegistry.getTargetContext());
+
+        int colorAlpha = 128;
+        MockDrawable mockDrawable = new MockDrawable();
+        mockDrawable.setAlpha(colorAlpha);
+        mockDrawable.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
+
+        imageView.setImageDrawable(mockDrawable);
+
+        imageView.setColorFilter(Color.RED);
+        assertEquals(colorAlpha, mockDrawable.getAlpha());
+        assertNotNull(mockDrawable.getXfermode());
+    }
+
+    @Test
+    public void testImageViewSetColorFilterPropagatedToDrawable() {
+        ImageView imageView = new ImageView(InstrumentationRegistry.getTargetContext());
+
+        MockDrawable mockDrawable = new MockDrawable();
+        imageView.setImageDrawable(mockDrawable);
+        imageView.setColorFilter(Color.RED);
+
+        ColorFilter imageViewColorFilter = imageView.getColorFilter();
+        assertTrue(imageViewColorFilter instanceof PorterDuffColorFilter);
+
+        PorterDuffColorFilter imageViewPorterDuffFilter =
+                (PorterDuffColorFilter) imageViewColorFilter;
+        assertEquals(Color.RED, imageViewPorterDuffFilter.getColor());
+        assertEquals(Mode.SRC_ATOP, imageViewPorterDuffFilter.getMode());
+
+        ColorFilter colorFilter = mockDrawable.getColorFilter();
+        assertTrue(colorFilter instanceof PorterDuffColorFilter);
+
+        PorterDuffColorFilter porterDuffColorFilter = (PorterDuffColorFilter) colorFilter;
+        assertEquals(Color.RED, porterDuffColorFilter.getColor());
+        assertEquals(PorterDuff.Mode.SRC_ATOP, porterDuffColorFilter.getMode());
+    }
+
     @UiThreadTest
     @Test
     public void testVerifyDrawable() {
@@ -674,4 +721,49 @@
             super.onSizeChanged(w, h, oldw, oldh);
         }
     }
+
+    public static class MockDrawable extends Drawable {
+
+        private ColorFilter mFilter;
+        private int mAlpha;
+        private Xfermode mXfermode;
+
+        @Override
+        public void draw(Canvas canvas) {
+            // NO-OP
+        }
+
+        @Override
+        public void setAlpha(int alpha) {
+            mAlpha = alpha;
+        }
+
+        public int getAlpha() {
+            return mAlpha;
+        }
+
+        @Override
+        public void setColorFilter(ColorFilter colorFilter) {
+            mFilter = colorFilter;
+        }
+
+        @Override
+        public void setXfermode(Xfermode mode) {
+            mXfermode = mode;
+        }
+
+        public @Nullable Xfermode getXfermode() {
+            return mXfermode;
+        }
+
+        @Override
+        public @Nullable ColorFilter getColorFilter() {
+            return mFilter;
+        }
+
+        @Override
+        public int getOpacity() {
+            return PixelFormat.TRANSLUCENT;
+        }
+    }
 }
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index 7c55f90..20e3a64 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -73,8 +73,6 @@
 import android.os.Looper;
 import android.os.Parcelable;
 import android.os.SystemClock;
-import androidx.annotation.IntDef;
-import androidx.annotation.Nullable;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.MediumTest;
@@ -150,6 +148,9 @@
 import android.widget.TextView.BufferType;
 import android.widget.cts.util.TestUtils;
 
+import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
+
 import com.android.compatibility.common.util.CtsKeyEventUtil;
 import com.android.compatibility.common.util.CtsTouchUtils;
 import com.android.compatibility.common.util.PollingCheck;
@@ -3288,6 +3289,19 @@
 
     @UiThreadTest
     @Test
+    public void testTextAttr_zeroTextSize() {
+        mTextView = findTextView(R.id.textview_textAttr_zeroTextSize);
+        // text size should be 0 as set in xml, rather than the text view default (15.0)
+        assertEquals(0f, mTextView.getTextSize(), 0.01f);
+        // text size can be set programmatically to non-negative values
+        mTextView.setTextSize(20f);
+        assertTrue(mTextView.getTextSize() > 0.0f);
+        mTextView.setTextSize(0f);
+        assertEquals(0f, mTextView.getTextSize(), 0.01f);
+    }
+
+    @UiThreadTest
+    @Test
     public void testAppend() {
         mTextView = new TextView(mActivity);
 
@@ -6260,9 +6274,6 @@
     public void testSetGetHyphenationFrequency() {
         TextView tv = new TextView(mActivity);
 
-        assertEquals(Layout.HYPHENATION_FREQUENCY_NORMAL, tv.getHyphenationFrequency());
-
-        tv.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE);
         assertEquals(Layout.HYPHENATION_FREQUENCY_NONE, tv.getHyphenationFrequency());
 
         tv.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
@@ -6270,6 +6281,9 @@
 
         tv.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
         assertEquals(Layout.HYPHENATION_FREQUENCY_FULL, tv.getHyphenationFrequency());
+
+        tv.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE);
+        assertEquals(Layout.HYPHENATION_FREQUENCY_NONE, tv.getHyphenationFrequency());
     }
 
     @UiThreadTest
@@ -7987,6 +8001,20 @@
         });
     }
 
+    @Test
+    public void testBreakStrategyDefaultValue() {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        final TextView textView = new TextView(context);
+        assertEquals(Layout.BREAK_STRATEGY_HIGH_QUALITY, textView.getBreakStrategy());
+    }
+
+    @Test
+    public void testHyphenationFrequencyDefaultValue() {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        final TextView textView = new TextView(context);
+        assertEquals(Layout.HYPHENATION_FREQUENCY_NONE, textView.getHyphenationFrequency());
+    }
+
     private void initializeTextForSmartSelection(CharSequence text) throws Throwable {
         assertTrue(text.length() >= SMARTSELECT_END);
         mActivityRule.runOnUiThread(() -> {
diff --git a/tests/tests/widget/src/android/widget/cts/ToolbarTest.java b/tests/tests/widget/src/android/widget/cts/ToolbarTest.java
index ff4da10..8a70859 100644
--- a/tests/tests/widget/src/android/widget/cts/ToolbarTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ToolbarTest.java
@@ -287,6 +287,41 @@
     }
 
     @Test
+    public void testCollapseConfiguration() throws Throwable {
+        // Inflate menu and expand action view to display collapse button
+        WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mMainToolbar,
+                () -> mMainToolbar.inflateMenu(R.menu.toolbar_menu_search));
+        final MenuItem searchMenuItem = mMainToolbar.getMenu().findItem(R.id.action_search);
+        mActivityRule.runOnUiThread(searchMenuItem::expandActionView);
+        mInstrumentation.waitForIdleSync();
+
+        WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mMainToolbar,
+                () -> mMainToolbar.setCollapseIcon(R.drawable.icon_green));
+        Drawable toolbarCollapseIcon = mMainToolbar.getCollapseIcon();
+        TestUtils.assertAllPixelsOfColor("Collapse icon is green", toolbarCollapseIcon,
+                toolbarCollapseIcon.getIntrinsicWidth(),
+                toolbarCollapseIcon.getIntrinsicHeight(),
+                true, Color.GREEN, 1, false);
+
+        WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mMainToolbar,
+                () -> mMainToolbar.setCollapseIcon(mActivity.getDrawable(R.drawable.icon_blue)));
+        toolbarCollapseIcon = mMainToolbar.getCollapseIcon();
+        TestUtils.assertAllPixelsOfColor("Collapse icon is blue", toolbarCollapseIcon,
+                toolbarCollapseIcon.getIntrinsicWidth(),
+                toolbarCollapseIcon.getIntrinsicHeight(),
+                true, Color.BLUE, 1, false);
+
+        mActivityRule.runOnUiThread(
+                () -> mMainToolbar.setCollapseContentDescription(R.string.toolbar_collapse));
+        assertEquals(mActivity.getResources().getString(R.string.toolbar_collapse),
+                mMainToolbar.getCollapseContentDescription());
+
+        mActivityRule.runOnUiThread(
+                () -> mMainToolbar.setCollapseContentDescription("Collapse legend"));
+        assertEquals("Collapse legend", mMainToolbar.getCollapseContentDescription());
+    }
+
+    @Test
     public void testLogoConfiguration() throws Throwable {
         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mMainToolbar,
                 () -> mMainToolbar.setLogo(R.drawable.icon_yellow));
diff --git a/tests/tests/widget/src/android/widget/cts/appwidget/MyAppWidgetService.java b/tests/tests/widget/src/android/widget/cts/appwidget/MyAppWidgetService.java
deleted file mode 100644
index 7fc6b49..0000000
--- a/tests/tests/widget/src/android/widget/cts/appwidget/MyAppWidgetService.java
+++ /dev/null
@@ -1,39 +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.widget.cts.appwidget;
-
-import android.content.Intent;
-import android.widget.RemoteViewsService;
-
-public class MyAppWidgetService extends RemoteViewsService {
-    private static final Object sLock = new Object();
-
-    private static RemoteViewsFactory sFactory;
-
-    public static void setFactory(RemoteViewsFactory factory) {
-        synchronized (sLock) {
-            sFactory = factory;
-        }
-    }
-
-    @Override
-    public RemoteViewsFactory onGetViewFactory(Intent intent) {
-        synchronized (sLock) {
-            return sFactory;
-        }
-    }
-}
diff --git a/tests/ui/AndroidTest.xml b/tests/ui/AndroidTest.xml
index 3f52b4a..9e51fba 100644
--- a/tests/ui/AndroidTest.xml
+++ b/tests/ui/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS UI test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="uitoolkit" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/vr/Android.mk b/tests/vr/Android.mk
index dfd5655..0c842cf 100644
--- a/tests/vr/Android.mk
+++ b/tests/vr/Android.mk
@@ -36,6 +36,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src) ../../apps/CtsVerifier/src/com/android/cts/verifier/vr/MockVrListenerService.java
 
 LOCAL_SDK_VERSION := test_current
+LOCAL_MIN_SDK_VERSION := 14
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/tests/vr/AndroidTest.xml b/tests/vr/AndroidTest.xml
index 02b2918..0212be6 100644
--- a/tests/vr/AndroidTest.xml
+++ b/tests/vr/AndroidTest.xml
@@ -23,5 +23,6 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.vr.cts" />
         <option name="runtime-hint" value="13m" />
+        <option name="hidden-api-checks" value="false" />
     </test>
 </configuration>
diff --git a/tools/release-parser/Android.mk b/tools/release-parser/Android.mk
new file mode 100644
index 0000000..aed8583
--- /dev/null
+++ b/tools/release-parser/Android.mk
@@ -0,0 +1,41 @@
+# 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)
+
+# cts release-parser java library
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    $(call all-subdir-java-files) \
+    $(call all-proto-files-under, proto)
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := full
+LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/proto/
+
+LOCAL_JAR_MANIFEST := MANIFEST.mf
+
+LOCAL_MODULE := release-parser
+
+# This tool is not checking any dependencies or metadata, so all of the
+# dependencies of all of the tests must be on its classpath. This is
+# super fragile.
+LOCAL_STATIC_JAVA_LIBRARIES += \
+  compatibility-host-util \
+  hosttestlib \
+  dexlib2 \
+  tradefed
+
+include $(BUILD_HOST_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/tools/release-parser/MANIFEST.mf b/tools/release-parser/MANIFEST.mf
new file mode 100644
index 0000000..8acac32
--- /dev/null
+++ b/tools/release-parser/MANIFEST.mf
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: com.android.cts.releaseparser.Main
+Implementation-Version: %BUILD_NUMBER%
\ No newline at end of file
diff --git a/tools/release-parser/proto/release.proto b/tools/release-parser/proto/release.proto
new file mode 100644
index 0000000..a956260
--- /dev/null
+++ b/tools/release-parser/proto/release.proto
@@ -0,0 +1,244 @@
+// 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.
+
+// [START declaration]
+syntax = "proto3";
+package com_android_cts_releaseparser;
+// [END declaration]
+
+// [START java_declaration]
+option java_package = "com.android.cts.releaseparser";
+option java_outer_classname = "ReleaseProto";
+// [END java_declaration]
+
+// [START messages]
+message Option {
+    string name = 1;
+    string key = 2;
+    string value =3;
+}
+
+message TestModuleConfig {
+    string module_name = 1;
+    string component = 2;
+    string description = 3;
+    repeated Option options = 4;
+
+    message TargetPreparer {
+        string test_class = 1;
+        repeated Option options = 2;
+    }
+    repeated TargetPreparer target_preparers = 5;
+    repeated string test_file_names = 6;
+
+    message TestClass {
+        string test_class = 1;
+        string package = 2;
+        repeated Option options = 3;
+    }
+    repeated TestClass test_classes = 7;
+    repeated string test_jars = 8;
+}
+
+// An entry in a release
+message Entry {
+    // Name
+    string name = 1;
+
+    enum EntryType {
+        FOLDER = 0;
+        FILE = 1;
+        TEST_MODULE_CONFIG = 2;
+        JAR = 3;
+        APK = 4;
+        EXE = 5;
+        SO = 6;
+        OAT = 7;
+        ODEX = 8;
+        VDEX = 9;
+        TEST_SUITE_TRADEFED = 10;
+        BUILD_PROP = 11;
+        SYMBOLIC_LINK = 12;
+        RC = 13;
+    }
+    // Type
+    EntryType type = 2;
+
+    // Size
+    int64 size = 3;
+    // Content ID
+    string content_id = 4;
+    // Parent folder
+    string parent_folder = 5;
+    // Relative path
+    string relative_path = 6;
+
+    // code_id
+    string code_id = 7;
+
+    // TestModule.config message
+    TestModuleConfig test_module_config = 8;
+    repeated string dependencies = 9;
+    string abi_architecture = 10;
+    int32 abi_bits = 11;
+    repeated Service services = 12;
+    repeated string dynamic_loading_dependencies = 13;
+}
+
+message ReleaseContent {
+    // Name
+    string name = 1;
+    // Version
+    string version = 2;
+    // Build Number
+    string build_number = 3;
+    // Content ID
+    string content_id = 4;
+    string fullname = 5;
+    string target_arch = 6;
+    string test_suite_tradefed = 7;
+    // File Entries (relative_path, entry)
+    map<string, Entry> entries = 8;
+    repeated string known_failures = 9;
+}
+
+message Annotation {
+    int32 visibility = 1;
+    string type = 2;
+
+    message Element {
+        string name = 1;
+        string value = 2;
+    }
+    repeated Element elements = 3;
+}
+
+enum TestClassType {
+    UNKNOWN = 0;
+    JUNIT3 = 1;
+    JUNIT4 = 2;
+    PARAMETERIZED = 3;
+    JAVAHOST = 4;
+}
+
+message TestSuite {
+    string name = 1;
+    // Version
+    string version = 2;
+    // Build Number
+    string build_number = 3;
+    // Content ID
+    string content_id = 4;
+
+    enum TestType {
+        UNKNOWN = 0;
+        ANDROIDJUNIT = 1;
+        JAVAHOST = 2;
+        GTEST = 3;
+        LIBCORE = 4;
+        DALVIK = 5;
+        DEQP = 6;
+    }
+
+    message Module {
+        string name = 1;
+        string config_file = 2;
+        TestType test_type = 3;
+        string test_class = 4;
+
+        message Package {
+            string name = 1;
+            string package_file = 2;
+            string content_id = 3;
+            string op_codes = 4;
+
+            message Class {
+                string name = 1;
+                string type = 2;
+                string super_class = 3;
+                string interface = 4;
+                TestClassType test_class_type = 5;
+                repeated Annotation annotations = 6;
+
+                message Method {
+                    string defining_class = 1;
+                    string name = 2;
+                    string parameters = 3;
+                    string return_type = 4;
+                    int32 access_flags = 5;
+                    string known_failure_filter = 6;
+                    repeated Annotation annotations = 7;
+                }
+                repeated Method methods = 7;
+
+                message Field {
+                    string defining_class = 1;
+                    string name = 2;
+                    string type = 3;
+                    int32 access_flags = 4;
+                    string initial_value = 5;
+                    repeated Annotation annotations = 6;
+                }
+                repeated Field fields = 8;
+            }
+            repeated Class classes = 5;
+        }
+        repeated Package packages = 5;
+    }
+    repeated Module modules = 5;
+}
+
+message Service {
+    string name = 1;
+    string file = 2;
+    repeated string arguments = 3;
+    string clazz = 4;
+    string user = 5;
+    string group = 6;
+    string writepid = 7;
+    repeated string options = 8;
+}
+
+message Api {
+    string name = 1;
+    string version = 2;
+
+    message Package {
+        string name = 1;
+
+        message Clazz {
+            string name = 1;
+            bool deprecated = 2;
+            bool abstract = 3;
+
+            message Method {
+                string name = 1;
+                string visibility = 2;
+                repeated string parameter_types = 3;
+                string return_type = 4;
+                bool deprecated = 5;
+                bool static = 6;
+                bool final = 7;
+                bool abstract = 8;
+            }
+            repeated Method constructors = 4;
+            repeated Method methods = 5;
+            Clazz super_class = 6;
+        }
+        map<string, Clazz> clazzez = 3;
+    }
+    map<string, Package> packages = 3;
+}
+
+// [END messages]
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/ApkParser.java b/tools/release-parser/src/com/android/cts/releaseparser/ApkParser.java
new file mode 100644
index 0000000..67211f8
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/ApkParser.java
@@ -0,0 +1,246 @@
+/*
+ * 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.releaseparser;
+
+import com.android.cts.releaseparser.ReleaseProto.*;
+
+import org.jf.dexlib2.DexFileFactory;
+import org.jf.dexlib2.Opcodes;
+import org.jf.dexlib2.ReferenceType;
+import org.jf.dexlib2.dexbacked.DexBackedClassDef;
+import org.jf.dexlib2.dexbacked.DexBackedDexFile;
+import org.jf.dexlib2.dexbacked.DexBackedField;
+import org.jf.dexlib2.dexbacked.DexBackedMethod;
+import org.jf.dexlib2.dexbacked.reference.DexBackedFieldReference;
+import org.jf.dexlib2.dexbacked.reference.DexBackedMethodReference;
+import org.jf.dexlib2.dexbacked.reference.DexBackedTypeReference;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class ApkParser extends FileParser {
+    private DexBackedDexFile mDexFile;
+    private Api.Builder mApiBuilder;
+
+    public ApkParser(File file) {
+        super(file);
+    }
+
+    @Override
+    public Entry.EntryType getType() {
+        return Entry.EntryType.APK;
+    }
+
+    public Api getApi() {
+        if (mApiBuilder == null) {
+            mApiBuilder = prase();
+        }
+        return mApiBuilder.build();
+    }
+
+    private boolean isInternal(DexBackedTypeReference t) {
+        if (t.getType().length() == 1) {
+            // primitive class
+            return true;
+        } else if (t.getType().charAt(0) == '[') {
+            return true;
+        }
+        return false;
+    }
+
+    // [[Lcom/foo/bar/MyClass$Inner; becomes
+    // com.foo.bar.MyClass.Inner[][]
+    // and [[I becomes int[][]
+    private static String toCanonicalName(String name) {
+        int arrayDepth = 0;
+        for (int i = 0; i < name.length(); i++) {
+            if (name.charAt(i) == '[') {
+                arrayDepth++;
+            } else {
+                break;
+            }
+        }
+
+        if (name.length() == arrayDepth + 1) {
+            // primitive types
+            name = name.substring(arrayDepth);
+        } else if (name.charAt(arrayDepth) == 'L') {
+            // omit the leading 'L' and the trailing ';'
+            name = name.substring(arrayDepth + 1, name.length() - 1);
+
+            // replace '/' to '.'
+            name = name.replace('/', '.');
+        } else {
+            throw new RuntimeException("Invalid type name " + name);
+        }
+
+        // add []'s, if any
+        if (arrayDepth > 0) {
+            for (int i = 0; i < arrayDepth; i++) {
+                name += "[]";
+            }
+        }
+        return name;
+    }
+
+    private String getSignature(DexBackedTypeReference f) {
+        return toCanonicalName(f.getType());
+    }
+
+    private String getSignature(DexBackedClassDef c) {
+        return toCanonicalName(c.getType());
+    }
+
+    private String getSignature(DexBackedMethod m) {
+        return toCanonicalName(m.getDefiningClass())
+                + "."
+                + m.getName()
+                + ","
+                + toParametersString(m.getParameterTypes())
+                + ","
+                + toCanonicalName(m.getReturnType());
+    }
+
+    private String getSignature(DexBackedField f) {
+        return toCanonicalName(f.getDefiningClass())
+                + "."
+                + f.getName()
+                + "."
+                + toCanonicalName(f.getType());
+    }
+
+    private String getRefName(DexBackedFieldReference f) {
+        return toCanonicalName(f.getDefiningClass()) + "." + f.getName() + " : " + f.getType();
+    }
+
+    private String getRefName(DexBackedMethodReference m) {
+        return toCanonicalName(m.getDefiningClass())
+                + "."
+                + m.getName()
+                + " : ("
+                + String.join("", m.getParameterTypes())
+                + ")"
+                + m.getReturnType();
+    }
+
+    private String toParametersString(List<String> pList) {
+        return pList.stream().map(p -> toCanonicalName(p)).collect(Collectors.joining(":"));
+    }
+
+    private Api.Builder prase() {
+        Api.Builder apiBuilder = Api.newBuilder();
+        DexBackedDexFile dexFile = null;
+        Map<String, DexBackedClassDef> definedClassesInDex = new HashMap<>();
+        Map<String, DexBackedMethod> definedMethodsInDex = new HashMap<>();
+        Map<String, DexBackedField> definedFieldsInDex = new HashMap<>();
+
+        // Loads a Dex file
+        System.out.println("dexFile: " + getFile().getName());
+        try {
+            dexFile = DexFileFactory.loadDexFile(getFile().getName(), Opcodes.getDefault());
+
+            dexFile.getClasses().stream().forEach(c -> definedClassesInDex.put(c.getType(), c));
+
+            for (DexBackedClassDef clazz : definedClassesInDex.values()) {
+                for (DexBackedField dxField : clazz.getFields()) {
+                    definedFieldsInDex.put(getSignature(dxField), dxField);
+                }
+                for (DexBackedMethod dxMethod : clazz.getMethods()) {
+                    definedMethodsInDex.put(getSignature(dxMethod), dxMethod);
+                }
+            }
+
+            System.out.println("Ext");
+            System.out.println("Classes:");
+            dexFile.getReferences(ReferenceType.TYPE)
+                    .stream()
+                    .map(t -> (DexBackedTypeReference) t)
+                    .filter(t -> !isInternal(t))
+                    .filter(t -> !definedClassesInDex.containsKey(t.getType()))
+                    .forEach(ref -> System.out.println(getSignature(ref)));
+
+            System.out.println("\nFields:");
+            dexFile.getReferences(ReferenceType.FIELD)
+                    .stream()
+                    .map(f -> (DexBackedFieldReference) f)
+                    .filter(f -> !definedClassesInDex.containsKey(f.getDefiningClass()))
+                    .forEach(f -> System.out.println(getRefName(f)));
+
+            System.out.println("\nMethods:");
+            dexFile.getReferences(ReferenceType.METHOD)
+                    .stream()
+                    .map(m -> (DexBackedMethodReference) m)
+                    .filter(m -> !definedClassesInDex.containsKey(m.getDefiningClass()))
+                    .filter(
+                            m ->
+                                    !(m.getDefiningClass().startsWith("[")
+                                            && m.getName().equals("clone")))
+                    .forEach(m -> System.out.println(getRefName(m)));
+
+        } catch (IOException | DexFileFactory.DexFileNotFoundException ex) {
+            System.err.println("Unable to load dex file: " + getFile().getName());
+            // ex.printStackTrace();
+        }
+        return apiBuilder;
+    }
+
+    private static final String USAGE_MESSAGE =
+            "Usage: java -jar releaseparser.jar com.android.cts.releaseparser.ApkParser [-options] <path> [args...]\n"
+                    + "           to prase an APK for API\n"
+                    + "Options:\n"
+                    + "\t-i PATH\t APK path \n";
+
+    /** Get the argument or print out the usage and exit. */
+    private static void printUsage() {
+        System.out.printf(USAGE_MESSAGE);
+        System.exit(1);
+    }
+
+    /** Get the argument or print out the usage and exit. */
+    private static String getExpectedArg(String[] args, int index) {
+        if (index < args.length) {
+            return args[index];
+        } else {
+            printUsage();
+            return null; // Never will happen because printUsage will call exit(1)
+        }
+    }
+
+    public static void main(String[] args) throws IOException {
+        String apkFileName = null;
+
+        for (int i = 0; i < args.length; i++) {
+            if (args[i].startsWith("-")) {
+                if ("-i".equals(args[i])) {
+                    apkFileName = getExpectedArg(args, ++i);
+                }
+            }
+        }
+
+        if (apkFileName == null) {
+            printUsage();
+        }
+
+        File apkFile = new File(apkFileName);
+        ApkParser apkParser = new ApkParser(apkFile);
+        Api api = apkParser.getApi();
+    }
+}
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/BuildPropParser.java b/tools/release-parser/src/com/android/cts/releaseparser/BuildPropParser.java
new file mode 100644
index 0000000..7aba653
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/BuildPropParser.java
@@ -0,0 +1,96 @@
+/*
+ * 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.releaseparser;
+
+import com.android.cts.releaseparser.ReleaseProto.*;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+
+public class BuildPropParser extends FileParser {
+    private Entry.EntryType mType;
+    private HashMap<String, String> mProp;
+
+    public BuildPropParser(File file) {
+        super(file);
+    }
+
+    @Override
+    public Entry.EntryType getType() {
+        if (mType == null) {
+            parseFile();
+        }
+        return mType;
+    }
+
+    public String getBuildNumber() {
+        if (mType == null) {
+            parseFile();
+        }
+        return mProp.get("ro.build.version.incremental");
+    }
+
+    public String getVersion() {
+        if (mType == null) {
+            parseFile();
+        }
+        return mProp.get("ro.build.id");
+    }
+
+    public String getName() {
+        if (mType == null) {
+            parseFile();
+        }
+        return mProp.get("ro.product.device");
+    }
+
+    public String getFullName() {
+        if (mType == null) {
+            parseFile();
+        }
+        return mProp.get("ro.build.flavor");
+    }
+
+    private void parseFile() {
+        try {
+            FileReader fileReader = new FileReader(getFile());
+            BufferedReader buffReader = new BufferedReader(fileReader);
+            String line;
+            mProp = new HashMap<>();
+            while ((line = buffReader.readLine()) != null) {
+                String trimLine = line.trim();
+                if (!trimLine.startsWith("#")) {
+                    String[] phases = trimLine.split("=");
+                    if (phases.length > 1) {
+                        mProp.put(phases[0], phases[1]);
+                    } else {
+                        mProp.put(phases[0], "");
+                    }
+                }
+            }
+            fileReader.close();
+            mType = Entry.EntryType.BUILD_PROP;
+        } catch (IOException e) {
+            // file is not a Test Module Config
+            System.err.println("BuildProp err:" + getFileName() + "\n" + e.getMessage());
+            mType = super.getType();
+        }
+    }
+}
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/DepPrinter.java b/tools/release-parser/src/com/android/cts/releaseparser/DepPrinter.java
new file mode 100644
index 0000000..9b22cc9
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/DepPrinter.java
@@ -0,0 +1,366 @@
+/*
+ * 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.releaseparser;
+
+import com.android.cts.releaseparser.ReleaseProto.*;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class DepPrinter {
+    private ReleaseContent mRelContent;
+    private PrintWriter mPWriter;
+    private HashMap<String, Integer> mLibMap;
+    private HashMap<String, Integer> mDepPathMap;
+    private ReleaseContent mBRelContent;
+    private int mBits;
+    private String mTitle;
+    private int mCurLevel;
+
+    private static String getTitle(ReleaseContent relContent) {
+        return relContent.getName() + relContent.getVersion() + relContent.getBuildNumber();
+    }
+
+    public DepPrinter(ReleaseContent relContent) {
+        mRelContent = relContent;
+        mTitle = getTitle(relContent);
+        mBRelContent = null;
+    }
+
+    public void writeDeltaDigraphs(ReleaseContent bRelContent, String dirName) {
+        mBRelContent = bRelContent;
+        mTitle = mTitle + "_vs_" + getTitle(bRelContent);
+        compareEntries(dirName);
+    }
+
+    public void compareEntries(String dirName) {
+        for (Entry entry : mRelContent.getEntries().values()) {
+            if (entry.getType() == Entry.EntryType.EXE) {
+                String exeName = entry.getName();
+                String fileName = String.format("%s/%s.gv", dirName, exeName);
+                writeDigraph(entry, fileName);
+            }
+        }
+    }
+
+    public void writeDeltaDigraph(String exeName, ReleaseContent bRelContent, String fileName) {
+        mBRelContent = bRelContent;
+        mTitle = mTitle + "_vs_" + getTitle(bRelContent);
+        compareEntry(exeName, fileName);
+    }
+
+    public void compareEntry(String exeName, String fileName) {
+        for (Entry entry : mRelContent.getEntries().values()) {
+            if (entry.getName().equals(exeName)) {
+                writeDigraph(entry, fileName);
+                break;
+            }
+        }
+    }
+
+    public void writeDigraph(Entry entry, String fileName) {
+        try {
+            String exeName = entry.getName();
+            FileWriter fWriter = new FileWriter(fileName);
+            mPWriter = new PrintWriter(fWriter);
+            mLibMap = new HashMap<String, Integer>();
+            mDepPathMap = new HashMap<String, Integer>();
+            mBits = entry.getAbiBits();
+            // Header
+            mPWriter.println("digraph {");
+            mPWriter.println(
+                    String.format(
+                            "a [label=\"%s %dbits\" shape=plaintext]", getNodeName(mTitle), mBits));
+
+            String sourceNode = getNodeName(exeName);
+            mPWriter.printf(String.format("%s [label=\"%s\"", sourceNode, exeName));
+            checkDelta(entry);
+            mPWriter.println("]");
+
+            mCurLevel = 0;
+            printDep(entry, sourceNode);
+
+            mPWriter.println("}");
+            mPWriter.flush();
+            mPWriter.close();
+        } catch (IOException e) {
+            System.err.println("IOException:" + e.getMessage());
+        }
+    }
+
+    public void writeRcDeltaDigraphs(ReleaseContent bRelContent, String dirName) {
+        mBRelContent = bRelContent;
+        mTitle = mTitle + "_vs_" + getTitle(bRelContent);
+        compareRcEntries(dirName);
+    }
+
+    public void compareRcEntries(String dirName) {
+        String fileName = String.format("%s/%s.gv", dirName, "RC-files");
+        try {
+            FileWriter fWriter = new FileWriter(fileName);
+            mPWriter = new PrintWriter(fWriter);
+            String rootNode = "root";
+            mPWriter.println("digraph {");
+            mPWriter.println("rankdir=LR;");
+            mPWriter.println("node [shape = box]");
+            mPWriter.println(String.format("%s [label=\"%s\"]", rootNode, getNodeName(mTitle)));
+
+            for (Entry entry : mRelContent.getEntries().values()) {
+                if (entry.getType() == Entry.EntryType.RC) {
+                    // only care if a RC starts Services
+                    if (entry.getDependenciesList().size() > 0) {
+                        writeRcDigraph(entry, rootNode);
+                    }
+                }
+            }
+
+            mPWriter.println("}");
+            mPWriter.flush();
+            mPWriter.close();
+        } catch (IOException e) {
+            System.err.println("IOException:" + e.getMessage());
+        }
+    }
+
+    public void writeRcDigraph(Entry entry, String rootNode) {
+        String rcName = entry.getRelativePath();
+        String sourceNode = getNodeName(rcName);
+        mPWriter.printf(String.format("%s [label=\"%s\"", sourceNode, rcName));
+        checkDelta(entry);
+        mPWriter.println("]");
+
+        mPWriter.println(String.format("%s -> %s", rootNode, sourceNode));
+
+        for (Service target : entry.getServicesList()) {
+            String targetFile =
+                    target.getFile().replace("/system/", "SYSTEM/").replace("/vendor/", "VENDOR/");
+            String targetNode = getNodeName(targetFile);
+            mPWriter.printf(String.format("%s [label=\"%s\"", targetNode, targetFile));
+            Entry targetEntry = mRelContent.getEntries().get(targetFile);
+            if (targetEntry != null) {
+                checkDelta(targetEntry);
+            }
+            mPWriter.println("]");
+
+            String depPath = String.format("%s -> %s", sourceNode, targetNode);
+            mPWriter.println(depPath);
+        }
+    }
+
+    private void checkDelta(Entry srcEntry) {
+        // compare
+        if (mBRelContent != null) {
+            // System.err.println(srcEntry.getRelativePath());
+            Entry bEntry = mBRelContent.getEntries().get(srcEntry.getRelativePath());
+            if (bEntry == null) {
+                // New Entry
+                mPWriter.printf(" fillcolor=\"gold1\" style=\"filled\"");
+            } else {
+                if (srcEntry.getContentId().equals(bEntry.getContentId())) {
+                    // Same
+                    mPWriter.printf(" fillcolor=\"green\" style=\"filled\"");
+                } else {
+                    // Different
+                    mPWriter.printf(" fillcolor=\"red\" style=\"filled\"");
+                }
+            }
+        }
+    }
+
+    private String getNodeName(String note) {
+        return note.replace(".", "_")
+                .replace("@", "")
+                .replace("+", "p")
+                .replace("-", "_")
+                .replace("/", "_");
+    }
+
+    private void printDep(Entry srcEntry, String sourceNode) {
+        mCurLevel += 1;
+        ArrayList<String> allDepList = new ArrayList<>();
+        allDepList.addAll(srcEntry.getDependenciesList());
+        int depCnt = allDepList.size();
+        allDepList.addAll(srcEntry.getDynamicLoadingDependenciesList());
+        int no = 0;
+        for (String dep : allDepList) {
+            boolean goFurther = false;
+            String targetNode = getNodeName(dep);
+
+            String depPath = String.format("%s -> %s", sourceNode, targetNode);
+            if (no < depCnt) {
+                depPath = String.format("%s -> %s", sourceNode, targetNode);
+            } else {
+                // This is Dyanmic Loading
+                depPath = String.format("%s -> %s [color=\"blue\"]", sourceNode, targetNode);
+            }
+
+            if (mDepPathMap.get(depPath) == null) {
+                // Print path once only
+                mDepPathMap.put(depPath, 1);
+                mPWriter.println(depPath);
+            }
+
+            Entry depEntry;
+            String filePath = dep;
+            if (dep.startsWith("/system")) {
+                depEntry = mRelContent.getEntries().get(dep.replace("/system/", "SYSTEM/"));
+            } else if (dep.startsWith("/vendor")) {
+                depEntry = mRelContent.getEntries().get(dep.replace("/vendor/", "VENDOR/"));
+            } else {
+                if (mBits == 32) {
+                    filePath = String.format("SYSTEM/lib/%s", dep);
+                } else {
+                    filePath = String.format("SYSTEM/lib64/%s", dep);
+                }
+
+                depEntry = mRelContent.getEntries().get(filePath);
+                if (depEntry == null) {
+                    // try Vendor
+                    if (mBits == 32) {
+                        filePath = String.format("VENDOR/lib/%s", dep);
+                    } else {
+                        filePath = String.format("VENDOR/lib64/%s", dep);
+                    }
+                    depEntry = mRelContent.getEntries().get(filePath);
+                }
+                if (depEntry == null) {
+                    // try Vendor
+                    if (mBits == 32) {
+                        filePath = String.format("VENDOR/lib/%s", dep);
+                    } else {
+                        filePath = String.format("VENDOR/lib64/%s", dep);
+                    }
+                    depEntry = mRelContent.getEntries().get(filePath);
+                }
+            }
+
+            if (depEntry == null && dep.endsWith("libGLES_android.so")) {
+                // try Vendor
+                if (mBits == 32) {
+                    filePath = "SYSTEM/lib/egl/libGLES_android.so";
+                } else {
+                    filePath = "SYSTEM/lib64/egl/libGLES_android.so";
+                }
+                depEntry = mRelContent.getEntries().get(filePath);
+            }
+
+            if (depEntry != null) {
+                if (mLibMap.get(targetNode) == null) {
+                    // Print Entry node once only
+                    mLibMap.put(targetNode, 1);
+                    mPWriter.printf(String.format("%s [label=\"%s\"", targetNode, dep));
+                    checkDelta(depEntry);
+                    mPWriter.println("]");
+                    // Only visit once
+                    goFurther = true;
+                } else {
+                    // Record max Level for a target
+                    int i = mLibMap.get(targetNode);
+                    mLibMap.put(targetNode, Math.max(i, mCurLevel));
+                }
+
+                if (goFurther) {
+                    printDep(depEntry, targetNode);
+                }
+            } else {
+                System.err.println("cannot find: " + filePath);
+            }
+            no++;
+        }
+        mCurLevel -= 1;
+    }
+
+    private static final String USAGE_MESSAGE =
+            "Usage: java -cp releaseparser.jar com.android.cts.releaseparser.DepPrinter [-options]\n"
+                    + "           to compare A B builds dependency for X \n"
+                    + "Options:\n"
+                    + "\t-a A-Release.pb\t A release Content Protobuf file \n"
+                    + "\t-b B-Release.pb\t B release Content Protobuf file \n"
+                    + "\t-e Exe Name\t generates the Delta Dependency Digraph for the Execuable \n"
+                    + "\t-r \t generates RC file Delta Dependency Digraphs \n"
+                    + "\t \t without -e & -t, it will generate all Delta Dependency Digraphs \n";
+
+    /** Get the argument or print out the usage and exit. */
+    private static void printUsage() {
+        System.out.printf(USAGE_MESSAGE);
+        System.exit(1);
+    }
+
+    /** Get the argument or print out the usage and exit. */
+    private static String getExpectedArg(String[] args, int index) {
+        if (index < args.length) {
+            return args[index];
+        } else {
+            printUsage();
+            return null; // Never will happen because printUsage will call exit(1)
+        }
+    }
+
+    public static void main(final String[] args) {
+        String aPB = null;
+        String bPB = null;
+        String exeName = null;
+        boolean processRcOnly = false;
+
+        for (int i = 0; i < args.length; i++) {
+            if (args[i].startsWith("-")) {
+                if ("-a".equals(args[i])) {
+                    aPB = getExpectedArg(args, ++i);
+                } else if ("-b".equals(args[i])) {
+                    bPB = getExpectedArg(args, ++i);
+                } else if ("-e".equals(args[i])) {
+                    exeName = getExpectedArg(args, ++i);
+                } else if ("-r".equals(args[i])) {
+                    processRcOnly = true;
+                } else {
+                    printUsage();
+                }
+            }
+        }
+        if (aPB == null || bPB == null) {
+            printUsage();
+        }
+
+        try {
+            ReleaseContent aRelContent = ReleaseContent.parseFrom(new FileInputStream(aPB));
+            DepPrinter depPrinter = new DepPrinter(aRelContent);
+            ReleaseContent bRelContent = ReleaseContent.parseFrom(new FileInputStream(bPB));
+            String dirName =
+                    String.format("%s-vs-%s", getTitle(aRelContent), getTitle(bRelContent));
+            File dir = new File(dirName);
+            dir.mkdir();
+            if (processRcOnly) {
+                // General RC delta digraphs
+                depPrinter.writeRcDeltaDigraphs(bRelContent, dirName);
+            } else if (exeName == null) {
+                // General all execuable delta digraphs
+                depPrinter.writeDeltaDigraphs(bRelContent, dirName);
+            } else {
+                depPrinter.writeDeltaDigraph(exeName, bRelContent, String.format("%s/%s.gv", dirName, exeName));
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            System.err.println(e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/ExeParser.java b/tools/release-parser/src/com/android/cts/releaseparser/ExeParser.java
new file mode 100644
index 0000000..0f0af10
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/ExeParser.java
@@ -0,0 +1,32 @@
+/*
+ * 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.releaseparser;
+
+import com.android.cts.releaseparser.ReleaseProto.*;
+
+import java.io.File;
+
+public class ExeParser extends SoParser {
+    public ExeParser(File file) {
+        super(file);
+    }
+
+    @Override
+    public Entry.EntryType getType() {
+        return Entry.EntryType.EXE;
+    }
+}
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/FileParser.java b/tools/release-parser/src/com/android/cts/releaseparser/FileParser.java
new file mode 100644
index 0000000..6584802
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/FileParser.java
@@ -0,0 +1,147 @@
+/*
+ * 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.releaseparser;
+
+import com.android.compatibility.common.util.ReadElf;
+import com.android.cts.releaseparser.ReleaseProto.*;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.List;
+
+public class FileParser {
+    private static final String NO_ID = "";
+    private static final int READ_BLOCK_SIZE = 1024;
+
+    // Target File Extensions
+    private static final String CONFIG_EXT_TAG = ".config";
+    private static final String TEST_SUITE_TRADEFED_TAG = "-tradefed.jar";
+    private static final String JAR_EXT_TAG = ".jar";
+    private static final String APK_EXT_TAG = ".apk";
+    private static final String SO_EXT_TAG = ".so";
+    private static final String ODEX_EXT_TAG = ".odex";
+    private static final String VDEX_EXT_TAG = ".vdex";
+    private static final String BUILD_PROP_TAG = "build.prop";
+    private static final String RC_TAG = ".rc";
+
+    protected File mFile;
+    protected String mContentId;
+    protected String mCodeId;
+
+    public static FileParser getParser(File file) {
+        String fName = file.getName();
+
+        // Starts with SymbolicLink
+        if (isSymbolicLink(file)) {
+            return new SymbolicLinkParser(file);
+        } else if (fName.endsWith(APK_EXT_TAG)) {
+            return new ApkParser(file);
+        } else if (fName.endsWith(CONFIG_EXT_TAG)) {
+            return new TestModuleConfigParser(file);
+        } else if (fName.endsWith(TEST_SUITE_TRADEFED_TAG)) {
+            return new TestSuiteTradefedParser(file);
+        } else if (fName.endsWith(JAR_EXT_TAG)) {
+            // keeps this after TEST_SUITE_TRADEFED_TAG to avoid missing it
+            return new JarParser(file);
+        } else if (fName.endsWith(SO_EXT_TAG)) {
+            return new SoParser(file);
+        } else if (fName.endsWith(ODEX_EXT_TAG)) {
+            return new OdexParser(file);
+        } else if (fName.endsWith(VDEX_EXT_TAG)) {
+            return new VdexParser(file);
+        } else if (fName.endsWith(BUILD_PROP_TAG)) {
+            return new BuildPropParser(file);
+        } else if (fName.endsWith(RC_TAG)) {
+            return new RcParser(file);
+        } else if (ReadElf.isElf(file)) {
+            // keeps this in the end as no Exe Ext name
+            return new ExeParser(file);
+        } else {
+            // Common File Parser
+            return new FileParser(file);
+        }
+    }
+
+    FileParser(File file) {
+        mFile = file;
+        mCodeId = NO_ID;
+        mContentId = NO_ID;
+    }
+
+    public File getFile() {
+        return mFile;
+    }
+
+    public String getFileName() {
+        return mFile.getName();
+    }
+
+    public Entry.EntryType getType() {
+        return Entry.EntryType.FILE;
+    }
+
+    public String getFileContentId() {
+        if (NO_ID.equals(mContentId)) {
+            try {
+                MessageDigest md = MessageDigest.getInstance("SHA-256");
+                FileInputStream fis = new FileInputStream(mFile);
+                byte[] dataBytes = new byte[READ_BLOCK_SIZE];
+                int nread = 0;
+                while ((nread = fis.read(dataBytes)) != -1) {
+                    md.update(dataBytes, 0, nread);
+                }
+                // Converts to Base64 String
+                mContentId = Base64.getEncoder().encodeToString(md.digest());
+            } catch (IOException e) {
+                System.err.println("IOException:" + e.getMessage());
+            } catch (NoSuchAlgorithmException e) {
+                System.err.println("NoSuchAlgorithmException:" + e.getMessage());
+            }
+        }
+        return mContentId;
+    }
+
+    public int getAbiBits() {
+        return 0;
+    }
+
+    public String getAbiArchitecture() {
+        return NO_ID;
+    }
+
+    public String getCodeId() {
+        return mCodeId;
+    }
+
+    public List<String> getDependencies() {
+        return new ArrayList<String>();
+    }
+
+    public List<String> getDynamicLoadingDependencies() {
+        return new ArrayList<String>();
+    }
+
+    private static boolean isSymbolicLink(File f) {
+        // Assumes 0b files are Symbolic Link
+        return (f.length() == 0);
+    }
+}
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/JarParser.java b/tools/release-parser/src/com/android/cts/releaseparser/JarParser.java
new file mode 100644
index 0000000..8948bba
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/JarParser.java
@@ -0,0 +1,32 @@
+/*
+ * 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.releaseparser;
+
+import com.android.cts.releaseparser.ReleaseProto.*;
+
+import java.io.File;
+
+public class JarParser extends FileParser {
+    public JarParser(File file) {
+        super(file);
+    }
+
+    @Override
+    public Entry.EntryType getType() {
+        return Entry.EntryType.JAR;
+    }
+}
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/JarTestFinder.java b/tools/release-parser/src/com/android/cts/releaseparser/JarTestFinder.java
new file mode 100644
index 0000000..b2b693d
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/JarTestFinder.java
@@ -0,0 +1,85 @@
+/*
+ * 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.releaseparser;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/** Representation of the entire CDD. */
+class JarTestFinder {
+
+    public static Collection<Class<?>> getClasses(File jarTestFile)
+            throws IllegalArgumentException {
+        List<Class<?>> classes = new ArrayList<>();
+
+        try (JarFile jarFile = new JarFile(jarTestFile)) {
+            Enumeration<JarEntry> e = jarFile.entries();
+
+            URL[] urls = {new URL(String.format("jar:file:%s!/", jarTestFile.getAbsolutePath()))};
+            URLClassLoader cl =
+                    URLClassLoader.newInstance(urls, JarTestFinder.class.getClassLoader());
+
+            while (e.hasMoreElements()) {
+                JarEntry je = e.nextElement();
+                if (je.isDirectory()
+                        || !je.getName().endsWith(".class")
+                        || je.getName().contains("$")) {
+                    continue;
+                }
+                String className = getClassName(je.getName());
+                if (!className.endsWith("Test")) {
+                    continue;
+                }
+                try {
+                    Class<?> cls = cl.loadClass(className);
+                    int modifiers = cls.getModifiers();
+                    if (!Modifier.isStatic(modifiers)
+                            && !Modifier.isPrivate(modifiers)
+                            && !Modifier.isProtected(modifiers)
+                            && !Modifier.isInterface(modifiers)
+                            && !Modifier.isAbstract(modifiers)) {
+
+                        classes.add(cls);
+                    }
+                } catch (ClassNotFoundException | Error x) {
+                    System.err.println(
+                            String.format(
+                                    "Cannot find test class %s from %s",
+                                    className, jarTestFile.getName()));
+                    x.printStackTrace();
+                }
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return classes;
+    }
+
+    private static String getClassName(String name) {
+        // -6 because of .class
+        return name.substring(0, name.length() - 6).replace('/', '.');
+    }
+}
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/Main.java b/tools/release-parser/src/com/android/cts/releaseparser/Main.java
new file mode 100644
index 0000000..5d6dced
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/Main.java
@@ -0,0 +1,147 @@
+/*
+ * 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.releaseparser;
+
+import com.android.cts.releaseparser.ReleaseProto.*;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Paths;
+
+/** Main of release parser */
+public class Main {
+
+    private static final String USAGE_MESSAGE =
+            "Usage: java -jar releaseparser.jar [-options] <folder> [args...]\n"
+                    + "           to prase a release content in the folder\n"
+                    + "Options:\n"
+                    + "\t-a API#\t API Level, e.g. 27 \n"
+                    + "\t-i PATH\t path to a release folder \n"
+                    + "\t-o PATH\t path to output files \n";
+
+    private Main() {}
+
+    /** Get the argument or print out the usage and exit. */
+    private static void printUsage() {
+        System.out.printf(USAGE_MESSAGE);
+        System.exit(1);
+    }
+
+    /** Get the argument or print out the usage and exit. */
+    private static String getExpectedArg(String[] args, int index) {
+        if (index < args.length) {
+            return args[index];
+        } else {
+            printUsage();
+            return null; // Never will happen because printUsage will call exit(1)
+        }
+    }
+
+    public static void main(final String[] args) {
+        String relNameVer;
+        String relFolder = "";
+        String outputPath = "";
+        int apiL = 27;
+
+        for (int i = 0; i < args.length; i++) {
+            if (args[i].startsWith("-")) {
+                if ("-o".equals(args[i])) {
+                    outputPath = getExpectedArg(args, ++i);
+                    File file = new File(outputPath);
+                    // Only acception a folder
+                    if (!file.isDirectory()) {
+                        printUsage();
+                    }
+                } else if ("-i".equals(args[i])) {
+                    relFolder = getExpectedArg(args, ++i);
+                    File file = new File(relFolder);
+                    // Only acception a folder
+                    if (!file.isDirectory()) {
+                        printUsage();
+                    }
+                } else if ("-a".equals(args[i])) {
+                    apiL = Integer.parseInt(getExpectedArg(args, ++i));
+                } else {
+                    printUsage();
+                }
+            }
+        }
+
+        if ("".equals(relFolder) || "".equals(outputPath)) {
+            printUsage();
+        }
+
+        ReleaseParser relParser = new ReleaseParser(relFolder);
+        relNameVer = relParser.getRelNameVer();
+
+        relParser.writeRelesaeContentCsvFile(
+                relNameVer,
+                Paths.get(outputPath, String.format("%s-ReleaseContent.csv", relNameVer))
+                        .toString());
+
+        relParser.writeKnownFailureCsvFile(
+                relNameVer,
+                Paths.get(outputPath, String.format("%s-KnownFailure.csv", relNameVer)).toString());
+
+        ReleaseContent relContent = relParser.getReleaseContent();
+
+        // Write release content message to disk.
+        try {
+            FileOutputStream output =
+                    new FileOutputStream(
+                            Paths.get(outputPath, String.format("%s-ReleaseContent.pb", relNameVer))
+                                    .toString());
+            try {
+                relContent.writeTo(output);
+            } finally {
+                output.close();
+            }
+        } catch (IOException e) {
+            System.err.println("IOException:" + e.getMessage());
+        }
+
+        TestSuiteParser tsParser = new TestSuiteParser(relContent, relFolder, apiL);
+        if (tsParser.getTestSuite().getModulesList().size() == 0) {
+            // skip if no test module
+            return;
+        }
+
+        tsParser.writeCsvFile(
+                relNameVer,
+                Paths.get(outputPath, String.format("%s-TestCase.csv", relNameVer)).toString());
+        tsParser.writeModuleCsvFile(
+                relNameVer,
+                Paths.get(outputPath, String.format("%s-TestModule.csv", relNameVer)).toString());
+
+        // Write test suite content message to disk.
+        TestSuite testSuite = tsParser.getTestSuite();
+        try {
+            FileOutputStream output =
+                    new FileOutputStream(
+                            Paths.get(outputPath, String.format("%s-TestSuite.pb", relNameVer))
+                                    .toString());
+            try {
+                testSuite.writeTo(output);
+            } finally {
+                output.close();
+            }
+        } catch (IOException e) {
+            System.err.println("IOException:" + e.getMessage());
+        }
+    }
+}
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/OdexParser.java b/tools/release-parser/src/com/android/cts/releaseparser/OdexParser.java
new file mode 100644
index 0000000..8de0657
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/OdexParser.java
@@ -0,0 +1,32 @@
+/*
+ * 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.releaseparser;
+
+import com.android.cts.releaseparser.ReleaseProto.*;
+
+import java.io.File;
+
+public class OdexParser extends FileParser {
+    public OdexParser(File file) {
+        super(file);
+    }
+
+    @Override
+    public Entry.EntryType getType() {
+        return Entry.EntryType.ODEX;
+    }
+}
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/RcParser.java b/tools/release-parser/src/com/android/cts/releaseparser/RcParser.java
new file mode 100644
index 0000000..668e1e4
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/RcParser.java
@@ -0,0 +1,147 @@
+/*
+ * 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.releaseparser;
+
+import com.android.cts.releaseparser.ReleaseProto.*;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class RcParser extends FileParser {
+    private static String OPTION_CLASS = "class ";
+    private static String OPTION_USER = "user ";
+    private static String OPTION_GROUP = "group ";
+    private static String OPTION_WRITEPID = "writepid ";
+
+    private Entry.EntryType mType;
+    private List<Service> mServices;
+
+    public RcParser(File file) {
+        super(file);
+    }
+
+    @Override
+    public Entry.EntryType getType() {
+        if (mType == null) {
+            parseFile();
+        }
+        return mType;
+    }
+
+    @Override
+    public List<String> getDependencies() {
+        if (mServices == null) {
+            parseFile();
+        }
+
+        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);
+        }
+
+        return new ArrayList<String>(dependencies.keySet());
+    }
+
+    public List<Service> getServiceList() {
+        if (mServices == null) {
+            parseFile();
+        }
+        return mServices;
+    }
+
+    public String toString() {
+        return toString(mServices);
+    }
+
+    public static String toString(List<Service> services) {
+        StringBuilder result = new StringBuilder();
+        for (Service service : services) {
+            result.append(
+                    String.format(
+                            "%s %s %s %s;",
+                            service.getName(),
+                            service.getClazz(),
+                            service.getUser(),
+                            service.getGroup()));
+            // System.err.println(String.format("RcParser-toString %s %s %s ", service.getName(),
+            // service.getFile(), String.join(" ", service.getArgumentsList())));
+        }
+        return result.toString();
+    }
+
+    private void parseFile() {
+        // rc file spec. at android/system/core/init/README.md
+        // android/system/core/init/init.cpp?q=CreateParser
+        mServices = new ArrayList<Service>();
+        try {
+            FileReader fileReader = new FileReader(getFile());
+            BufferedReader buffReader = new BufferedReader(fileReader);
+
+            String line;
+            while ((line = buffReader.readLine()) != null) {
+                if (line.startsWith("service")) {
+                    Service.Builder serviceBld = Service.newBuilder();
+                    String[] phases = line.split(" ");
+                    serviceBld.setName(phases[1]);
+                    serviceBld.setFile(phases[2]);
+                    if (phases.length > 3) {
+                        serviceBld.addAllArguments(Arrays.asList(phases).subList(3, phases.length));
+                    }
+                    String sLine;
+                    while ((sLine = buffReader.readLine()) != null) {
+                        String sTrimLine = sLine.trim();
+                        if (sTrimLine.isEmpty()) {
+                            // End of a service block
+                            break;
+                        }
+                        if (sTrimLine.startsWith("#")) {
+                            // Skips comment
+                            continue;
+                        } else if (sTrimLine.startsWith(OPTION_CLASS)) {
+                            serviceBld.setClazz(sTrimLine.substring(OPTION_CLASS.length()));
+                        } else if (sTrimLine.startsWith(OPTION_USER)) {
+                            serviceBld.setUser(sTrimLine.substring(OPTION_USER.length()));
+                        } else if (sTrimLine.startsWith(OPTION_GROUP)) {
+                            serviceBld.setGroup(sTrimLine.substring(OPTION_GROUP.length()));
+                        } else if (sTrimLine.startsWith(OPTION_WRITEPID)) {
+                            serviceBld.setGroup(sTrimLine.substring(OPTION_WRITEPID.length()));
+                        } else {
+                            serviceBld.addOptions(sTrimLine);
+                        }
+                    }
+                    mServices.add(serviceBld.build());
+                }
+            }
+            fileReader.close();
+            mType = Entry.EntryType.RC;
+        } catch (IOException e) {
+            // file is not a RC Config
+            System.err.println("RcParser err:" + getFileName() + "\n" + e.getMessage());
+            mType = super.getType();
+        }
+        // System.err.println(this.toString());
+    }
+}
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/ReleaseParser.java b/tools/release-parser/src/com/android/cts/releaseparser/ReleaseParser.java
new file mode 100644
index 0000000..0e370c4
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/ReleaseParser.java
@@ -0,0 +1,273 @@
+/*
+ * 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.releaseparser;
+
+import com.android.cts.releaseparser.ReleaseProto.*;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+class ReleaseParser {
+    // configuration option
+    private static final String NOT_SHARDABLE_TAG = "not-shardable";
+    // test class option
+    private static final String RUNTIME_HIT_TAG = "runtime-hint";
+    // com.android.tradefed.testtype.AndroidJUnitTest option
+    private static final String PACKAGE_TAG = "package";
+    // com.android.compatibility.common.tradefed.testtype.JarHostTest option
+    private static final String JAR_NAME_TAG = "jar";
+    // com.android.tradefed.testtype.GTest option
+    private static final String NATIVE_TEST_DEVICE_PATH_TAG = "native-test-device-path";
+    private static final String MODULE_TAG = "module-name";
+
+    private static final String SUITE_API_INSTALLER_TAG =
+            "com.android.tradefed.targetprep.suite.SuiteApkInstaller";
+    private static final String JAR_HOST_TEST_TAG =
+            "com.android.compatibility.common.tradefed.testtype.JarHostTest";
+    // com.android.tradefed.targetprep.suite.SuiteApkInstaller option
+    private static final String TEST_FILE_NAME_TAG = "test-file-name";
+    // com.android.compatibility.common.tradefed.targetprep.FilePusher option
+    private static final String PUSH_TAG = "push";
+
+    // test class
+    private static final String ANDROID_JUNIT_TEST_TAG =
+            "com.android.tradefed.testtype.AndroidJUnitTest";
+
+    private static final String TESTCASES_FOLDER_FORMAT = "testcases/%s";
+
+    private final String mFolderPath;
+    private Path mRootPath;
+    private ReleaseContent.Builder mRelContentBuilder;
+    private Map<String, Entry> mEntries;
+
+    ReleaseParser(String folder) {
+        mFolderPath = folder;
+        File fFile = new File(mFolderPath);
+        mRootPath = Paths.get(fFile.getAbsolutePath());
+        mEntries = new HashMap<String, Entry>();
+    }
+
+    public String getRelNameVer() {
+        ReleaseContent relContent = getReleaseContent();
+        return String.format(
+                "%s-%s-%s",
+                relContent.getName(), relContent.getVersion(), relContent.getBuildNumber());
+    }
+
+    public ReleaseContent getReleaseContent() {
+        if (mRelContentBuilder == null) {
+            mRelContentBuilder = ReleaseContent.newBuilder();
+            // also add the root folder entry
+            Entry.Builder fBuilder = parseFolder(mFolderPath);
+            if (mRelContentBuilder.getName().equals("")) {
+                System.err.println("Release Name unknown!");
+                mRelContentBuilder.setName(mFolderPath);
+            }
+            fBuilder.setName(
+                    String.format(
+                            "%s-%s-%s",
+                            mRelContentBuilder.getName(),
+                            mRelContentBuilder.getVersion(),
+                            mRelContentBuilder.getBuildNumber()));
+            fBuilder.setParentFolder(mFolderPath);
+            Entry fEntry = fBuilder.build();
+            mEntries.put(fEntry.getRelativePath(), fEntry);
+            mRelContentBuilder.putAllEntries(mEntries);
+        }
+        return mRelContentBuilder.build();
+    }
+
+    // Parse all files in a folder and return the foler entry builder
+    private Entry.Builder parseFolder(String fPath) {
+        Entry.Builder folderEntry = Entry.newBuilder();
+        File folder = new File(fPath);
+        Path folderPath = Paths.get(folder.getAbsolutePath());
+        String folderRelativePath = mRootPath.relativize(folderPath).toString();
+        File[] fileList = folder.listFiles();
+        Long folderSize = 0L;
+        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();
+                Entry.Builder fileEntry = Entry.newBuilder();
+                fileEntry.setName(file.getName());
+                fileEntry.setSize(file.length());
+                fileEntry.setRelativePath(fileRelativePath);
+                fileEntry.setParentFolder(folderRelativePath);
+
+                FileParser fParser = FileParser.getParser(file);
+                fileEntry.setContentId(fParser.getFileContentId());
+                fileEntry.setCodeId(fParser.getCodeId());
+
+                Entry.EntryType eType = fParser.getType();
+
+                fileEntry.setType(eType);
+                switch (eType) {
+                    case TEST_MODULE_CONFIG:
+                        TestModuleConfigParser tmcParser = (TestModuleConfigParser) fParser;
+                        fileEntry.setTestModuleConfig(tmcParser.getTestModuleConfig());
+                        break;
+                    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());
+                        break;
+                    case BUILD_PROP:
+                        BuildPropParser bpParser = (BuildPropParser) fParser;
+                        try {
+                            mRelContentBuilder.setName(bpParser.getName());
+                            mRelContentBuilder.setFullname(bpParser.getFullName());
+                            mRelContentBuilder.setBuildNumber(bpParser.getBuildNumber());
+                            mRelContentBuilder.setVersion(bpParser.getVersion());
+                        } catch (Exception e) {
+                            System.err.println(
+                                    "No product name, version & etc. in "
+                                            + file.getAbsoluteFile()
+                                            + ", err:"
+                                            + e.getMessage());
+                        }
+                        break;
+                    case RC:
+                        RcParser rcParser = (RcParser) fParser;
+                        fileEntry.addAllServices(rcParser.getServiceList());
+                        break;
+                    default:
+                }
+                // System.err.println("File:" + file.getAbsoluteFile());
+                fileEntry.addAllDependencies(fParser.getDependencies());
+                fileEntry.addAllDynamicLoadingDependencies(fParser.getDynamicLoadingDependencies());
+                fileEntry.setAbiBits(fParser.getAbiBits());
+                fileEntry.setAbiArchitecture(fParser.getAbiArchitecture());
+
+                Entry fEntry = fileEntry.build();
+                entryList.add(fEntry);
+                mEntries.put(fEntry.getRelativePath(), fEntry);
+                folderSize += file.length();
+            } else if (file.isDirectory()) {
+                // Checks subfolders
+                Entry.Builder subFolderEntry = parseFolder(file.getAbsolutePath());
+                subFolderEntry.setParentFolder(folderRelativePath);
+                Entry sfEntry = subFolderEntry.build();
+                entryList.add(sfEntry);
+                mEntries.put(sfEntry.getRelativePath(), sfEntry);
+                folderSize += sfEntry.getSize();
+            }
+        }
+        folderEntry.setName(folderRelativePath);
+        folderEntry.setSize(folderSize);
+        folderEntry.setType(Entry.EntryType.FOLDER);
+        folderEntry.setContentId(getFolderContentId(folderEntry, entryList));
+        folderEntry.setRelativePath(folderRelativePath);
+        return folderEntry;
+    }
+
+    private static String getFolderContentId(Entry.Builder folderEntry, List<Entry> entryList) {
+        String id = null;
+        try {
+            MessageDigest md = MessageDigest.getInstance("SHA-256");
+            for (Entry entry : entryList) {
+                md.update(entry.getContentId().getBytes(StandardCharsets.UTF_8));
+            }
+            // Converts to Base64 String
+            id = Base64.getEncoder().encodeToString(md.digest());
+        } catch (NoSuchAlgorithmException e) {
+            System.err.println("NoSuchAlgorithmException:" + e.getMessage());
+        }
+        return id;
+    }
+
+    // writes releaes content to a CSV file
+    public void writeRelesaeContentCsvFile(String relNameVer, String csvFile) {
+        try {
+            FileWriter fWriter = new FileWriter(csvFile);
+            PrintWriter pWriter = new PrintWriter(fWriter);
+            // Header
+            pWriter.printf(
+                    "release,type,name,size,relative_path,content_id,parent_folder,code_id,architecture,bits,dependencies,dynamic_loading_dependencies,services\n");
+            for (Entry entry : getFileEntriesList()) {
+                pWriter.printf(
+                        "%s,%s,%s,%d,%s,%s,%s,%s,%s,%d,%s,%s,%s\n",
+                        relNameVer,
+                        entry.getType(),
+                        entry.getName(),
+                        entry.getSize(),
+                        entry.getRelativePath(),
+                        entry.getContentId(),
+                        entry.getParentFolder(),
+                        entry.getCodeId(),
+                        entry.getAbiArchitecture(),
+                        entry.getAbiBits(),
+                        String.join(" ", entry.getDependenciesList()),
+                        String.join(" ", entry.getDynamicLoadingDependenciesList()),
+                        RcParser.toString(entry.getServicesList()));
+            }
+            pWriter.flush();
+            pWriter.close();
+        } catch (IOException e) {
+            System.err.println("IOException:" + e.getMessage());
+        }
+    }
+
+    // writes known failures to a CSV file
+    public void writeKnownFailureCsvFile(String relNameVer, String csvFile) {
+        ReleaseContent relContent = getReleaseContent();
+        if (relContent.getKnownFailuresList().size() == 0) {
+            // Skip if no Known Failures
+            return;
+        }
+
+        try {
+            FileWriter fWriter = new FileWriter(csvFile);
+            PrintWriter pWriter = new PrintWriter(fWriter);
+            //Header
+            pWriter.printf("release,compatibility:exclude-filter\n");
+            for (String kf : relContent.getKnownFailuresList()) {
+                pWriter.printf("%s,%s\n", relNameVer, kf);
+            }
+            pWriter.flush();
+            pWriter.close();
+        } catch (IOException e) {
+            System.err.println("IOException:" + e.getMessage());
+        }
+    }
+
+    public Collection<Entry> getFileEntriesList() {
+        return getReleaseContent().getEntries().values();
+    }
+}
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/SoParser.java b/tools/release-parser/src/com/android/cts/releaseparser/SoParser.java
new file mode 100644
index 0000000..573986f
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/SoParser.java
@@ -0,0 +1,140 @@
+/*
+ * 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.releaseparser;
+
+import com.android.compatibility.common.util.ReadElf;
+import com.android.cts.releaseparser.ReleaseProto.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class SoParser extends FileParser {
+    private int mBits;
+    private String mArch;
+    private List<String> mDependencies;
+    private List<String> mDynamicLoadingDependencies;
+
+    public SoParser(File file) {
+        super(file);
+        mBits = 0;
+        mArch = "";
+    }
+
+    @Override
+    public Entry.EntryType getType() {
+        return Entry.EntryType.SO;
+    }
+
+    @Override
+    public String getCodeId() {
+        return getFileContentId();
+    }
+
+    @Override
+    public List<String> getDependencies() {
+        if (mDependencies == null) {
+            try {
+                ReadElf elf = ReadElf.read(getFile());
+                mBits = elf.getBits();
+                mArch = elf.getArchitecture();
+                mDependencies = elf.getDynamicDependencies();
+
+                // System.out.println(String.format("SoParser: %s, %s", getFileName(),
+                // depList.toString()));
+
+                // Check Dynamic Loading dependencies
+                mDynamicLoadingDependencies = getDynamicLoadingDependencies(elf);
+            } catch (Exception ex) {
+                System.err.println(
+                        String.format(
+                                "err: SoParser can not getDependencies from %s.", getFileName()));
+                mDependencies = super.getDependencies();
+                mDynamicLoadingDependencies = super.getDynamicLoadingDependencies();
+            }
+        }
+        return mDependencies;
+    }
+
+    @Override
+    public List<String> getDynamicLoadingDependencies() {
+        if (mDynamicLoadingDependencies == null) {
+            // This also parses DynamicLoadingDependencies
+            getDependencies();
+        }
+        return mDynamicLoadingDependencies;
+    }
+
+    @Override
+    public int getAbiBits() {
+        if (mBits == 0) {
+            try {
+                ReadElf elf = ReadElf.read(getFile());
+                mBits = elf.getBits();
+                mArch = elf.getArchitecture();
+            } catch (Exception ex) {
+                mBits = -1;
+                mArch = "unknown";
+            }
+        }
+        return mBits;
+    }
+
+    @Override
+    public String getAbiArchitecture() {
+        getAbiBits();
+        return mArch;
+    }
+
+    private List<String> getDynamicLoadingDependencies(ReadElf elf) throws IOException {
+        List<String> depList = new ArrayList<>();
+        // check if it does refer to dlopen
+        if (elf.getDynamicSymbol("dlopen") != null) {
+            List<String> roStrings = elf.getRoStrings();
+            for (String str : roStrings) {
+                // skip ".so" or less
+                if (str.length() < 4) {
+                    continue;
+                }
+
+                if (str.endsWith(".so")) {
+                    // skip itself
+                    if (str.contains(getFileName())) {
+                        continue;
+                    }
+                    if (str.contains(" ")) {
+                        continue;
+                    }
+                    if (str.contains("?")) {
+                        continue;
+                    }
+                    if (str.contains("%")) {
+                        System.err.println("ToDo getDynamicLoadingDependencies: " + str);
+                        continue;
+                    }
+                    if (str.startsWith("_")) {
+                        System.err.println("ToDo getDynamicLoadingDependencies: " + str);
+                        continue;
+                    }
+                    depList.add(str);
+                }
+            }
+        }
+        return depList;
+    }
+}
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/SymbolicLinkParser.java b/tools/release-parser/src/com/android/cts/releaseparser/SymbolicLinkParser.java
new file mode 100644
index 0000000..3f235d4
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/SymbolicLinkParser.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.releaseparser;
+
+import com.android.cts.releaseparser.ReleaseProto.*;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class SymbolicLinkParser extends FileParser {
+    public SymbolicLinkParser(File file) {
+        super(file);
+    }
+
+    @Override
+    public Entry.EntryType getType() {
+        return Entry.EntryType.SYMBOLIC_LINK;
+    }
+
+    @Override
+    public List<String> getDependencies() {
+        ArrayList results = new ArrayList<String>();
+        results.add(getFileName());
+        return results;
+    }
+
+    @Override
+    public String getFileContentId() {
+        // NO_ID for Symbolic Link
+        return mContentId;
+    }
+}
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/TestModuleConfigHandler.java b/tools/release-parser/src/com/android/cts/releaseparser/TestModuleConfigHandler.java
new file mode 100644
index 0000000..5251f07
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/TestModuleConfigHandler.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.releaseparser;
+
+import com.android.cts.releaseparser.ReleaseProto.*;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * {@link DefaultHandler} that builds an empty {@link ApiCoverage} object from scanning
+ * TestModule.xml.
+ */
+class TestModuleConfigHandler extends DefaultHandler {
+    private static final String CONFIGURATION_TAG = "configuration";
+    private static final String DESCRIPTION_TAG = "description";
+    private static final String OPTION_TAG = "option";
+    private static final String TARGET_PREPARER_TAG = "target_preparer";
+    private static final String TEST_TAG = "test";
+    private static final String CLASS_TAG = "class";
+    private static final String NAME_TAG = "name";
+    private static final String KEY_TAG = "key";
+    private static final String VALUE_TAG = "value";
+    private static final String MODULE_NAME_TAG = "module-name";
+    private static final String GTEST_CLASS_TAG = "com.android.tradefed.testtype.GTest";
+    // com.android.compatibility.common.tradefed.testtype.JarHostTest option
+    private static final String JAR_TAG = "jar";
+    // com.android.tradefed.targetprep.suite.SuiteApkInstaller option
+    private static final String TEST_FILE_NAME_TAG = "test-file-name";
+
+    private TestModuleConfig.Builder mTestModuleConfig;
+    private TestModuleConfig.TargetPreparer.Builder mTargetPreparer;
+    private TestModuleConfig.TestClass.Builder mTestCase;
+    private String mModuleName = null;
+
+    TestModuleConfigHandler(String configFileName) {
+        mTestModuleConfig = TestModuleConfig.newBuilder();
+        mTestCase = null;
+        mTargetPreparer = null;
+        // Default Module Name is the Config File Name
+        mModuleName = configFileName.replaceAll(".config$", "");
+    }
+
+    @Override
+    public void startElement(String uri, String localName, String name, Attributes attributes)
+            throws SAXException {
+        super.startElement(uri, localName, name, attributes);
+
+        switch (localName) {
+            case CONFIGURATION_TAG:
+                if (null != attributes.getValue(DESCRIPTION_TAG)) {
+                    mTestModuleConfig.setDescription(attributes.getValue(DESCRIPTION_TAG));
+                } else {
+                    mTestModuleConfig.setDescription("WARNING: no description.");
+                }
+                break;
+            case TEST_TAG:
+                mTestCase = TestModuleConfig.TestClass.newBuilder();
+                mTestCase.setTestClass(attributes.getValue(CLASS_TAG));
+                break;
+            case TARGET_PREPARER_TAG:
+                mTargetPreparer = TestModuleConfig.TargetPreparer.newBuilder();
+                mTargetPreparer.setTestClass(attributes.getValue(CLASS_TAG));
+                break;
+            case OPTION_TAG:
+                Option.Builder option = Option.newBuilder();
+                option.setName(attributes.getValue(NAME_TAG));
+                option.setValue(attributes.getValue(VALUE_TAG));
+                String keyStr = attributes.getValue(KEY_TAG);
+                if (null != keyStr) {
+                    option.setKey(keyStr);
+                }
+                if (null != mTestCase) {
+                    mTestCase.addOptions(option);
+                    switch (option.getName()) {
+                        case JAR_TAG:
+                            mTestModuleConfig.addTestJars(option.getValue());
+                            break;
+                        case GTEST_CLASS_TAG:
+                            mModuleName = option.getValue();
+                            break;
+                    }
+                } else if (null != mTargetPreparer) {
+                    mTargetPreparer.addOptions(option);
+                    if (TEST_FILE_NAME_TAG.equalsIgnoreCase(option.getName())) {
+                        mTestModuleConfig.addTestFileNames(option.getValue());
+                    }
+                }
+                break;
+        }
+    }
+
+    @Override
+    public void endElement(String uri, String localName, String name) throws SAXException {
+        super.endElement(uri, localName, name);
+        switch (localName) {
+            case CONFIGURATION_TAG:
+                mTestModuleConfig.setModuleName(mModuleName);
+                break;
+            case TARGET_PREPARER_TAG:
+                mTestModuleConfig.addTargetPreparers(mTargetPreparer);
+                mTargetPreparer = null;
+                break;
+            case TEST_TAG:
+                mTestModuleConfig.addTestClasses(mTestCase);
+                mTestCase = null;
+                break;
+        }
+    }
+
+    public String getModuleName() {
+        return mModuleName;
+    }
+
+    public String getTestClassName() {
+        //return the 1st Test Class
+        return mTestModuleConfig.getTestClassesList().get(0).getTestClass();
+    }
+
+    public TestModuleConfig getTestModuleConfig() {
+        return mTestModuleConfig.build();
+    }
+}
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/TestModuleConfigParser.java b/tools/release-parser/src/com/android/cts/releaseparser/TestModuleConfigParser.java
new file mode 100644
index 0000000..2c806c2
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/TestModuleConfigParser.java
@@ -0,0 +1,71 @@
+/*
+ * 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.releaseparser;
+
+import com.android.cts.releaseparser.ReleaseProto.*;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+
+public class TestModuleConfigParser extends FileParser {
+    private Entry.EntryType mType;
+    private TestModuleConfig mTestModuleConfig;
+
+    public TestModuleConfigParser(File file) {
+        super(file);
+    }
+
+    @Override
+    public Entry.EntryType getType() {
+        if (mType == null) {
+            parseFile();
+        }
+        return mType;
+    }
+
+    public TestModuleConfig getTestModuleConfig() {
+        if (mType == null) {
+            parseFile();
+        }
+        return mTestModuleConfig;
+    }
+
+    private void parseFile() {
+        try {
+            XMLReader xmlReader = XMLReaderFactory.createXMLReader();
+            TestModuleConfigHandler testModuleXmlHandler =
+                    new TestModuleConfigHandler(getFileName());
+            xmlReader.setContentHandler(testModuleXmlHandler);
+            FileReader fileReader = new FileReader(getFile());
+            xmlReader.parse(new InputSource(fileReader));
+            mTestModuleConfig = testModuleXmlHandler.getTestModuleConfig();
+            fileReader.close();
+            mType = Entry.EntryType.TEST_MODULE_CONFIG;
+        } catch (IOException | SAXException e) {
+            // file is not a Test Module Config
+            System.err.println(
+                    "TestModuleConfigParser err:" + getFileName() + "\n" + e.getMessage());
+            mType = Entry.EntryType.FILE;
+        }
+    }
+}
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/TestSuiteParser.java b/tools/release-parser/src/com/android/cts/releaseparser/TestSuiteParser.java
new file mode 100644
index 0000000..ce97882
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/TestSuiteParser.java
@@ -0,0 +1,633 @@
+/*
+ * 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.releaseparser;
+
+import com.android.cts.releaseparser.ReleaseProto.*;
+import com.android.tradefed.testtype.IRemoteTest;
+
+import junit.framework.Test;
+
+import org.jf.dexlib2.AccessFlags;
+import org.jf.dexlib2.DexFileFactory;
+import org.jf.dexlib2.Opcodes;
+import org.jf.dexlib2.iface.Annotation;
+import org.jf.dexlib2.iface.AnnotationElement;
+import org.jf.dexlib2.iface.ClassDef;
+import org.jf.dexlib2.iface.DexFile;
+import org.jf.dexlib2.iface.Method;
+import org.jf.dexlib2.iface.value.TypeEncodedValue;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite.SuiteClasses;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Paths;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+class TestSuiteParser {
+    // JUNIT3 Test suffix
+    private static final String TEST_TAG = "Test;";
+    // Some may ends with Tests e.g. cts/tests/tests/accounts/src/android/accounts/cts/AbstractAuthenticatorTests.java
+    private static final String TESTS_TAG = "Tests;";
+    private static final String TEST_PREFIX_TAG = "test";
+    private static final String DEPRECATED_ANNOTATION_TAG = "Ljava/lang/Deprecated;";
+    private static final String RUN_WITH_ANNOTATION_TAG = "Lorg/junit/runner/RunWith;";
+    private static final String TEST_ANNOTATION_TAG = "Lorg/junit/Test;";
+    private static final String SUPPRESS_ANNOTATION_TAG = "/Suppress;";
+    private static final String ANDROID_JUNIT4_TEST_TAG =
+            "Landroid/support/test/runner/AndroidJUnit4;";
+    private static final String PARAMETERIZED_TEST_TAG = "Lorg/junit/runners/Parameterized;";
+
+    // configuration option
+    private static final String NOT_SHARDABLE_TAG = "not-shardable";
+    // test class option
+    private static final String RUNTIME_HIT_TAG = "runtime-hint";
+    // com.android.tradefed.testtype.AndroidJUnitTest option
+    private static final String PACKAGE_TAG = "package";
+    // com.android.compatibility.common.tradefed.testtype.JarHostTest option
+    private static final String JAR_TAG = "jar";
+    // com.android.tradefed.testtype.GTest option
+    private static final String NATIVE_TEST_DEVICE_PATH_TAG = "native-test-device-path";
+    private static final String MODULE_TAG = "module-name";
+    private static final String TESTCASES_FOLDER_FORMAT = "testcases/%s";
+
+    private static final String SUITE_API_INSTALLER_TAG =
+            "com.android.tradefed.targetprep.suite.SuiteApkInstaller";
+    private static final String HOST_TEST_CLASS_TAG =
+            "com.android.compatibility.common.tradefed.testtype.JarHostTest";
+    // com.android.tradefed.targetprep.suite.SuiteApkInstaller option
+    private static final String TEST_FILE_NAME_TAG = "test-file-name";
+    // com.android.compatibility.common.tradefed.targetprep.FilePusher option
+    private static final String PUSH_TAG = "push";
+
+    // test class
+    private static final String ANDROID_JUNIT_TEST_TAG =
+            "com.android.tradefed.testtype.AndroidJUnitTest";
+    private static final String DEQP_TEST_TAG = "com.drawelements.deqp.runner.DeqpTestRunner";
+    private static final String GTEST_TAG = "com.android.tradefed.testtype.GTest";
+    private static final String LIBCORE_TEST_TAG = "com.android.compatibility.testtype.LibcoreTest";
+    private static final String DALVIK_TEST_TAG = "com.android.compatibility.testtype.DalvikTest";
+
+    // Target File Extensions
+    private static final String CONFIG_EXT_TAG = ".config";
+    private static final String CONFIG_REGEX = ".config$";
+    private static final String JAR_EXT_TAG = ".jar";
+    private static final String APK_EXT_TAG = ".apk";
+    private static final String SO_EXT_TAG = ".so";
+
+    // [module].[class]#[method]
+    public static final String TESTCASE_NAME_FORMAT = "%s.%s#%s";
+
+    private final String mFolderPath;
+    private ReleaseContent mRelContent;
+    private final int mApiLevel;
+    private TestSuite.Builder mTSBuilder;
+
+    TestSuiteParser(ReleaseContent relContent, String folder, int apiL) {
+        mFolderPath = folder;
+        mRelContent = relContent;
+        mApiLevel = apiL;
+    }
+
+    public TestSuite getTestSuite() {
+        if (mTSBuilder == null) {
+            mTSBuilder = praseTestSuite();
+        }
+        return mTSBuilder.build();
+    }
+
+    private TestSuite.Builder praseTestSuite() {
+        TestSuite.Builder tsBuilder = TestSuite.newBuilder();
+
+        tsBuilder.setName(mRelContent.getName());
+        tsBuilder.setVersion(mRelContent.getVersion());
+        tsBuilder.setBuildNumber(mRelContent.getBuildNumber());
+
+        // Iterates all file
+        for (Entry entry : getFileEntriesList(mRelContent)) {
+            // Only parses test module config files
+            if (Entry.EntryType.TEST_MODULE_CONFIG == entry.getType()) {
+                TestModuleConfig config = entry.getTestModuleConfig();
+                TestSuite.Module.Builder moduleBuilder = praseModule(config);
+                moduleBuilder.setConfigFile(entry.getRelativePath());
+                tsBuilder.addModules(moduleBuilder);
+            }
+        }
+        return tsBuilder;
+    }
+
+    private TestSuite.Module.Builder praseModule(TestModuleConfig config) {
+        TestSuite.Module.Builder moduleBuilder = TestSuite.Module.newBuilder();
+        // parse test package and class
+        List<TestModuleConfig.TestClass> testClassesList = config.getTestClassesList();
+        moduleBuilder.setName(config.getModuleName());
+        for (TestModuleConfig.TestClass tClass : testClassesList) {
+            String testClass = tClass.getTestClass();
+            moduleBuilder.setTestClass(testClass);
+            switch (testClass) {
+                case ANDROID_JUNIT_TEST_TAG:
+                    moduleBuilder.setTestType(TestSuite.TestType.ANDROIDJUNIT);
+                    parseAndroidJUnitTest(moduleBuilder, config, tClass.getTestClass());
+                    break;
+                case HOST_TEST_CLASS_TAG:
+                    moduleBuilder.setTestType(TestSuite.TestType.JAVAHOST);
+                    parseJavaHostTest(moduleBuilder, config, tClass.getTestClass());
+                    break;
+                default:
+                    //ToDo
+                    moduleBuilder.setTestType(TestSuite.TestType.UNKNOWN);
+                    TestSuite.Module.Package.Builder pkgBuilder =
+                            TestSuite.Module.Package.newBuilder();
+                    moduleBuilder.addPackages(pkgBuilder);
+                    System.err.printf(
+                            "ToDo Test Type: %s %s\n", tClass.getTestClass(), tClass.getPackage());
+            }
+        }
+        return moduleBuilder;
+    }
+
+    private void parseAndroidJUnitTest(
+            TestSuite.Module.Builder moduleBuilder, TestModuleConfig config, String tClass) {
+        // getting apk list from Test Module Configuration
+        List<TestModuleConfig.TargetPreparer> tPrepList = config.getTargetPreparersList();
+        for (TestModuleConfig.TargetPreparer tPrep : tPrepList) {
+            for (Option opt : tPrep.getOptionsList()) {
+                if (TEST_FILE_NAME_TAG.equalsIgnoreCase(opt.getName())) {
+                    TestSuite.Module.Package.Builder pkgBuilder =
+                            TestSuite.Module.Package.newBuilder();
+                    String testFileName = opt.getValue();
+                    Entry tEntry = getFileEntry(testFileName);
+                    pkgBuilder.setName(testFileName);
+                    pkgBuilder.setPackageFile(tEntry.getRelativePath());
+                    pkgBuilder.setContentId(tEntry.getContentId());
+                    parseApkTestCase(pkgBuilder, config);
+                    moduleBuilder.addPackages(pkgBuilder);
+                }
+            }
+        }
+    }
+
+    private Entry getFileEntry(String name) {
+        Entry fEntry = null;
+        for (Entry et : getFileEntriesList(mRelContent)) {
+            if (name.equals(et.getName())) {
+                fEntry = et;
+                break;
+            }
+        }
+        return fEntry;
+    }
+    // Parses test case list from an APK
+    private void parseApkTestCase(
+            TestSuite.Module.Package.Builder pkgBuilder, TestModuleConfig config) {
+        DexFile dexFile = null;
+        String apkPath = Paths.get(mFolderPath, pkgBuilder.getPackageFile()).toString();
+        String moduleName = config.getModuleName();
+
+        // Loads a Dex file
+        try {
+            dexFile = DexFileFactory.loadDexFile(apkPath, Opcodes.forApi(mApiLevel));
+
+            // Iterates through all clesses in the Dex file
+            for (ClassDef classDef : dexFile.getClasses()) {
+                // adjust the format Lclass/y;
+                String className = classDef.getType().replace('/', '.');
+                // remove L...;
+                if (className.length() > 2) {
+                    className = className.substring(1, className.length() - 1);
+                }
+
+                // Parses test classes
+                TestClassType cType = chkTestClassType(classDef);
+                TestSuite.Module.Package.Class.Builder tClassBuilder =
+                        TestSuite.Module.Package.Class.newBuilder();
+                switch (cType) {
+                    case JUNIT3:
+                        tClassBuilder.setTestClassType(cType);
+                        tClassBuilder.setName(className);
+                        // Checks all test method
+                        for (Method method : classDef.getMethods()) {
+                            // Only care about Public
+                            if ((method.getAccessFlags() & AccessFlags.PUBLIC.getValue()) != 0) {
+                                String mName = method.getName();
+                                // Warn current test result accounting does not work well with Supress
+                                if (hasAnnotationSuffix(
+                                        method.getAnnotations(), SUPPRESS_ANNOTATION_TAG)) {
+                                    System.err.printf("%s#%s with Suppress:\n", className, mName);
+                                } else if (mName.startsWith(TEST_PREFIX_TAG)) {
+                                    // Junit3 style test case name starts with test
+                                    tClassBuilder.addMethods(
+                                            newTestBuilder(
+                                                    moduleName, className, method.getName()));
+                                } else if (hasAnnotationSuffix(
+                                        method.getAnnotations(), TEST_ANNOTATION_TAG)) {
+                                    tClassBuilder.addMethods(
+                                            newTestBuilder(
+                                                    moduleName, className, method.getName()));
+                                    System.err.printf(
+                                            "%s#%s JUNIT3 mixes with %s annotation:\n",
+                                            className, mName, TEST_ANNOTATION_TAG);
+                                }
+                            }
+                        }
+                        pkgBuilder.addClasses(tClassBuilder);
+                        break;
+                    case PARAMETERIZED:
+                        // ToDo need to find a way to count Parameterized tests
+                        System.err.printf("To count Parameterized tests: %s\n", className);
+                        tClassBuilder.setTestClassType(cType);
+                        tClassBuilder.setName(className);
+                        for (Method method : classDef.getMethods()) {
+                            // Junit4 style test case annotated with @Test
+                            if (hasAnnotation(method.getAnnotations(), TEST_ANNOTATION_TAG)) {
+                                tClassBuilder.addMethods(
+                                        newTestBuilder(moduleName, className, method.getName()));
+                            }
+                        }
+                        pkgBuilder.addClasses(tClassBuilder);
+                        break;
+                    case JUNIT4:
+                        tClassBuilder.setTestClassType(cType);
+                        tClassBuilder.setName(className);
+                        for (Method method : classDef.getMethods()) {
+                            // Junit4 style test case annotated with @Test
+                            if (hasAnnotation(method.getAnnotations(), TEST_ANNOTATION_TAG)) {
+                                tClassBuilder.addMethods(
+                                        newTestBuilder(moduleName, className, method.getName()));
+                            }
+                        }
+                        pkgBuilder.addClasses(tClassBuilder);
+                        break;
+                    default:
+                        // Not a known test class
+                }
+            }
+        } catch (IOException | DexFileFactory.DexFileNotFoundException ex) {
+            System.err.println("Unable to load dex file: " + apkPath);
+            // ex.printStackTrace();
+        }
+    }
+
+    private TestSuite.Module.Package.Class.Method.Builder newTestBuilder(
+            String moduleName, String className, String testName) {
+        TestSuite.Module.Package.Class.Method.Builder testBuilder =
+                TestSuite.Module.Package.Class.Method.newBuilder();
+        testBuilder.setName(testName);
+        // Check if it's an known failure
+        String nfFilter = getKnownFailureFilter(moduleName, className, testName);
+        if (null != nfFilter) {
+            testBuilder.setKnownFailureFilter(nfFilter);
+        }
+        return testBuilder;
+    }
+
+    private void parseJavaHostTest(
+            TestSuite.Module.Builder moduleBuilder, TestModuleConfig config, String tClass) {
+        TestSuite.Module.Package.Builder pkgBuilder = TestSuite.Module.Package.newBuilder();
+        //Assuming there is only one test Jar
+        String testFileName = config.getTestJars(0);
+        Entry tEntry = getFileEntry(testFileName);
+        String jarPath = tEntry.getRelativePath();
+
+        pkgBuilder.setName(testFileName);
+        pkgBuilder.setPackageFile(jarPath);
+        pkgBuilder.setContentId(tEntry.getContentId());
+        Collection<Class<?>> classes =
+                getJarTestClasses(
+                        Paths.get(mFolderPath, jarPath).toFile(),
+                        // Includes [x]-tradefed.jar for classes such as CompatibilityHostTestBase
+                        Paths.get(mFolderPath, mRelContent.getTestSuiteTradefed()).toFile());
+
+        for (Class<?> c : classes) {
+            TestSuite.Module.Package.Class.Builder tClassBuilder =
+                    TestSuite.Module.Package.Class.newBuilder();
+            tClassBuilder.setTestClassType(TestClassType.JAVAHOST);
+            tClassBuilder.setName(c.getName());
+
+            for (java.lang.reflect.Method m : c.getMethods()) {
+                int mdf = m.getModifiers();
+                if (Modifier.isPublic(mdf) || Modifier.isProtected(mdf)) {
+                    if (m.getName().startsWith(TEST_PREFIX_TAG)) {
+                        TestSuite.Module.Package.Class.Method.Builder methodBuilder =
+                                TestSuite.Module.Package.Class.Method.newBuilder();
+                        methodBuilder.setName(m.getName());
+                        // Check if it's an known failure
+                        String nfFilter =
+                                getKnownFailureFilter(
+                                        config.getModuleName(), c.getName(), m.getName());
+                        if (null != nfFilter) {
+                            methodBuilder.setKnownFailureFilter(nfFilter);
+                        }
+                        tClassBuilder.addMethods(methodBuilder);
+                    }
+                }
+            }
+            pkgBuilder.addClasses(tClassBuilder);
+        }
+        moduleBuilder.addPackages(pkgBuilder);
+    }
+
+    private static boolean hasAnnotation(Set<? extends Annotation> annotations, String tag) {
+        for (Annotation annotation : annotations) {
+            if (annotation.getType().equals(tag)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean hasAnnotationSuffix(Set<? extends Annotation> annotations, String tag) {
+        for (Annotation annotation : annotations) {
+            if (annotation.getType().endsWith(tag)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static TestClassType chkTestClassType(ClassDef classDef) {
+        // Only care about Public Class
+        if ((classDef.getAccessFlags() & AccessFlags.PUBLIC.getValue()) == 0) {
+            return TestClassType.UNKNOWN;
+        }
+
+        for (Annotation annotation : classDef.getAnnotations()) {
+            if (annotation.getType().equals(DEPRECATED_ANNOTATION_TAG)) {
+                return TestClassType.UNKNOWN;
+            }
+            if (annotation.getType().equals(RUN_WITH_ANNOTATION_TAG)) {
+                for (AnnotationElement annotationEle : annotation.getElements()) {
+                    if ("value".equals(annotationEle.getName())) {
+                        String aValue = ((TypeEncodedValue) annotationEle.getValue()).getValue();
+                        if (ANDROID_JUNIT4_TEST_TAG.equals(aValue)) {
+                            return TestClassType.JUNIT4;
+                        } else if (PARAMETERIZED_TEST_TAG.equals(aValue)) {
+                            return TestClassType.PARAMETERIZED;
+                        }
+                    }
+                }
+                System.err.printf("Unknown test class type: %s\n", classDef.getType());
+                return TestClassType.JUNIT4;
+            }
+        }
+
+        if (classDef.getType().endsWith(TEST_TAG) || classDef.getType().endsWith(TESTS_TAG)) {
+            return TestClassType.JUNIT3;
+        } else {
+            return TestClassType.UNKNOWN;
+        }
+    }
+
+    private static boolean isTargetClass(List<String> pkgList, String className) {
+        boolean found = false;
+        for (String pkg : pkgList) {
+            if (className.startsWith(pkg)) {
+                found = true;
+                break;
+            }
+        }
+        return found;
+    }
+
+    private static Collection<Class<?>> getJarTestClasses(File jarTestFile, File tfFile)
+            throws IllegalArgumentException {
+        List<Class<?>> classes = new ArrayList<>();
+
+        try (JarFile jarFile = new JarFile(jarTestFile)) {
+            Enumeration<JarEntry> e = jarFile.entries();
+
+            URL[] urls = {
+                new URL(String.format("jar:file:%s!/", jarTestFile.getAbsolutePath())),
+                new URL(String.format("jar:file:%s!/", tfFile.getAbsolutePath()))
+            };
+            URLClassLoader cl =
+                    URLClassLoader.newInstance(urls, JarTestFinder.class.getClassLoader());
+
+            while (e.hasMoreElements()) {
+                JarEntry je = e.nextElement();
+                if (je.isDirectory()
+                        || !je.getName().endsWith(".class")
+                        || je.getName().contains("$")
+                        || je.getName().contains("junit/")) {
+                    continue;
+                }
+                String className = getClassName(je.getName());
+
+                /*if (!className.endsWith("Test")) {
+                    continue;
+                }*/
+                try {
+                    Class<?> cls = cl.loadClass(className);
+
+                    if (IRemoteTest.class.isAssignableFrom(cls)
+                            || Test.class.isAssignableFrom(cls)) {
+                        classes.add(cls);
+                    } else if (!Modifier.isAbstract(cls.getModifiers())
+                            && hasJUnit4Annotation(cls)) {
+                        classes.add(cls);
+                    }
+                } catch (ClassNotFoundException | Error x) {
+                    System.err.println(
+                            String.format(
+                                    "Cannot find test class %s from %s",
+                                    className, jarTestFile.getName()));
+                    x.printStackTrace();
+                }
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return classes;
+    }
+
+    /** Helper to determine if we are dealing with a Test class with Junit4 annotations. */
+    protected static boolean hasJUnit4Annotation(Class<?> classObj) {
+        if (classObj.isAnnotationPresent(SuiteClasses.class)) {
+            return true;
+        }
+        if (classObj.isAnnotationPresent(RunWith.class)) {
+            return true;
+        }
+        /*for (Method m : classObj.getMethods()) {
+            if (m.isAnnotationPresent(org.junit.Test.class)) {
+                return true;
+            }
+        }*/
+        return false;
+    }
+
+    private static String getClassName(String name) {
+        // -6 because of .class
+        return name.substring(0, name.length() - 6).replace('/', '.');
+    }
+
+    private String getKnownFailureFilter(String tModule, String tClass, String tMethod) {
+        List<String> knownFailures = mRelContent.getKnownFailuresList();
+        String tsName = String.format(TESTCASE_NAME_FORMAT, tModule, tClass, tMethod);
+        for (String kf : knownFailures) {
+            if (tsName.startsWith(kf)) {
+                return kf;
+            }
+        }
+        return null;
+    }
+
+    // Iterates though all test suite content and prints them.
+    public void writeCsvFile(String relNameVer, String csvFile) {
+        TestSuite ts = getTestSuite();
+        try {
+            FileWriter fWriter = new FileWriter(csvFile);
+            PrintWriter pWriter = new PrintWriter(fWriter);
+            //Header
+            pWriter.println(
+                    "release,module,test_class,test,test_package,test_type,known_failure_filter,package_content_id");
+            for (TestSuite.Module module : ts.getModulesList()) {
+                for (TestSuite.Module.Package pkg : module.getPackagesList()) {
+                    for (TestSuite.Module.Package.Class cls : pkg.getClassesList()) {
+                        for (TestSuite.Module.Package.Class.Method mtd : cls.getMethodsList()) {
+                            pWriter.printf(
+                                    "%s,%s,%s,%s,%s,%s,%s,%s\n",
+                                    relNameVer,
+                                    module.getName(),
+                                    cls.getName(),
+                                    mtd.getName(),
+                                    pkg.getPackageFile(),
+                                    cls.getTestClassType(),
+                                    mtd.getKnownFailureFilter(),
+                                    getTestTargetContentId(pkg.getPackageFile()));
+                        }
+                    }
+                }
+            }
+            pWriter.flush();
+            pWriter.close();
+        } catch (IOException e) {
+            System.err.println("IOException:" + e.getMessage());
+        }
+    }
+
+    // Iterates though all test module and prints them.
+    public void writeModuleCsvFile(String relNameVer, String csvFile) {
+        TestSuite ts = getTestSuite();
+        try {
+            FileWriter fWriter = new FileWriter(csvFile);
+            PrintWriter pWriter = new PrintWriter(fWriter);
+
+            //Header
+            pWriter.print(
+                    "release,module,test_no,known_failure_no,test_type,test_class,component,description,test_config_file,test_file_names,test_jars,module_content_id\n");
+
+            for (TestSuite.Module module : ts.getModulesList()) {
+                int classCnt = 0;
+                int methodCnt = 0;
+                int kfCnt = 0;
+                for (TestSuite.Module.Package pkg : module.getPackagesList()) {
+                    for (TestSuite.Module.Package.Class cls : pkg.getClassesList()) {
+                        for (TestSuite.Module.Package.Class.Method mtd : cls.getMethodsList()) {
+                            // Filter out known failures
+                            if (mtd.getKnownFailureFilter().isEmpty()) {
+                                methodCnt++;
+                            } else {
+                                kfCnt++;
+                            }
+                        }
+                        classCnt++;
+                    }
+                }
+                String config = module.getConfigFile();
+                Entry entry = mRelContent.getEntries().get(config);
+                TestModuleConfig tmConfig = entry.getTestModuleConfig();
+                pWriter.printf(
+                        "%s,%s,%d,%d,%s,%s,%s,%s,%s,%s,%s,%s\n",
+                        relNameVer,
+                        module.getName(),
+                        methodCnt,
+                        kfCnt,
+                        module.getTestType(),
+                        module.getTestClass(),
+                        tmConfig.getComponent(),
+                        tmConfig.getDescription(),
+                        config,
+                        String.join(" ", tmConfig.getTestFileNamesList()),
+                        String.join(" ", tmConfig.getTestJarsList()),
+                        getTestModuleContentId(
+                                entry,
+                                tmConfig.getTestFileNamesList(),
+                                tmConfig.getTestJarsList()));
+            }
+            pWriter.flush();
+            pWriter.close();
+        } catch (IOException e) {
+            System.err.println("IOException:" + e.getMessage());
+        }
+    }
+
+    public static Collection<Entry> getFileEntriesList(ReleaseContent relContent) {
+        return relContent.getEntries().values();
+    }
+
+    // get Test Module Content Id = config cid + apk cids + jar cids
+    private String getTestModuleContentId(Entry config, List<String> apks, List<String> jars) {
+        String id = null;
+        //Starts with config file content_id
+        String idStr = config.getContentId();
+        try {
+            MessageDigest md = MessageDigest.getInstance("SHA-256");
+            //Add all apk content_id
+            for (String apk : apks) {
+                idStr += getTestTargetContentId(String.format(TESTCASES_FOLDER_FORMAT, apk));
+            }
+            //Add all jar content_id
+            for (String jar : jars) {
+                idStr += getTestTargetContentId(String.format(TESTCASES_FOLDER_FORMAT, jar));
+            }
+            md.update(idStr.getBytes(StandardCharsets.UTF_8));
+            // Converts to Base64 String
+            id = Base64.getEncoder().encodeToString(md.digest());
+            // System.out.println("getTestModuleContentId: " + idStr);
+        } catch (NoSuchAlgorithmException e) {
+            System.err.println("NoSuchAlgorithmException:" + e.getMessage());
+        }
+        return id;
+    }
+
+    private String getTestTargetContentId(String targetFile) {
+        Entry entry = mRelContent.getEntries().get(targetFile);
+        if (entry != null) {
+            return entry.getContentId();
+        } else {
+            System.err.println("No getTestTargetContentId: " + targetFile);
+            return "";
+        }
+    }
+
+}
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/TestSuiteTradefedParser.java b/tools/release-parser/src/com/android/cts/releaseparser/TestSuiteTradefedParser.java
new file mode 100644
index 0000000..fb04748
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/TestSuiteTradefedParser.java
@@ -0,0 +1,195 @@
+/*
+ * 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.releaseparser;
+
+import com.android.cts.releaseparser.ReleaseProto.*;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+public class TestSuiteTradefedParser extends JarParser {
+    private static final String TEST_SUITE_INFO_PROPERTIES_FILE = "test-suite-info.properties";
+    private static final String KNOWN_FAILURES_XML_FILE = "-known-failures.xml";
+    private static final String EXCLUDE_FILTER_TAG = "compatibility:exclude-filter";
+    private static final String NAME_TAG = "name";
+    private static final String VALUE_TAG = "value";
+
+    private Entry.EntryType mType;
+    private List<String> mKnownFailureList;
+    private String mName;
+    private String mFullname;
+    private String mBuildNumber;
+    private String mTargetArch;
+    private String mVersion;
+
+    public TestSuiteTradefedParser(File file) {
+        super(file);
+    }
+
+    @Override
+    public Entry.EntryType getType() {
+        if (mType == null) {
+            parseFile();
+        }
+        return mType;
+    }
+
+    public String getName() {
+        if (mType == null) {
+            parseFile();
+        }
+        return mName;
+    }
+
+    public String getFullName() {
+        if (mType == null) {
+            parseFile();
+        }
+        return mFullname;
+    }
+
+    public String getBuildNumber() {
+        if (mType == null) {
+            parseFile();
+        }
+        return mBuildNumber;
+    }
+
+    public String getTargetArch() {
+        if (mType == null) {
+            parseFile();
+        }
+        return mTargetArch;
+    }
+
+    public String getVersion() {
+        if (mType == null) {
+            parseFile();
+        }
+        return mVersion;
+    }
+
+    public List<String> getKnownFailureList() {
+        if (mKnownFailureList == null) {
+            mKnownFailureList = new ArrayList<String>();
+            praseKnownFailure();
+        }
+        return mKnownFailureList;
+    }
+
+    private void praseKnownFailure() {
+        try {
+            ZipFile zip = new ZipFile(getFile());
+            try {
+                Enumeration<? extends ZipEntry> entries = zip.entries();
+                while (entries.hasMoreElements()) {
+                    ZipEntry entry = entries.nextElement();
+
+                    if (entry.getName().endsWith(KNOWN_FAILURES_XML_FILE)) {
+                        SAXParserFactory spf = SAXParserFactory.newInstance();
+                        spf.setNamespaceAware(false);
+                        SAXParser saxParser = spf.newSAXParser();
+                        InputStream xmlStream = zip.getInputStream(entry);
+                        KnownFailuresXmlHandler kfXmlHandler =
+                                new KnownFailuresXmlHandler();
+                        saxParser.parse(xmlStream, kfXmlHandler);
+                        xmlStream.close();
+                    }
+                }
+            } finally {
+                zip.close();
+            }
+        } catch (Exception e) {
+            System.err.println(String.format("Cannot praseKnownFailure %s", e.getMessage()));
+        }
+    }
+
+    private class KnownFailuresXmlHandler extends DefaultHandler {
+        @Override
+        public void startElement(String uri, String localName, String name, Attributes attributes)
+                throws SAXException {
+            super.startElement(uri, localName, name, attributes);
+            if (EXCLUDE_FILTER_TAG.equals(attributes.getValue(NAME_TAG))) {
+                String kfFilter = attributes.getValue(VALUE_TAG).replace(' ', '.');
+                mKnownFailureList.add(kfFilter);
+            }
+        }
+    }
+
+    private void parseFile() {
+        try {
+            ZipFile zip = new ZipFile(getFile());
+            try {
+                Enumeration<? extends ZipEntry> entries = zip.entries();
+                while (entries.hasMoreElements()) {
+                    ZipEntry entry = entries.nextElement();
+
+                    if (entry.getName().equals(TEST_SUITE_INFO_PROPERTIES_FILE)) {
+                        InputStream inStream = zip.getInputStream(entry);
+                        InputStreamReader isReader = new InputStreamReader(inStream, "UTF-8");
+                        BufferedReader bfReader = new BufferedReader(isReader);
+                        String ln;
+                        while((ln = bfReader.readLine()) != null) {
+                            String[] tokens = ln.split(" = ");
+                            switch (tokens[0]) {
+                                case "build_number":
+                                    mBuildNumber = tokens[1];
+                                    break;
+                                case "target_arch":
+                                    mTargetArch = tokens[1];
+                                    break;
+                                case "name":
+                                    mName = tokens[1];
+                                    break;
+                                case "fullname":
+                                    mFullname = tokens[1];
+                                    break;
+                                case "version":
+                                    mVersion = tokens[1];
+                                    break;
+                            }
+                        }
+                        inStream.close();
+                        isReader.close();
+                        bfReader.close();
+                    }
+                }
+                mType = Entry.EntryType.TEST_SUITE_TRADEFED;
+            } finally {
+                zip.close();
+            }
+        } catch (Exception e) {
+            System.err.println(
+                    String.format("Cannot %s %s", TEST_SUITE_INFO_PROPERTIES_FILE, e.getMessage()));
+            mType = super.getType();
+        }
+    }
+}
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/VdexParser.java b/tools/release-parser/src/com/android/cts/releaseparser/VdexParser.java
new file mode 100644
index 0000000..1fadfa3
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/VdexParser.java
@@ -0,0 +1,37 @@
+/*
+ * 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.releaseparser;
+
+import com.android.cts.releaseparser.ReleaseProto.*;
+
+import java.io.File;
+
+public class VdexParser extends FileParser {
+    public VdexParser(File file) {
+        super(file);
+    }
+
+    @Override
+    public Entry.EntryType getType() {
+        return Entry.EntryType.VDEX;
+    }
+
+    @Override
+    public String getCodeId() {
+        return getFileContentId();
+    }
+}